Skip to content
This repository was archived by the owner on Dec 1, 2024. It is now read-only.

Commit 50dc50b

Browse files
authoredSep 28, 2021
Add db.getMany(keys) (#787)
Ref Level/community#101
1 parent 8e72ac5 commit 50dc50b

File tree

5 files changed

+140
-6
lines changed

5 files changed

+140
-6
lines changed
 

‎README.md

+13-3
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ If you are working on `leveldown` itself and want to re-compile the C++ code, ru
106106
- <a href="#leveldown_close"><code>db.<b>close()</b></code></a>
107107
- <a href="#leveldown_put"><code>db.<b>put()</b></code></a>
108108
- <a href="#leveldown_get"><code>db.<b>get()</b></code></a>
109+
- <a href="#leveldown_get_many"><code>db.<b>getMany()</b></code></a>
109110
- <a href="#leveldown_del"><code>db.<b>del()</b></code></a>
110111
- <a href="#leveldown_batch"><code>db.<b>batch()</b></code></a> _(array form)_
111112
- <a href="#leveldown_chainedbatch"><code>db.<b>batch()</b></code></a> _(chained form)_
@@ -210,7 +211,7 @@ The `callback` function will be called with no arguments if the operation is suc
210211

211212
### `db.get(key[, options], callback)`
212213

213-
<code>get()</code> is an instance method on an existing database object, used to fetch individual entries from the LevelDB store.
214+
Get a value from the LevelDB store by `key`.
214215

215216
The `key` object may either be a string or a Buffer and cannot be `undefined` or `null`. Other object types are converted to strings with the `toString()` method and the resulting string _may not_ be a zero-length. A richer set of data-types is catered for in `levelup`.
216217

@@ -220,11 +221,20 @@ Values fetched via `get()` that are stored as zero-length character arrays (`nul
220221

221222
The optional `options` object may contain:
222223

224+
- `asBuffer` (boolean, default: `true`): Used to determine whether to return the `value` of the entry as a string or a Buffer. Note that converting from a Buffer to a string incurs a cost so if you need a string (and the `value` can legitimately become a UTF8 string) then you should fetch it as one with `{ asBuffer: false }` and you'll avoid this conversion cost.
223225
- `fillCache` (boolean, default: `true`): LevelDB will by default fill the in-memory LRU Cache with data from a call to get. Disabling this is done by setting `fillCache` to `false`.
224226

225-
- `asBuffer` (boolean, default: `true`): Used to determine whether to return the `value` of the entry as a string or a Buffer. Note that converting from a Buffer to a string incurs a cost so if you need a string (and the `value` can legitimately become a UTF8 string) then you should fetch it as one with `{ asBuffer: false }` and you'll avoid this conversion cost.
227+
The `callback` function will be called with a single `error` if the operation failed for any reason, including if the key was not found. If successful the first argument will be `null` and the second argument will be the `value` as a string or Buffer depending on the `asBuffer` option.
228+
229+
<a name="leveldown_get_many"></a>
230+
231+
### `db.getMany(keys[, options][, callback])`
232+
233+
Get multiple values from the store by an array of `keys`. The optional `options` object may contain `asBuffer` and `fillCache`, as described in [`get()`](#leveldown_get).
234+
235+
The `callback` function will be called with an `Error` if the operation failed for any reason. If successful the first argument will be `null` and the second argument will be an array of values with the same order as `keys`. If a key was not found, the relevant value will be `undefined`.
226236

227-
The `callback` function will be called with a single `error` if the operation failed for any reason. If successful the first argument will be `null` and the second argument will be the `value` as a string or Buffer depending on the `asBuffer` option.
237+
If no callback is provided, a promise is returned.
228238

229239
<a name="leveldown_del"></a>
230240

‎binding.cc

+118
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,31 @@ static std::string* RangeOption (napi_env env, napi_value opts, const char* name
242242
return NULL;
243243
}
244244

245+
/**
246+
* Converts an array containing Buffer or string keys to a vector.
247+
* Empty elements are skipped.
248+
*/
249+
static std::vector<std::string>* KeyArray (napi_env env, napi_value arr) {
250+
uint32_t length;
251+
std::vector<std::string>* result = new std::vector<std::string>();
252+
253+
if (napi_get_array_length(env, arr, &length) == napi_ok) {
254+
result->reserve(length);
255+
256+
for (uint32_t i = 0; i < length; i++) {
257+
napi_value element;
258+
259+
if (napi_get_element(env, arr, i, &element) == napi_ok &&
260+
StringOrBufferLength(env, element) > 0) {
261+
LD_STRING_OR_BUFFER_TO_COPY(env, element, to);
262+
result->emplace_back(toCh_, toSz_);
263+
}
264+
}
265+
}
266+
267+
return result;
268+
}
269+
245270
/**
246271
* Calls a function.
247272
*/
@@ -1132,6 +1157,98 @@ NAPI_METHOD(db_get) {
11321157
NAPI_RETURN_UNDEFINED();
11331158
}
11341159

1160+
/**
1161+
* Worker class for getting many values.
1162+
*/
1163+
struct GetManyWorker final : public PriorityWorker {
1164+
GetManyWorker (napi_env env,
1165+
Database* database,
1166+
const std::vector<std::string>* keys,
1167+
napi_value callback,
1168+
const bool valueAsBuffer,
1169+
const bool fillCache)
1170+
: PriorityWorker(env, database, callback, "leveldown.get.many"),
1171+
keys_(keys), valueAsBuffer_(valueAsBuffer) {
1172+
options_.fill_cache = fillCache;
1173+
options_.snapshot = database->NewSnapshot();
1174+
}
1175+
1176+
~GetManyWorker() {
1177+
delete keys_;
1178+
}
1179+
1180+
void DoExecute () override {
1181+
cache_.reserve(keys_->size());
1182+
1183+
for (const std::string& key: *keys_) {
1184+
std::string* value = new std::string();
1185+
leveldb::Status status = database_->Get(options_, key, *value);
1186+
1187+
if (status.ok()) {
1188+
cache_.push_back(value);
1189+
} else if (status.IsNotFound()) {
1190+
delete value;
1191+
cache_.push_back(NULL);
1192+
} else {
1193+
delete value;
1194+
for (const std::string* value: cache_) {
1195+
if (value != NULL) delete value;
1196+
}
1197+
SetStatus(status);
1198+
break;
1199+
}
1200+
}
1201+
1202+
database_->ReleaseSnapshot(options_.snapshot);
1203+
}
1204+
1205+
void HandleOKCallback (napi_env env, napi_value callback) override {
1206+
size_t size = cache_.size();
1207+
napi_value array;
1208+
napi_create_array_with_length(env, size, &array);
1209+
1210+
for (size_t idx = 0; idx < size; idx++) {
1211+
std::string* value = cache_[idx];
1212+
napi_value element;
1213+
Entry::Convert(env, value, valueAsBuffer_, &element);
1214+
napi_set_element(env, array, static_cast<uint32_t>(idx), element);
1215+
if (value != NULL) delete value;
1216+
}
1217+
1218+
napi_value argv[2];
1219+
napi_get_null(env, &argv[0]);
1220+
argv[1] = array;
1221+
CallFunction(env, callback, 2, argv);
1222+
}
1223+
1224+
private:
1225+
leveldb::ReadOptions options_;
1226+
const std::vector<std::string>* keys_;
1227+
const bool valueAsBuffer_;
1228+
std::vector<std::string*> cache_;
1229+
};
1230+
1231+
/**
1232+
* Gets many values from a database.
1233+
*/
1234+
NAPI_METHOD(db_get_many) {
1235+
NAPI_ARGV(4);
1236+
NAPI_DB_CONTEXT();
1237+
1238+
const std::vector<std::string>* keys = KeyArray(env, argv[1]);
1239+
napi_value options = argv[2];
1240+
const bool asBuffer = BooleanProperty(env, options, "asBuffer", true);
1241+
const bool fillCache = BooleanProperty(env, options, "fillCache", true);
1242+
napi_value callback = argv[3];
1243+
1244+
GetManyWorker* worker = new GetManyWorker(
1245+
env, database, keys, callback, asBuffer, fillCache
1246+
);
1247+
1248+
worker->Queue(env);
1249+
NAPI_RETURN_UNDEFINED();
1250+
}
1251+
11351252
/**
11361253
* Worker class for deleting a value from a database.
11371254
*/
@@ -1916,6 +2033,7 @@ NAPI_INIT() {
19162033
NAPI_EXPORT_FUNCTION(db_close);
19172034
NAPI_EXPORT_FUNCTION(db_put);
19182035
NAPI_EXPORT_FUNCTION(db_get);
2036+
NAPI_EXPORT_FUNCTION(db_get_many);
19192037
NAPI_EXPORT_FUNCTION(db_del);
19202038
NAPI_EXPORT_FUNCTION(db_clear);
19212039
NAPI_EXPORT_FUNCTION(db_approximate_size);

‎leveldown.js

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function LevelDOWN (location) {
2121
permanence: true,
2222
seek: true,
2323
clear: true,
24+
getMany: true,
2425
createIfMissing: true,
2526
errorIfExists: true,
2627
additionalMethods: {
@@ -59,6 +60,10 @@ LevelDOWN.prototype._get = function (key, options, callback) {
5960
binding.db_get(this.context, key, options, callback)
6061
}
6162

63+
LevelDOWN.prototype._getMany = function (keys, options, callback) {
64+
binding.db_get_many(this.context, keys, options, callback)
65+
}
66+
6267
LevelDOWN.prototype._del = function (key, options, callback) {
6368
binding.db_del(this.context, key, options, callback)
6469
}

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"prebuild-win32-x64": "prebuildify -t 8.14.0 --napi --strip"
2626
},
2727
"dependencies": {
28-
"abstract-leveldown": "^7.0.0",
28+
"abstract-leveldown": "^7.2.0",
2929
"napi-macros": "~2.0.0",
3030
"node-gyp-build": "^4.3.0"
3131
},

‎test/common.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = suite.common({
99
return leveldown(tempy.directory())
1010
},
1111

12-
// Opt-in to new clear() tests
13-
clear: true
12+
// Opt-in to new tests
13+
clear: true,
14+
getMany: true
1415
})

0 commit comments

Comments
 (0)
This repository has been archived.