Current functionality and the future (hoping on support for all use cases)
See original GitHub issueJust wanted to jot down how it actually works but with words. Mostly for my own processing but also to explain to others that might be interested.
What I like the most with this module, is that it allows you to create sub levels (duh) but also with their specific encodings and it does this quite cleverly by peeling off the levelup layer, adding one down layer (SubDb) on top of the current down (leveldown, memdown etc wrapped by levelup) and then finishes off by adding back a levelup layer.
This trickery happens in ._open() (with some added comments):
SubDown.prototype._open = function (opts, cb) {
var self = this
if (this.db.isOpen()) {
if (this.db.db.type === 'subdown' && this.db.db.prefix) {
// This happens when we do a nested sub level
// this.db is a levelup and this.db.db is the SubDown and
// this.db.db.leveldown is the original down (see else case below)
this.prefix = this.db.db.prefix + this.prefix
this.leveldown = this.db.db.leveldown
} else {
// this.db is a levelup and this.db.db is the *down it's wrapping
this.leveldown = this.db.db
}
return done()
}
this.db.on('open', this.open.bind(this, opts, done))
function done (err) {
if (err || !self._beforeOpen) return cb(err)
self._beforeOpen(cb)
}
}
The reason this works is because older versions of levelup takes care of the encodings and just applies them to a *down (SubDown in this case).
From index.js:
module.exports = function (db, prefix, opts) {
if (typeof prefix === 'object' && !opts) return module.exports(db, null, prefix)
if (!opts) opts = {}
opts.db = function () {
return subdown(db, prefix, opts)
}
return levelup(opts)
}
The problem we face now is that levelup was rewritten and encodings moved out into encoding-down so if we want to support encodings in the same way we can no longer rely on levelup alone.
So what if we in index.js finish off with:
module.exports = function (db, prefix, opts) {
// ..
return levelup(encoding(subdown(db, prefix, opts), opts), opts)
}
That should take care of the current functionality of levelup. But it’s not enough. What if we want to create a sub level out of a level? This will not work (as @emilbayes pointed out here https://github.com/Level/subleveldown/pull/7#issuecomment-367958457) since it would mean double decodings (and encodings I guess).
So what I propose is that we continue with this trickery a bit and tweak ._open() to peel off two layers if there’s an encoding-down:
SubDown.prototype._open = function (opts, cb) {
var self = this
if (this.db.isOpen()) {
if (this.db.db.type === 'subdown' && this.db.db.prefix) {
this.prefix = this.db.db.prefix + this.prefix
this.leveldown = this.db.db.leveldown
} else if (encDown.isEncodingDown(this.db.db)) { // <-- HERE!
this.leveldown = this.db.db.db
} else {
this.leveldown = this.db.db
}
return done()
}
this.db.on('open', this.open.bind(this, opts, done))
function done (err) {
if (err || !self._beforeOpen) return cb(err)
self._beforeOpen(cb)
}
}
It’s a bit hacky, but I think it should work well. We need to implement a proper static encDown.isEncodingDown() function though which should support cross realms (using Symbol.for() etc).
Issue Analytics
- State:
- Created 5 years ago
- Reactions:2
- Comments:13 (13 by maintainers)

Top Related StackOverflow Question
@vweevers Had to tweak the first
subdbcheck tothis.leveldown = down(subdb.db)since callingthis.leveldown = down(subdb)will result back insubdb.return this.db.down(type). It doesn’t matter ifthis.dbisdeferred-leveldownor the opendb, the result would be the same.I was thinking about that too. We might be able to get by with
while (db.db) db = db.dbwith some additional check thatdb.dbis anabstract-leveldown(and not an underlying IndexedDB, for example).