Skip to content

Add builtin sublevels ✨ #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright © 2013 Rod Vagg and the contributors to abstract-level and abstract-leveldown.
Copyright © 2013 Rod Vagg and the contributors to abstract-level.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the and abstract-leveldown because I'd also have to list encoding-down, levelup, deferred-leveldown and subleveldown. Not sure how meaningful that is.

@mafintosh I copied code from subleveldown to here. You're the copyright holder on subleveldown. Do you want your name in the LICENSE file of abstract-level (or in code comments)?


Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
193 changes: 169 additions & 24 deletions README.md

Large diffs are not rendered by default.

414 changes: 342 additions & 72 deletions UPGRADING.md

Large diffs are not rendered by default.

36 changes: 22 additions & 14 deletions abstract-chained-batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,24 @@ AbstractChainedBatch.prototype.put = function (key, value, options) {
const err = this.db._checkKey(key) || this.db._checkValue(value)
if (err) throw err

const keyEncoding = this.db.keyEncoding(options && options.keyEncoding)
const valueEncoding = this.db.valueEncoding(options && options.valueEncoding)
const db = options && options.sublevel != null ? options.sublevel : this.db
const original = options
const keyEncoding = db.keyEncoding(options && options.keyEncoding)
const valueEncoding = db.valueEncoding(options && options.valueEncoding)
const keyFormat = keyEncoding.format

// Forward encoding options
if (!options || options.keyEncoding !== keyEncoding.format ||
options.valueEncoding !== valueEncoding.format) {
options = {
...options,
keyEncoding: keyEncoding.format,
valueEncoding: valueEncoding.format
}
options = { ...options, keyEncoding: keyFormat, valueEncoding: valueEncoding.format }

// Prevent double prefixing
if (db !== this.db) {
options.sublevel = null
}

this._put(keyEncoding.encode(key), valueEncoding.encode(value), options)
const mappedKey = db.prefixKey(keyEncoding.encode(key), keyFormat)
const mappedValue = valueEncoding.encode(value)

this._put(mappedKey, mappedValue, options)
this[kOperations].push({ ...original, type: 'put', key, value })

return this
Expand All @@ -75,15 +78,20 @@ AbstractChainedBatch.prototype.del = function (key, options) {
const err = this.db._checkKey(key)
if (err) throw err

const db = options && options.sublevel != null ? options.sublevel : this.db
const original = options
const keyEncoding = this.db.keyEncoding(options && options.keyEncoding)
const keyEncoding = db.keyEncoding(options && options.keyEncoding)
const keyFormat = keyEncoding.format

// Forward encoding options
if (!options || options.keyEncoding !== keyEncoding.format) {
options = { ...options, keyEncoding: keyEncoding.format }
options = { ...options, keyEncoding: keyFormat }

// Prevent double prefixing
if (db !== this.db) {
options.sublevel = null
}

this._del(keyEncoding.encode(key), options)
this._del(db.prefixKey(keyEncoding.encode(key), keyFormat), options)
this[kOperations].push({ ...original, type: 'del', key })

return this
Expand Down
12 changes: 6 additions & 6 deletions abstract-iterator.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,15 @@ AbstractIterator.prototype.seek = function (target, options) {
code: 'LEVEL_ITERATOR_BUSY'
})
} else {
const keyEncoding = this.db.keyEncoding(
options.keyEncoding || this[kKeyEncoding]
)
const keyEncoding = this.db.keyEncoding(options.keyEncoding || this[kKeyEncoding])
const keyFormat = keyEncoding.format

if (options.keyEncoding !== keyEncoding.format) {
options = { ...options, keyEncoding: keyEncoding.format }
if (options.keyEncoding !== keyFormat) {
options = { ...options, keyEncoding: keyFormat }
}

this._seek(keyEncoding.encode(target), options)
const mapped = this.db.prefixKey(keyEncoding.encode(target), keyFormat)
this._seek(mapped, options)
}
}

Expand Down
109 changes: 62 additions & 47 deletions abstract-level.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ function AbstractLevel (manifest, options) {
promises: true,
clear: true,
getMany: true,
idempotentOpen: true,
passiveOpen: true,
deferredOpen: true,
snapshots: manifest.snapshots !== false,
permanence: manifest.permanence !== false,
Expand Down Expand Up @@ -77,14 +75,14 @@ function AbstractLevel (manifest, options) {
}

this[kDefaultOptions] = {
empty: {},
entry: {
empty: Object.freeze({}),
entry: Object.freeze({
keyEncoding: this[kKeyEncoding].commonName,
valueEncoding: this[kValueEncoding].commonName
},
key: {
}),
key: Object.freeze({
keyEncoding: this[kKeyEncoding].commonName
}
})
}

// Let subclass finish its constructor
Expand Down Expand Up @@ -285,18 +283,15 @@ AbstractLevel.prototype.get = function (key, options, callback) {

const keyEncoding = this.keyEncoding(options.keyEncoding)
const valueEncoding = this.valueEncoding(options.valueEncoding)
const keyFormat = keyEncoding.format
const valueFormat = valueEncoding.format

// Forward encoding options to the underlying store
if (options.keyEncoding !== keyEncoding.format ||
options.valueEncoding !== valueEncoding.format) {
options = {
...options,
keyEncoding: keyEncoding.format,
valueEncoding: valueEncoding.format
}
if (options.keyEncoding !== keyFormat || options.valueEncoding !== valueFormat) {
options = { ...options, keyEncoding: keyFormat, valueEncoding: valueFormat }
}

this._get(keyEncoding.encode(key), options, (err, value) => {
this._get(this.prefixKey(keyEncoding.encode(key), keyFormat), options, (err, value) => {
if (err) {
// Normalize not found error for backwards compatibility with abstract-leveldown and level(up)
if (err.code === 'LEVEL_NOT_FOUND' || err.notFound || /NotFound/i.test(err)) {
Expand Down Expand Up @@ -353,18 +348,15 @@ AbstractLevel.prototype.getMany = function (keys, options, callback) {

const keyEncoding = this.keyEncoding(options.keyEncoding)
const valueEncoding = this.valueEncoding(options.valueEncoding)
const keyFormat = keyEncoding.format
const valueFormat = valueEncoding.format

// Forward encoding options
if (options.keyEncoding !== keyEncoding.format ||
options.valueEncoding !== valueEncoding.format) {
options = {
...options,
keyEncoding: keyEncoding.format,
valueEncoding: valueEncoding.format
}
if (options.keyEncoding !== keyFormat || options.valueEncoding !== valueFormat) {
options = { ...options, keyEncoding: keyFormat, valueEncoding: valueFormat }
}

const encoded = new Array(keys.length)
const mappedKeys = new Array(keys.length)

for (let i = 0; i < keys.length; i++) {
const key = keys[i]
Expand All @@ -375,10 +367,10 @@ AbstractLevel.prototype.getMany = function (keys, options, callback) {
return callback[kPromise]
}

encoded[i] = keyEncoding.encode(key)
mappedKeys[i] = this.prefixKey(keyEncoding.encode(key), keyFormat)
}

this._getMany(encoded, options, (err, values) => {
this._getMany(mappedKeys, options, (err, values) => {
if (err) return callback(err)

try {
Expand Down Expand Up @@ -427,18 +419,18 @@ AbstractLevel.prototype.put = function (key, value, options, callback) {

const keyEncoding = this.keyEncoding(options.keyEncoding)
const valueEncoding = this.valueEncoding(options.valueEncoding)
const keyFormat = keyEncoding.format
const valueFormat = valueEncoding.format

// Forward encoding options
if (options.keyEncoding !== keyEncoding.format ||
options.valueEncoding !== valueEncoding.format) {
options = {
...options,
keyEncoding: keyEncoding.format,
valueEncoding: valueEncoding.format
}
if (options.keyEncoding !== keyFormat || options.valueEncoding !== valueFormat) {
options = { ...options, keyEncoding: keyFormat, valueEncoding: valueFormat }
}

this._put(keyEncoding.encode(key), valueEncoding.encode(value), options, (err) => {
const mappedKey = this.prefixKey(keyEncoding.encode(key), keyFormat)
const mappedValue = valueEncoding.encode(value)

this._put(mappedKey, mappedValue, options, (err) => {
if (err) return callback(err)
this.emit('put', key, value)
callback()
Expand Down Expand Up @@ -473,13 +465,14 @@ AbstractLevel.prototype.del = function (key, options, callback) {
}

const keyEncoding = this.keyEncoding(options.keyEncoding)
const keyFormat = keyEncoding.format

// Forward encoding options
if (options.keyEncoding !== keyEncoding.format) {
options = { ...options, keyEncoding: keyEncoding.format }
if (options.keyEncoding !== keyFormat) {
options = { ...options, keyEncoding: keyFormat }
}

this._del(keyEncoding.encode(key), options, (err) => {
this._del(this.prefixKey(keyEncoding.encode(key), keyFormat), options, (err) => {
if (err) return callback(err)
this.emit('del', key)
callback()
Expand Down Expand Up @@ -507,7 +500,7 @@ AbstractLevel.prototype.batch = function (operations, options, callback) {
else callback = getCallback(options, callback)

callback = fromCallback(callback, kPromise)
options = getOptions(options, this[kDefaultOptions].entry)
options = getOptions(options, this[kDefaultOptions].empty)

if (this[kStatus] === 'opening') {
this.defer(() => this.batch(operations, options, callback))
Expand All @@ -528,8 +521,8 @@ AbstractLevel.prototype.batch = function (operations, options, callback) {
return callback[kPromise]
}

const encoded = new Array(operations.length)
const { keyEncoding: ke, valueEncoding: ve, ...rest } = options
const mapped = new Array(operations.length)
const { keyEncoding: ke, valueEncoding: ve, ...forward } = options

for (let i = 0; i < operations.length; i++) {
if (typeof operations[i] !== 'object' || operations[i] === null) {
Expand All @@ -551,10 +544,12 @@ AbstractLevel.prototype.batch = function (operations, options, callback) {
return callback[kPromise]
}

const keyEncoding = this.keyEncoding(op.keyEncoding || ke)
const db = op.sublevel != null ? op.sublevel : this
const keyEncoding = db.keyEncoding(op.keyEncoding || ke)
const keyFormat = keyEncoding.format

op.key = keyEncoding.encode(op.key)
op.keyEncoding = keyEncoding.format
op.key = db.prefixKey(keyEncoding.encode(op.key), keyFormat)
op.keyEncoding = keyFormat

if (op.type === 'put') {
const valueErr = this._checkValue(op.value)
Expand All @@ -564,16 +559,21 @@ AbstractLevel.prototype.batch = function (operations, options, callback) {
return callback[kPromise]
}

const valueEncoding = this.valueEncoding(op.valueEncoding || ve)
const valueEncoding = db.valueEncoding(op.valueEncoding || ve)

op.value = valueEncoding.encode(op.value)
op.valueEncoding = valueEncoding.format
}

encoded[i] = op
// Prevent double prefixing
if (db !== this) {
op.sublevel = null
}

mapped[i] = op
}

this._batch(encoded, rest, (err) => {
this._batch(mapped, forward, (err) => {
if (err) return callback(err)
this.emit('batch', operations)
callback()
Expand All @@ -586,6 +586,18 @@ AbstractLevel.prototype._batch = function (operations, options, callback) {
this.nextTick(callback)
}

AbstractLevel.prototype.sublevel = function (name, options) {
return this._sublevel(name, AbstractSublevel.defaults(options))
}

AbstractLevel.prototype._sublevel = function (name, options) {
return new AbstractSublevel(this, name, options)
}

AbstractLevel.prototype.prefixKey = function (key, keyFormat) {
return key
}

AbstractLevel.prototype.clear = function (options, callback) {
callback = getCallback(options, callback)
callback = fromCallback(callback, kPromise)
Expand Down Expand Up @@ -673,7 +685,7 @@ AbstractLevel.prototype[kUndefer] = function () {
}
}

// TODO: docs
// TODO: docs and types
AbstractLevel.prototype.attachResource = function (resource) {
if (typeof resource !== 'object' || resource === null ||
typeof resource.close !== 'function') {
Expand All @@ -683,7 +695,7 @@ AbstractLevel.prototype.attachResource = function (resource) {
this[kResources].add(resource)
}

// TODO: docs
// TODO: docs and types
AbstractLevel.prototype.detachResource = function (resource) {
this[kResources].delete(resource)
}
Expand Down Expand Up @@ -712,7 +724,10 @@ AbstractLevel.prototype._checkValue = function (value) {
// TODO: after we drop node 10, also use queueMicrotask in node
AbstractLevel.prototype.nextTick = require('./lib/next-tick')

const { AbstractSublevel } = require('./lib/abstract-sublevel')({ AbstractLevel })

exports.AbstractLevel = AbstractLevel
exports.AbstractSublevel = AbstractSublevel

const maybeError = function (db, callback) {
if (db[kStatus] !== 'open') {
Expand Down
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export {
AbstractChainedBatchDelOptions
} from './types/abstract-chained-batch'

export {
AbstractSublevel,
AbstractSublevelOptions
} from './types/abstract-sublevel'

export {
NodeCallback
} from './types/interfaces'
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'

exports.AbstractLevel = require('./abstract-level').AbstractLevel
exports.AbstractSublevel = require('./abstract-level').AbstractSublevel
exports.AbstractIterator = require('./abstract-iterator').AbstractIterator
exports.AbstractChainedBatch = require('./abstract-chained-batch').AbstractChainedBatch
42 changes: 42 additions & 0 deletions lib/abstract-sublevel-iterator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict'

const { AbstractIterator } = require('../abstract-iterator')

const kUnfix = Symbol('unfix')
const kIterator = Symbol('iterator')
const kNext = Symbol('next')
const kCallback = Symbol('callback')

class AbstractSublevelIterator extends AbstractIterator {
constructor (db, options, iterator, unfix) {
super(db, options)

// TODO: do this natively if db supports it
this[kUnfix] = unfix
this[kIterator] = iterator
this[kNext] = this[kNext].bind(this)
this[kCallback] = null
}

_next (callback) {
this[kCallback] = callback
this[kIterator].next(this[kNext])
}

[kNext] (err, key, value) {
const callback = this[kCallback]
if (err) return callback(err)
if (key !== undefined) key = this[kUnfix](key)
callback(err, key, value)
}

_seek (target, options) {
this[kIterator].seek(target, options)
}

_close (callback) {
this[kIterator].close(callback)
}
}

exports.AbstractSublevelIterator = AbstractSublevelIterator
Loading