Skip to content
This repository was archived by the owner on Dec 2, 2024. It is now read-only.
/ subleveldown Public archive
This repository was archived by the owner on Dec 2, 2024. It is now read-only.

Current functionality and the future (hoping on support for all use cases) #34

@ralphtheninja

Description

@ralphtheninja

Just 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 #7 (comment)) 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions