Skip to content
This repository was archived by the owner on Dec 2, 2024. It is now read-only.
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
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,33 @@ var sub = require('subleveldown')
var level = require('level')

var db = level('db')
var example = sub(db, 'example')
var nested = sub(example, 'nested')
```

var test = sub(db, 'test') // test is just a regular levelup
var test2 = sub(db, 'test2')
var nested = sub(test, 'nested')
The `example` and `nested` db's are just regular [`levelup`][levelup] instances:

test.put('hello', 'world', function() {
nested.put('hi', 'welt', function() {
```js
example.put('hello', 'world', function () {
nested.put('hi', 'welt', function () {
// will print {key:'hello', value:'world'}
test.createReadStream().on('data', console.log)
example.createReadStream().on('data', console.log)
})
})
```

They also support `db.clear()` which is very useful to empty a bucket of stuff:

```js
example.clear(function (err) {})

// Or delete a range within `example`
example.clear({ gt: 'hello' }, function (err) {})

// For believers
await example.clear()
```

## Background

`subleveldown` separates a [`levelup`][levelup] database into sections - or _sublevels_ from here on out. Think SQL tables, but evented, ranged and realtime!
Expand Down
29 changes: 29 additions & 0 deletions leveldown.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ var inherits = require('inherits')
var abstract = require('abstract-leveldown')
var wrap = require('level-option-wrap')

var rangeOptions = 'start end gt gte lt lte'.split(' ')
var defaultClear = abstract.AbstractLevelDOWN.prototype._clear
var hasOwnProperty = Object.prototype.hasOwnProperty
var END = Buffer.from([0xff])

function concat (prefix, key, force) {
Expand Down Expand Up @@ -123,6 +126,32 @@ SubDown.prototype._batch = function (operations, opts, cb) {
this.leveldown.batch(operations, opts, cb)
}

SubDown.prototype._clear = function (opts, cb) {
if (typeof this.leveldown.clear === 'function') {
// Prefer optimized implementation of clear()
opts = addRestOptions(wrap(opts, this._wrap), opts)
this.leveldown.clear(opts, cb)
} else {
// Fall back to iterator-based implementation
defaultClear.call(this, opts, cb)
}
}

function addRestOptions (target, opts) {
for (var k in opts) {
if (hasOwnProperty.call(opts, k) && !isRangeOption(k)) {
target[k] = opts[k]
}
}

return target
}

function isRangeOption (k) {
return rangeOptions.indexOf(k) !== -1
}

// TODO (refactor): use addRestOptions instead
function extend (xopts, opts) {
xopts.keys = opts.keys
xopts.values = opts.values
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@
"test": "test"
},
"dependencies": {
"abstract-leveldown": "^6.0.2",
"encoding-down": "^6.0.1",
"abstract-leveldown": "^6.1.1",
"encoding-down": "^6.2.0",
"inherits": "^2.0.3",
"level-option-wrap": "^1.1.0",
"levelup": "^4.0.1"
"levelup": "^4.2.0"
},
"devDependencies": {
"after": "^0.8.2",
"coveralls": "^3.0.2",
"dependency-check": "^3.3.0",
"hallmark": "^2.0.0",
"level-community": "^3.0.0",
"level-concat-iterator": "^2.0.1",
"memdown": "^5.0.0",
"nyc": "^14.0.0",
"standard": "^14.0.0",
Expand Down
82 changes: 80 additions & 2 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ var test = require('tape')
var suite = require('abstract-leveldown/test')
var memdown = require('memdown')
var encoding = require('encoding-down')
var concat = require('level-concat-iterator')
var after = require('after')
var subdown = require('../leveldown')
var subdb = require('..')
var levelup = require('levelup')
Expand All @@ -16,7 +18,10 @@ suite({
// Unsupported features
seek: false,
createIfMissing: false,
errorIfExists: false
errorIfExists: false,

// Opt-in to new clear() tests
clear: true
})

// Test without a user-provided levelup layer
Expand All @@ -29,7 +34,10 @@ suite({
// Unsupported features
seek: false,
createIfMissing: false,
errorIfExists: false
errorIfExists: false,

// Opt-in to new clear() tests
clear: true
})

// Additional tests for this implementation
Expand Down Expand Up @@ -283,4 +291,74 @@ test('SubDb main function', function (t) {
})
})
})

t.test('clear (optimized)', function (t) {
var down = memdown()
t.is(typeof down.clear, 'function', 'has clear()')
testClear(t, down)
})

t.test('clear (with iterator-based fallback)', function (t) {
var down = memdown()
down.clear = undefined
testClear(t, down)
})

function testClear (t, down) {
const db = levelup(down)
const sub1 = subdb(db, '1')
const sub2 = subdb(db, '2')

populate([sub1, sub2], ['a', 'b'], function (err) {
t.ifError(err, 'no populate error')

verify(['!1!a', '!1!b', '!2!a', '!2!b'], function () {
clear([sub1], {}, function (err) {
t.ifError(err, 'no clear error')

verify(['!2!a', '!2!b'], function () {
populate([sub1], ['a', 'b'], function (err) {
t.ifError(err, 'no populate error')

clear([sub2], { lt: 'b' }, function (err) {
t.ifError(err, 'no clear error')
verify(['!1!a', '!1!b', '!2!b'], t.end.bind(t))
})
})
})
})
})
})

function populate (subs, items, callback) {
const next = after(subs.length, callback)

for (const sub of subs) {
sub.batch(items.map(function (item) {
return { type: 'put', key: item, value: item }
}), next)
}
}

function clear (subs, opts, callback) {
const next = after(subs.length, callback)

for (const sub of subs) {
sub.clear(opts, next)
}
}

function verify (expected, callback) {
concat(db.iterator({ keyAsBuffer: false }), function (err, entries) {
t.ifError(err, 'no concat error')
t.same(entries.map(getKey), expected)

if (callback) callback()
})
}
}
})

function getKey (entry) {
return entry.key
}