Level / abstract-level

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

Add `db.keys()`, `.values()`, `iterator.nextv()` and `.all()` #12

Closed vweevers closed 2 years ago

vweevers commented 2 years ago

I'm squeezing this in for similar reasons as #8. I wondered whether these additions would be breaking. The short answer is no. In addition, adding this now means level-read-stream can make use of it without conditional code paths based on db.supports.*.

Ref Level/abstract-leveldown#380

Adds key and value iterators: db.keys() and db.values(). As a replacement for levelup's db.create{Key,Value}Stream(). Example:

for await (const key of db.keys({ gte: 'a' }) {
  console.log(key)
}

Adds two new methods to all three types of iterators: nextv() for getting many items (entries, keys or values) in one call and all() for getting all (remaining) items. These methods have functional but suboptimal defaults: all() falls back to repeatedly calling nextv() and that in turn falls back to next(). Example:

const values = await db.values({ limit: 10 }).all()

Adds a lot of new code, with unfortunately some duplicate code because I wanted to avoid mixins and other forms of complexity, which means key and value iterators use classes that are separate from preexisting iterators. For example, a method like _seek() must now be implemented on three classes: AbstractIterator, AbstractKeyIterator and AbstractValueIterator. This (small?) problem extends to implementations and their subclasses, if they choose to override key and value iterators to improve performance.

On the flip side, the new methods are supported across the board: on sublevels, with encodings, with deferred open, and fully functional. This may demonstrate one of the major benefits of abstract-level over abstract-leveldown paired with levelup.

Yet todo:

vweevers commented 2 years ago

@Level/core Side note: I'm concerned about the bus factor here, adding code to an already-refactored code base that no one else is familiar with. Realistically, there's not much I can do about that, besides spending more time not writing code (i.e. looking for contributors, funding, doing that awful thing called self-marketing). I'm still having fun here though, so I'm moving ahead. Just let me know if there's anything I can do to facilitate code reviews.

juliangruber commented 2 years ago

Hey @vweevers, thanks for all the work you're putting in and the clarity of communication! I personally currently don't have the time to look at large PRs, so if it made sense to make smaller PRs that would help me but also I don't think the bus factor is critical here as everything looks to be well organized and your direction is clear

vweevers commented 2 years ago

@juliangruber Noted, thanks! Once I'm past the current refactoring/forking stage, smaller PRs will be easier. For now I will remind myself to make less (unrelated) tweaks, like how this PR includes README changes that I could easily do separately.

vweevers commented 2 years ago

Added tests and needed a few more things to make those pass:

To improve coverage, test/self.js now includes a minimal abstract-level implementation, which only supports utf8 but is otherwise fully compliant, and is used to test the abstract test suite. Without it, coverage is 91%. With it's 98%. Also useful just to demonstrate the new API's and the problem that I outlined in the OP:

https://github.com/Level/abstract-level/blob/273de0dfa826dfb745693bea103455fb191b9592/test/util.js#L121-L278

This PR is good to go, but the amount of code makes me want to sleep on it.

vweevers commented 2 years ago

I'm happy with the public API. Less so with the private API and tests (which in short are "structurally inconsistent") but it's easy enough (for implementations) to work around. In addition, I want to start benchmarking (https://github.com/Level/abstract-level/issues/4) and I'm making it harder for myself to benchmark fairly when I'm moving further away from abstract-leveldown and levelup. So, I'm opening an issue to later refactor the private API (likely in a next major).