@@ -882,7 +882,7 @@ struct BaseIterator {
882
882
if (reverse_ ? cmp > 0 : cmp < 0 ) {
883
883
Next ();
884
884
}
885
- } else {
885
+ } else { // TODO: can we skip this code path if not in reverse?
886
886
SeekToFirst ();
887
887
if (dbIterator_->Valid ()) {
888
888
int cmp = dbIterator_->key ().compare (target);
@@ -893,6 +893,15 @@ struct BaseIterator {
893
893
}
894
894
}
895
895
896
+ /* *
897
+ * Seek to an exact key.
898
+ */
899
+ bool SeekExact (leveldb::Slice& target) {
900
+ didSeek_ = true ;
901
+ dbIterator_->Seek (target);
902
+ return dbIterator_->Valid () && dbIterator_->key () == target;
903
+ }
904
+
896
905
void CloseIterator () {
897
906
if (!hasClosed_) {
898
907
hasClosed_ = true ;
@@ -1376,6 +1385,74 @@ NAPI_METHOD(db_get) {
1376
1385
return promise;
1377
1386
}
1378
1387
1388
+ /* *
1389
+ * Worker class for db.has().
1390
+ */
1391
+ struct HasWorker final : public PriorityWorker {
1392
+ HasWorker (
1393
+ napi_env env,
1394
+ Database* database,
1395
+ napi_deferred deferred,
1396
+ leveldb::Slice key,
1397
+ const bool fillCache,
1398
+ ExplicitSnapshot* snapshot
1399
+ ) : PriorityWorker(env, database, deferred, " classic_level.db.has" ),
1400
+ key_ (key) {
1401
+ iterator_ = new BaseIterator (
1402
+ database,
1403
+ // Range options (not relevant)
1404
+ false , NULL , NULL , NULL , NULL , -1 ,
1405
+ fillCache,
1406
+ snapshot
1407
+ );
1408
+ }
1409
+
1410
+ ~HasWorker () {
1411
+ DisposeSliceBuffer (key_);
1412
+ delete iterator_;
1413
+ }
1414
+
1415
+ void DoExecute () override {
1416
+ // LevelDB has no Has() method so use an iterator
1417
+ result_ = iterator_->SeekExact (key_);
1418
+ SetStatus (iterator_->Status ());
1419
+ iterator_->CloseIterator ();
1420
+ }
1421
+
1422
+ void HandleOKCallback (napi_env env, napi_deferred deferred) override {
1423
+ napi_value resultBoolean;
1424
+ napi_get_boolean (env, result_, &resultBoolean);
1425
+ napi_resolve_deferred (env, deferred, resultBoolean);
1426
+ }
1427
+
1428
+ private:
1429
+ leveldb::Slice key_;
1430
+ BaseIterator* iterator_;
1431
+ bool result_;
1432
+ };
1433
+
1434
+ /* *
1435
+ * Check if the database has an entry with the given key.
1436
+ */
1437
+ NAPI_METHOD (db_has) {
1438
+ NAPI_ARGV (4 );
1439
+ NAPI_DB_CONTEXT ();
1440
+ NAPI_PROMISE ();
1441
+
1442
+ leveldb::Slice key = ToSlice (env, argv[1 ]);
1443
+ const bool fillCache = BooleanValue (env, argv[2 ], true );
1444
+
1445
+ ExplicitSnapshot* snapshot = NULL ;
1446
+ napi_get_value_external (env, argv[3 ], (void **)&snapshot);
1447
+
1448
+ HasWorker* worker = new HasWorker (
1449
+ env, database, deferred, key, fillCache, snapshot
1450
+ );
1451
+
1452
+ worker->Queue (env);
1453
+ return promise;
1454
+ }
1455
+
1379
1456
/* *
1380
1457
* Worker class for getting many values.
1381
1458
*/
@@ -1481,6 +1558,78 @@ NAPI_METHOD(db_get_many) {
1481
1558
return promise;
1482
1559
}
1483
1560
1561
+ /* *
1562
+ * Worker class for db.hasMany().
1563
+ */
1564
+ struct HasManyWorker final : public PriorityWorker {
1565
+ HasManyWorker (
1566
+ napi_env env,
1567
+ Database* database,
1568
+ std::vector<std::string> keys,
1569
+ napi_deferred deferred,
1570
+ uint32_t * bitset,
1571
+ const bool fillCache,
1572
+ ExplicitSnapshot* snapshot
1573
+ ) : PriorityWorker(env, database, deferred, " classic_level.has.many" ),
1574
+ keys_ (std::move(keys)),
1575
+ bitset_(bitset) {
1576
+ iterator_ = new BaseIterator (
1577
+ database,
1578
+ // Range options (not relevant)
1579
+ false , NULL , NULL , NULL , NULL , -1 ,
1580
+ fillCache,
1581
+ snapshot
1582
+ );
1583
+ }
1584
+
1585
+ ~HasManyWorker () {
1586
+ delete iterator_;
1587
+ }
1588
+
1589
+ void DoExecute () override {
1590
+ for (size_t i = 0 ; i != keys_.size (); i++) {
1591
+ leveldb::Slice target = leveldb::Slice (keys_[i]);
1592
+
1593
+ if (iterator_->SeekExact (target)) {
1594
+ bitset_[i >> 5 ] |= 1 << (i & 31 ); // Set bit
1595
+ }
1596
+ }
1597
+
1598
+ SetStatus (iterator_->Status ());
1599
+ iterator_->CloseIterator ();
1600
+ }
1601
+
1602
+ private:
1603
+ const std::vector<std::string> keys_;
1604
+ uint32_t * bitset_;
1605
+ BaseIterator* iterator_;
1606
+ };
1607
+
1608
+ /* *
1609
+ * Check if the database has entries with the given keys.
1610
+ */
1611
+ NAPI_METHOD (db_has_many) {
1612
+ NAPI_ARGV (5 );
1613
+ NAPI_DB_CONTEXT ();
1614
+ NAPI_PROMISE ();
1615
+
1616
+ const auto keys = KeyArray (env, argv[1 ]);
1617
+ const bool fillCache = BooleanValue (env, argv[2 ], true );
1618
+
1619
+ ExplicitSnapshot* snapshot = NULL ;
1620
+ napi_get_value_external (env, argv[3 ], (void **)&snapshot);
1621
+
1622
+ uint32_t * bitset = NULL ;
1623
+ NAPI_STATUS_THROWS (napi_get_arraybuffer_info (env, argv[4 ], (void **)&bitset, NULL ));
1624
+
1625
+ HasManyWorker* worker = new HasManyWorker (
1626
+ env, database, keys, deferred, bitset, fillCache, snapshot
1627
+ );
1628
+
1629
+ worker->Queue (env);
1630
+ return promise;
1631
+ }
1632
+
1484
1633
/* *
1485
1634
* Worker class for deleting a value from a database.
1486
1635
*/
@@ -2280,6 +2429,8 @@ NAPI_INIT() {
2280
2429
NAPI_EXPORT_FUNCTION (db_put);
2281
2430
NAPI_EXPORT_FUNCTION (db_get);
2282
2431
NAPI_EXPORT_FUNCTION (db_get_many);
2432
+ NAPI_EXPORT_FUNCTION (db_has);
2433
+ NAPI_EXPORT_FUNCTION (db_has_many);
2283
2434
NAPI_EXPORT_FUNCTION (db_del);
2284
2435
NAPI_EXPORT_FUNCTION (db_clear);
2285
2436
NAPI_EXPORT_FUNCTION (db_approximate_size);
0 commit comments