Skip to content

Commit a28189d

Browse files
committed
Implement has() and hasMany()
Adds support of two methods: ```js await db.put('love', 'u') await db.has('love') // true await db.hasMany(['love', 'hate']) // [true, false] ``` Depends on a pending `abstract-level` release, and lacks support of explicit snapshots which should be implemented after #110 lands. Ref: Level/community#142
1 parent d142686 commit a28189d

File tree

3 files changed

+167
-1
lines changed

3 files changed

+167
-1
lines changed

binding.cc

+151
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,75 @@ NAPI_METHOD(db_get) {
13061306
return promise;
13071307
}
13081308

1309+
/**
1310+
* Worker class for db.has().
1311+
*/
1312+
struct HasWorker final : public PriorityWorker {
1313+
HasWorker(
1314+
napi_env env,
1315+
Database* database,
1316+
napi_deferred deferred,
1317+
leveldb::Slice key,
1318+
const bool fillCache
1319+
) : PriorityWorker(env, database, deferred, "classic_level.db.has"),
1320+
key_(key) {
1321+
options_.fill_cache = fillCache;
1322+
options_.snapshot = database->NewSnapshot();
1323+
}
1324+
1325+
~HasWorker () {
1326+
DisposeSliceBuffer(key_);
1327+
}
1328+
1329+
void DoExecute () override {
1330+
// LevelDB (and our wrapper) has no Has() method
1331+
std::string value;
1332+
leveldb::Status status = database_->Get(options_, key_, value);
1333+
1334+
if (status.ok()) {
1335+
result_ = true;
1336+
SetStatus(status);
1337+
} else if (status.IsNotFound()) {
1338+
result_ = false;
1339+
SetStatus(leveldb::Status::OK());
1340+
} else {
1341+
SetStatus(status);
1342+
}
1343+
1344+
database_->ReleaseSnapshot(options_.snapshot);
1345+
}
1346+
1347+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
1348+
napi_value resultBoolean;
1349+
napi_get_boolean(env, result_, &resultBoolean);
1350+
napi_resolve_deferred(env, deferred, resultBoolean);
1351+
}
1352+
1353+
private:
1354+
leveldb::ReadOptions options_;
1355+
leveldb::Slice key_;
1356+
bool result_;
1357+
};
1358+
1359+
/**
1360+
* Check if the database has an entry with the given key.
1361+
*/
1362+
NAPI_METHOD(db_has) {
1363+
NAPI_ARGV(3);
1364+
NAPI_DB_CONTEXT();
1365+
NAPI_PROMISE();
1366+
1367+
leveldb::Slice key = ToSlice(env, argv[1]);
1368+
const bool fillCache = BooleanValue(env, argv[2], true);
1369+
1370+
HasWorker* worker = new HasWorker(
1371+
env, database, deferred, key, fillCache
1372+
);
1373+
1374+
worker->Queue(env);
1375+
return promise;
1376+
}
1377+
13091378
/**
13101379
* Worker class for getting many values.
13111380
*/
@@ -1391,6 +1460,86 @@ NAPI_METHOD(db_get_many) {
13911460
return promise;
13921461
}
13931462

1463+
/**
1464+
* Worker class for db.hasMany().
1465+
*/
1466+
struct HasManyWorker final : public PriorityWorker {
1467+
HasManyWorker(
1468+
napi_env env,
1469+
Database* database,
1470+
std::vector<std::string> keys,
1471+
napi_deferred deferred,
1472+
const bool fillCache
1473+
) : PriorityWorker(env, database, deferred, "classic_level.has.many"),
1474+
keys_(std::move(keys)) {
1475+
options_.fill_cache = fillCache;
1476+
options_.snapshot = database->NewSnapshot();
1477+
}
1478+
1479+
void DoExecute () override {
1480+
cache_.reserve(keys_.size());
1481+
1482+
for (const std::string& key: keys_) {
1483+
std::string value;
1484+
leveldb::Status status = database_->Get(options_, key, value);
1485+
1486+
if (status.ok()) {
1487+
cache_.push_back(true);
1488+
} else if (status.IsNotFound()) {
1489+
cache_.push_back(false);
1490+
} else {
1491+
SetStatus(status);
1492+
break;
1493+
}
1494+
}
1495+
1496+
database_->ReleaseSnapshot(options_.snapshot);
1497+
}
1498+
1499+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
1500+
size_t size = cache_.size();
1501+
1502+
napi_value array;
1503+
napi_value booleanTrue;
1504+
napi_value booleanFalse;
1505+
1506+
napi_create_array_with_length(env, size, &array);
1507+
napi_get_boolean(env, true, &booleanTrue);
1508+
napi_get_boolean(env, false, &booleanFalse);
1509+
1510+
for (size_t i = 0; i < size; i++) {
1511+
auto value = cache_[i] ? booleanTrue : booleanFalse;
1512+
napi_set_element(env, array, static_cast<uint32_t>(i), value);
1513+
}
1514+
1515+
napi_resolve_deferred(env, deferred, array);
1516+
}
1517+
1518+
private:
1519+
leveldb::ReadOptions options_;
1520+
const std::vector<std::string> keys_;
1521+
std::vector<bool> cache_;
1522+
};
1523+
1524+
/**
1525+
* Check if the database has entries with the given keys.
1526+
*/
1527+
NAPI_METHOD(db_has_many) {
1528+
NAPI_ARGV(3);
1529+
NAPI_DB_CONTEXT();
1530+
NAPI_PROMISE();
1531+
1532+
const auto keys = KeyArray(env, argv[1]);
1533+
const bool fillCache = BooleanValue(env, argv[2], true);
1534+
1535+
HasManyWorker* worker = new HasManyWorker(
1536+
env, database, keys, deferred, fillCache
1537+
);
1538+
1539+
worker->Queue(env);
1540+
return promise;
1541+
}
1542+
13941543
/**
13951544
* Worker class for deleting a value from a database.
13961545
*/
@@ -2182,6 +2331,8 @@ NAPI_INIT() {
21822331
NAPI_EXPORT_FUNCTION(db_put);
21832332
NAPI_EXPORT_FUNCTION(db_get);
21842333
NAPI_EXPORT_FUNCTION(db_get_many);
2334+
NAPI_EXPORT_FUNCTION(db_has);
2335+
NAPI_EXPORT_FUNCTION(db_has_many);
21852336
NAPI_EXPORT_FUNCTION(db_del);
21862337
NAPI_EXPORT_FUNCTION(db_clear);
21872338
NAPI_EXPORT_FUNCTION(db_approximate_size);

index.js

+15
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class ClassicLevel extends AbstractLevel {
2323
view: true
2424
},
2525
seek: true,
26+
has: true,
2627
createIfMissing: true,
2728
errorIfExists: true,
2829
additionalMethods: {
@@ -71,6 +72,20 @@ class ClassicLevel extends AbstractLevel {
7172
return binding.db_get_many(this[kContext], keys, options)
7273
}
7374

75+
// TODO: snapshots
76+
async _has (key, options) {
77+
return binding.db_has(
78+
this[kContext],
79+
key,
80+
options.fillCache
81+
)
82+
}
83+
84+
// TODO: snapshots
85+
async _hasMany (keys, options) {
86+
return binding.db_has_many(this[kContext], keys, options.fillCache)
87+
}
88+
7489
async _del (key, options) {
7590
return binding.db_del(this[kContext], key, options)
7691
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"prebuild-win32-x64": "prebuildify -t 18.20.4 --napi --strip"
2828
},
2929
"dependencies": {
30-
"abstract-level": "^2.0.0",
30+
"abstract-level": "github:Level/abstract-level",
3131
"module-error": "^1.0.1",
3232
"napi-macros": "^2.2.2",
3333
"node-gyp-build": "^4.3.0"

0 commit comments

Comments
 (0)