Skip to content

Commit e3a328c

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] ``` Ref: Level/community#142 Category: addition
1 parent 15eb289 commit e3a328c

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

binding.cc

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,89 @@ NAPI_METHOD(db_get) {
13761376
return promise;
13771377
}
13781378

1379+
/**
1380+
* Worker class for db.has().
1381+
*/
1382+
struct HasWorker final : public PriorityWorker {
1383+
HasWorker(
1384+
napi_env env,
1385+
Database* database,
1386+
napi_deferred deferred,
1387+
leveldb::Slice key,
1388+
const bool fillCache,
1389+
ExplicitSnapshot* snapshot
1390+
) : PriorityWorker(env, database, deferred, "classic_level.db.has"),
1391+
key_(key) {
1392+
options_.fill_cache = fillCache;
1393+
1394+
if (snapshot == NULL) {
1395+
implicitSnapshot_ = database->NewSnapshot();
1396+
options_.snapshot = implicitSnapshot_;
1397+
} else {
1398+
implicitSnapshot_ = NULL;
1399+
options_.snapshot = snapshot->nut;
1400+
}
1401+
}
1402+
1403+
~HasWorker () {
1404+
DisposeSliceBuffer(key_);
1405+
}
1406+
1407+
void DoExecute () override {
1408+
// LevelDB (and our wrapper) has no Has() method
1409+
std::string value;
1410+
leveldb::Status status = database_->Get(options_, key_, value);
1411+
1412+
if (status.ok()) {
1413+
result_ = true;
1414+
SetStatus(status);
1415+
} else if (status.IsNotFound()) {
1416+
result_ = false;
1417+
SetStatus(leveldb::Status::OK());
1418+
} else {
1419+
SetStatus(status);
1420+
}
1421+
1422+
if (implicitSnapshot_) {
1423+
database_->ReleaseSnapshot(implicitSnapshot_);
1424+
}
1425+
}
1426+
1427+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
1428+
napi_value resultBoolean;
1429+
napi_get_boolean(env, result_, &resultBoolean);
1430+
napi_resolve_deferred(env, deferred, resultBoolean);
1431+
}
1432+
1433+
private:
1434+
leveldb::ReadOptions options_;
1435+
leveldb::Slice key_;
1436+
bool result_;
1437+
const leveldb::Snapshot* implicitSnapshot_;
1438+
};
1439+
1440+
/**
1441+
* Check if the database has an entry with the given key.
1442+
*/
1443+
NAPI_METHOD(db_has) {
1444+
NAPI_ARGV(4);
1445+
NAPI_DB_CONTEXT();
1446+
NAPI_PROMISE();
1447+
1448+
leveldb::Slice key = ToSlice(env, argv[1]);
1449+
const bool fillCache = BooleanValue(env, argv[2], true);
1450+
1451+
ExplicitSnapshot* snapshot = NULL;
1452+
napi_get_value_external(env, argv[3], (void**)&snapshot);
1453+
1454+
HasWorker* worker = new HasWorker(
1455+
env, database, deferred, key, fillCache, snapshot
1456+
);
1457+
1458+
worker->Queue(env);
1459+
return promise;
1460+
}
1461+
13791462
/**
13801463
* Worker class for getting many values.
13811464
*/
@@ -1481,6 +1564,100 @@ NAPI_METHOD(db_get_many) {
14811564
return promise;
14821565
}
14831566

1567+
/**
1568+
* Worker class for db.hasMany().
1569+
*/
1570+
struct HasManyWorker final : public PriorityWorker {
1571+
HasManyWorker(
1572+
napi_env env,
1573+
Database* database,
1574+
std::vector<std::string> keys,
1575+
napi_deferred deferred,
1576+
const bool fillCache,
1577+
ExplicitSnapshot* snapshot
1578+
) : PriorityWorker(env, database, deferred, "classic_level.has.many"),
1579+
keys_(std::move(keys)) {
1580+
options_.fill_cache = fillCache;
1581+
1582+
if (snapshot == NULL) {
1583+
implicitSnapshot_ = database->NewSnapshot();
1584+
options_.snapshot = implicitSnapshot_;
1585+
} else {
1586+
implicitSnapshot_ = NULL;
1587+
options_.snapshot = snapshot->nut;
1588+
}
1589+
}
1590+
1591+
void DoExecute () override {
1592+
cache_.reserve(keys_.size());
1593+
1594+
for (const std::string& key: keys_) {
1595+
std::string value;
1596+
leveldb::Status status = database_->Get(options_, key, value);
1597+
1598+
if (status.ok()) {
1599+
cache_.push_back(true);
1600+
} else if (status.IsNotFound()) {
1601+
cache_.push_back(false);
1602+
} else {
1603+
SetStatus(status);
1604+
break;
1605+
}
1606+
}
1607+
1608+
if (implicitSnapshot_) {
1609+
database_->ReleaseSnapshot(implicitSnapshot_);
1610+
}
1611+
}
1612+
1613+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
1614+
size_t size = cache_.size();
1615+
1616+
napi_value array;
1617+
napi_value booleanTrue;
1618+
napi_value booleanFalse;
1619+
1620+
napi_create_array_with_length(env, size, &array);
1621+
napi_get_boolean(env, true, &booleanTrue);
1622+
napi_get_boolean(env, false, &booleanFalse);
1623+
1624+
for (size_t i = 0; i < size; i++) {
1625+
auto value = cache_[i] ? booleanTrue : booleanFalse;
1626+
napi_set_element(env, array, static_cast<uint32_t>(i), value);
1627+
}
1628+
1629+
napi_resolve_deferred(env, deferred, array);
1630+
}
1631+
1632+
private:
1633+
leveldb::ReadOptions options_;
1634+
const std::vector<std::string> keys_;
1635+
std::vector<bool> cache_;
1636+
const leveldb::Snapshot* implicitSnapshot_;
1637+
};
1638+
1639+
/**
1640+
* Check if the database has entries with the given keys.
1641+
*/
1642+
NAPI_METHOD(db_has_many) {
1643+
NAPI_ARGV(4);
1644+
NAPI_DB_CONTEXT();
1645+
NAPI_PROMISE();
1646+
1647+
const auto keys = KeyArray(env, argv[1]);
1648+
const bool fillCache = BooleanValue(env, argv[2], true);
1649+
1650+
ExplicitSnapshot* snapshot = NULL;
1651+
napi_get_value_external(env, argv[3], (void**)&snapshot);
1652+
1653+
HasManyWorker* worker = new HasManyWorker(
1654+
env, database, keys, deferred, fillCache, snapshot
1655+
);
1656+
1657+
worker->Queue(env);
1658+
return promise;
1659+
}
1660+
14841661
/**
14851662
* Worker class for deleting a value from a database.
14861663
*/
@@ -2280,6 +2457,8 @@ NAPI_INIT() {
22802457
NAPI_EXPORT_FUNCTION(db_put);
22812458
NAPI_EXPORT_FUNCTION(db_get);
22822459
NAPI_EXPORT_FUNCTION(db_get_many);
2460+
NAPI_EXPORT_FUNCTION(db_has);
2461+
NAPI_EXPORT_FUNCTION(db_has_many);
22832462
NAPI_EXPORT_FUNCTION(db_del);
22842463
NAPI_EXPORT_FUNCTION(db_clear);
22852464
NAPI_EXPORT_FUNCTION(db_approximate_size);

index.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class ClassicLevel extends AbstractLevel {
2222
utf8: true,
2323
view: true
2424
},
25+
has: true,
2526
createIfMissing: true,
2627
errorIfExists: true,
2728
explicitSnapshots: true,
@@ -77,6 +78,24 @@ class ClassicLevel extends AbstractLevel {
7778
)
7879
}
7980

81+
async _has (key, options) {
82+
return binding.db_has(
83+
this[kContext],
84+
key,
85+
options.fillCache,
86+
options.snapshot?.[kContext]
87+
)
88+
}
89+
90+
async _hasMany (keys, options) {
91+
return binding.db_has_many(
92+
this[kContext],
93+
keys,
94+
options.fillCache,
95+
options.snapshot?.[kContext]
96+
)
97+
}
98+
8099
async _del (key, options) {
81100
return binding.db_del(this[kContext], key, options)
82101
}

0 commit comments

Comments
 (0)