Skip to content

Commit

Permalink
Support clear() (#182)
Browse files Browse the repository at this point in the history
* Support clear()
* Optimize clear() and fix IE & Edge
  • Loading branch information
vweevers committed Sep 20, 2019
1 parent f207ae6 commit 9a04b3b
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 23 deletions.
29 changes: 29 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ var serialize = require('./util/serialize')
var deserialize = require('./util/deserialize')
var setImmediate = require('./util/immediate')
var support = require('./util/support')
var clear = require('./util/clear')
var createKeyRange = require('./util/key-range')

var DEFAULT_PREFIX = 'level-js-'

Expand Down Expand Up @@ -181,6 +183,33 @@ Level.prototype._batch = function (operations, options, callback) {
loop()
}

Level.prototype._clear = function (options, callback) {
try {
var keyRange = createKeyRange(options)
} catch (e) {
// The lower key is greater than the upper key.
// IndexedDB throws an error, but we'll just do nothing.
return setImmediate(callback)
}

if (options.limit >= 0) {
// IDBObjectStore#delete(range) doesn't have such an option.
// Fall back to cursor-based implementation.
return clear(this, this.location, keyRange, options, callback)
}

try {
var store = this.store('readwrite')
var req = keyRange ? store.delete(keyRange) : store.clear()
} catch (err) {
return setImmediate(function () {
callback(err)
})
}

this.await(req, callback)
}

Level.prototype._close = function (callback) {
this.db.close()
setImmediate(callback)
Expand Down
23 changes: 2 additions & 21 deletions iterator.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
/* global IDBKeyRange */

'use strict'

var inherits = require('inherits')
var AbstractIterator = require('abstract-leveldown').AbstractIterator
var ltgt = require('ltgt')
var createKeyRange = require('./util/key-range')
var deserialize = require('./util/deserialize')
var setImmediate = require('./util/immediate')
var noop = function () {}
Expand Down Expand Up @@ -34,7 +32,7 @@ function Iterator (db, location, options) {
}

try {
var keyRange = this.createKeyRange(options)
var keyRange = createKeyRange(options)
} catch (e) {
// The lower key is greater than the upper key.
// IndexedDB throws an error, but we'll just return 0 results.
Expand All @@ -47,23 +45,6 @@ function Iterator (db, location, options) {

inherits(Iterator, AbstractIterator)

Iterator.prototype.createKeyRange = function (options) {
var lower = ltgt.lowerBound(options)
var upper = ltgt.upperBound(options)
var lowerOpen = ltgt.lowerBoundExclusive(options)
var upperOpen = ltgt.upperBoundExclusive(options)

if (lower !== undefined && upper !== undefined) {
return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen)
} else if (lower !== undefined) {
return IDBKeyRange.lowerBound(lower, lowerOpen)
} else if (upper !== undefined) {
return IDBKeyRange.upperBound(upper, upperOpen)
} else {
return null
}
}

Iterator.prototype.createIterator = function (location, keyRange, reverse) {
var self = this
var transaction = this.db.db.transaction([location], 'readonly')
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"./util/immediate.js": "./util/immediate-browser.js"
},
"dependencies": {
"abstract-leveldown": "~6.0.1",
"abstract-leveldown": "~6.1.1",
"immediate": "~3.2.3",
"inherits": "^2.0.3",
"ltgt": "^2.1.2"
Expand Down
24 changes: 24 additions & 0 deletions test/custom-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,5 +296,29 @@ module.exports = function (leveljs, test, testCommon) {
})
})

// TODO: move to abstract-leveldown test suite (and add to iterator tests too)
test('clear() with lower key greater than upper key', function (t) {
var db = testCommon.factory()

db.open(function (err) {
t.ifError(err, 'no open error')

db.put('a', 'a', function (err) {
t.ifError(err, 'no put error')

db.clear({ gt: 'b', lt: 'a' }, function (err) {
t.ifError(err, 'no clear error')

db.get('a', { asBuffer: false }, function (err, value) {
t.ifError(err, 'no get error')
t.is(value, 'a')

db.close(t.end.bind(t))
})
})
})
})
})

test('teardown', testCommon.tearDown)
}
5 changes: 4 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ var testCommon = suite.common({
seek: false,

// Support of buffer keys depends on environment
bufferKeys: leveljs(uuid()).supports.bufferKeys
bufferKeys: leveljs(uuid()).supports.bufferKeys,

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

// Test abstract-leveldown compliance
Expand Down
36 changes: 36 additions & 0 deletions util/clear.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict'

var setImmediate = require('./immediate')

module.exports = function clear (db, location, keyRange, options, callback) {
if (options.limit === 0) return setImmediate(callback)

var transaction = db.db.transaction([location], 'readwrite')
var store = transaction.objectStore(location)
var count = 0

transaction.oncomplete = function () {
callback()
}

transaction.onabort = function () {
callback(transaction.error || new Error('aborted by user'))
}

// A key cursor is faster (skips reading values) but not supported by IE
var method = store.openKeyCursor ? 'openKeyCursor' : 'openCursor'
var direction = options.reverse ? 'prev' : 'next'

store[method](keyRange, direction).onsuccess = function (ev) {
var cursor = ev.target.result

if (cursor) {
// Wait for a request to complete before continuing, saving CPU.
store.delete(cursor.key).onsuccess = function () {
if (options.limit <= 0 || ++count < options.limit) {
cursor.continue()
}
}
}
}
}
23 changes: 23 additions & 0 deletions util/key-range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* global IDBKeyRange */

'use strict'

var ltgt = require('ltgt')
var NONE = {}

module.exports = function createKeyRange (options) {
var lower = ltgt.lowerBound(options, NONE)
var upper = ltgt.upperBound(options, NONE)
var lowerOpen = ltgt.lowerBoundExclusive(options, NONE)
var upperOpen = ltgt.upperBoundExclusive(options, NONE)

if (lower !== NONE && upper !== NONE) {
return IDBKeyRange.bound(lower, upper, lowerOpen, upperOpen)
} else if (lower !== NONE) {
return IDBKeyRange.lowerBound(lower, lowerOpen)
} else if (upper !== NONE) {
return IDBKeyRange.upperBound(upper, upperOpen)
} else {
return null
}
}

0 comments on commit 9a04b3b

Please sign in to comment.