Level / abstract-level

Abstract class for a lexicographically sorted key-value database.
MIT License
128 stars 8 forks source link

Make iterator._seek async #34

Closed ccollie closed 2 years ago

ccollie commented 2 years ago

I'm writing a module implementing abstract-level over cacache. One issue is that the api call in cacache to get index entries returns a promise, so it's currently not possible AFAICS to implement iterators given that seek is not async.

Is there a workaround using a DeferredIterator ?

ccollie commented 2 years ago

Thinking about it, iterators are possible, but currently async _seek is not

vweevers commented 2 years ago

The result of a seek doesn't matter until next() is called, so it can be async behind the scenes.

ccollie commented 2 years ago

The result of a seek doesn't matter until next() is called, so it can be async behind the scenes.

I worked around it by storing the seek parameters and handling them if they exist in the other calls

  Ctor.prototype[kInit] = function (db, options) {
    const reverse = options.reverse

    this[kDb] = db
    this[kReverse] = reverse
    this[kOptions] = options
    this[kLocation] = db[kLocation]

    this[kAdvance] = this[kReverse] ? this[kAdvancePrev] : this[kAdvanceNext]

    this[kIndexPromise] = cacache.ls(location).then(entries => {
      const keys = entries.map(entry => entry.key).sort(compare);

      this[kEntries] = entries
      const { start, end, lowerBound, upperBound } = parseBounds(keys, options)

      this[kCursor] = 0
      this[kLowerBound] = lowerBound
      this[kUpperBound] = upperBound
      this[kIndexKeys] = keys.splice(start, end - start + 1)
    });
  }

  Ctor.prototype._seek = function (target, options) {
    this[kSeekOptions] = {
      ...options,
      target
    }
  }

  Ctor.prototype[kNext] = function (callback) {
    this[kAwaitIndex]().then(() => {
      const key = this[kAdvance]()
      if (!key) return this.nextTick(callback)
      getValue(this[kLocation], key, (err, value) => {
        callback(err, key, value.data)
      })
    }).catch(callback)
  }

 Ctor.prototype[kAwaitIndex] = function () {
    const options = this[kSeekOptions]
    if (!options) {
      return this[kIndexPromise]
    }
    this[kSeekOptions] = null

    // handle seek
    return this[kIndexPromise].then(() =>
    {
         this[kCursor] = parseSeekParams(this, options)
    }).catch(err => {
      this[kDone] = true
      this[kCursor] = -1
      return err
    });
  }