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

Commit

Permalink
Add db.getMany(keys)
Browse files Browse the repository at this point in the history
  • Loading branch information
vweevers committed Sep 25, 2021
1 parent dcde940 commit 5fc85b0
Show file tree
Hide file tree
Showing 11 changed files with 421 additions and 38 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,17 @@ Get a value from the store by `key`. The optional `options` object may contain:

- `asBuffer` _(boolean, default: `true`)_: Whether to return the `value` as a Buffer. If `false`, the returned type depends on the implementation.

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 the value.
The `callback` function will be called with an `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.

### `db.getMany(keys[, options][, callback])`

Get multiple values from the store by an array of `keys`. The optional `options` object may contain:

- `asBuffer` _(boolean, default: `true`)_: Whether to return the `value` as a Buffer. If `false`, the returned type depends on the implementation.

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`.

If no callback is provided, a promise is returned.

### `db.put(key, value[, options], callback)`

Expand Down Expand Up @@ -435,6 +445,14 @@ Get a value by `key`. The `options` object will always have the following proper

The default `_get()` invokes `callback` on a next tick with a `NotFound` error. It must be overridden.

### `db._getMany(keys, options, callback)`

**This new method is optional for the time being. To enable its tests, set the [`getMany` option of the test suite](#excluding-tests) to `true`.**

Get multiple values by an array of `keys`. The `options` object will always have the following properties: `asBuffer`. If an error occurs, call the `callback` function with an `Error`. Otherwise call `callback` with `null` as the first argument and an array of values as the second. If a key does not exist, set the relevant value to `undefined`.

The default `_getMany()` invokes `callback` on a next tick with an array of values that is equal in length to `keys` and is filled with `undefined`. It must be overridden to support `getMany()` but this is currently an opt-in feature. If the implementation does support `getMany()` then `db.supports.getMany` must be set to true via the [constructor](#db--abstractleveldownmanifest).

### `db._put(key, value, options, callback)`

Store a new entry or overwrite an existing entry. There are no default options but `options` will always be an object. If putting failed, call the `callback` function with an `Error`. Otherwise call `callback` without any arguments.
Expand Down Expand Up @@ -581,6 +599,7 @@ This also serves as a signal to users of your implementation. The following opti
- `bufferKeys`: set to `false` if binary keys are not supported by the underlying storage
- `seek`: set to `false` if your `iterator` does not implement `_seek`
- `clear`: defaults to `false` until a next major release. Set to `true` if your implementation either implements `_clear()` itself or is suitable to use the default implementation of `_clear()` (which requires binary key support).
- `getMany`: defaults to `false` until a next major release. Set to `true` if your implementation implements `_getMany()`.
- `snapshots`: set to `false` if any of the following is true:
- Reads don't operate on a [snapshot](#iterator)
- Snapshots are created asynchronously
Expand Down
80 changes: 80 additions & 0 deletions abstract-leveldown.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

const supports = require('level-supports')
const isBuffer = require('is-buffer')
const catering = require('catering')
const AbstractIterator = require('./abstract-iterator')
const AbstractChainedBatch = require('./abstract-chained-batch')
const getCallback = require('./lib/common').getCallback
const getOptions = require('./lib/common').getOptions

const hasOwnProperty = Object.prototype.hasOwnProperty
const rangeOptions = ['lt', 'lte', 'gt', 'gte']

Expand Down Expand Up @@ -90,6 +94,51 @@ AbstractLevelDOWN.prototype._get = function (key, options, callback) {
this._nextTick(function () { callback(new Error('NotFound')) })
}

AbstractLevelDOWN.prototype.getMany = function (keys, options, callback) {
callback = getCallback(options, callback)
callback = catering.fromCallback(callback)
options = getOptions(options)

if (maybeError(this, callback)) {
return callback.promise
}

if (!Array.isArray(keys)) {
this._nextTick(callback, new Error('getMany() requires an array argument'))
return callback.promise
}

if (keys.length === 0) {
this._nextTick(callback, null, [])
return callback.promise
}

if (typeof options.asBuffer !== 'boolean') {
options = { ...options, asBuffer: true }
}

const serialized = new Array(keys.length)

for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const err = this._checkKey(key)

if (err) {
this._nextTick(callback, err)
return callback.promise
}

serialized[i] = this._serializeKey(key)
}

this._getMany(serialized, options, callback)
return callback.promise
}

AbstractLevelDOWN.prototype._getMany = function (keys, options, callback) {
this._nextTick(callback, null, new Array(keys.length).fill(undefined))
}

AbstractLevelDOWN.prototype.put = function (key, value, options, callback) {
if (typeof options === 'function') callback = options

Expand Down Expand Up @@ -315,9 +364,40 @@ AbstractLevelDOWN.prototype._checkValue = function (value) {
}
}

// Undocumented, only here for levelup API parity
AbstractLevelDOWN.prototype.isOpen = function () {
return this.status === 'open'
}

AbstractLevelDOWN.prototype.isClosed = function () {
return this.status === 'new' || this.status === 'closing' || this.status === 'closed'
}

// Expose browser-compatible nextTick for dependents
// TODO: rename _nextTick to _queueMicrotask
// TODO: after we drop node 10, also use queueMicrotask in node
AbstractLevelDOWN.prototype._nextTick = require('./next-tick')

module.exports = AbstractLevelDOWN

function maybeError (db, callback) {
if (db.status !== 'open') {
// Exemption to support deferredOpen
if (db.type === 'deferred-leveldown') {
return false
}

// TODO: deferred-leveldown and levelup are inconsistent: the former allows
// operations on any db status, the latter only if 'opening' or 'open'. We
// should define the scope of the deferredOpen feature. For now, pick the
// levelup behavior. Ref https://github.com/Level/community/issues/58
if (db.supports.deferredOpen && db.status === 'opening') {
return false
}

db._nextTick(callback, new Error('Database is not open'))
return true
}

return false
}
9 changes: 9 additions & 0 deletions lib/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict'

exports.getCallback = function (options, callback) {
return typeof options === 'function' ? options : callback
}

exports.getOptions = function (options) {
return typeof options === 'object' && options !== null ? options : {}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
],
"dependencies": {
"buffer": "^6.0.3",
"catering": "^2.0.0",
"is-buffer": "^2.0.5",
"level-concat-iterator": "^3.0.0",
"level-supports": "^2.0.0",
"level-supports": "^2.0.1",
"queue-microtask": "^1.2.3"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions test/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ function testCommon (options) {
snapshots: options.snapshots !== false,
seek: options.seek !== false,
clear: !!options.clear,
getMany: !!options.getMany,

// Support running test suite on a levelup db. All options below this line
// are undocumented and should not be used by abstract-leveldown db's (yet).
Expand Down
Loading

0 comments on commit 5fc85b0

Please sign in to comment.