Skip to content

Commit

Permalink
Expose path of sublevel (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
vweevers authored Jan 27, 2024
1 parent ccb6c14 commit 429df4b
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 2 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
- [`sublevel.prefix`](#sublevelprefix)
- [`sublevel.parent`](#sublevelparent)
- [`sublevel.db`](#subleveldb)
- [`sublevel.path([local])`](#sublevelpathlocal)
- [Hooks](#hooks)
- [`hook = db.hooks.prewrite`](#hook--dbhooksprewrite)
- [Example](#example)
Expand Down Expand Up @@ -782,7 +783,7 @@ A value iterator has the same interface as `iterator` except that its methods yi

### `sublevel`

A sublevel is an instance of the `AbstractSublevel` class, which extends `AbstractLevel` and thus has the same API as documented above. Sublevels have a few additional properties.
A sublevel is an instance of the `AbstractSublevel` class, which extends `AbstractLevel` and thus has the same API as documented above. Sublevels have a few additional properties and methods.

#### `sublevel.prefix`

Expand Down Expand Up @@ -820,6 +821,24 @@ console.log(example.db === db) // true
console.log(nested.db === db) // true
```

#### `sublevel.path([local])`

Get the path of this sublevel, which is its prefix without separators. If `local` is true, exclude path of parent database. If false (the default) then recurse to form a fully-qualified path that travels from the root database to this sublevel.

```js
const example = db.sublevel('example')
const nested = example.sublevel('nested')
const foo = db.sublevel(['example', 'nested', 'foo'])

// Get global or local path
console.log(nested.path()) // ['example', 'nested']
console.log(nested.path(true)) // ['nested']

// Has no intermediary sublevels, so the local option has no effect
console.log(foo.path()) // ['example', 'nested', 'foo']
console.log(foo.path(true)) // ['example', 'nested', 'foo']
```

### Hooks

**Hooks are experimental and subject to change without notice.**
Expand Down
1 change: 1 addition & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This document describes breaking changes and how to upgrade. For a complete list
- [1.2. Not found](#12-not-found)
- [1.3. Not ready](#13-not-ready)
- [1.4. Hooks](#14-hooks)
- [1.5. Open before creating a chained batch](#15-open-before-creating-a-chained-batch)
- [2. Private API](#2-private-api)
- [2.1. Promises all the way](#21-promises-all-the-way)
- [2.2. Ticks](#22-ticks)
Expand Down
8 changes: 8 additions & 0 deletions lib/abstract-sublevel.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const {

const kGlobalPrefix = Symbol('prefix')
const kLocalPrefix = Symbol('localPrefix')
const kLocalPath = Symbol('localPath')
const kGlobalPath = Symbol('globalPath')
const kGlobalUpperBound = Symbol('upperBound')
const kPrefixRange = Symbol('prefixRange')
const kRoot = Symbol('root')
Expand Down Expand Up @@ -61,6 +63,8 @@ module.exports = function ({ AbstractLevel }) {
// to change, until we add some form of preread or postread hooks.
this[kRoot] = root
this[kParent] = db
this[kLocalPath] = names
this[kGlobalPath] = db.prefix ? db.path().concat(names) : names
this[kGlobalPrefix] = new MultiFormat(globalPrefix)
this[kGlobalUpperBound] = new MultiFormat(globalUpperBound)
this[kLocalPrefix] = new MultiFormat(localPrefix)
Expand Down Expand Up @@ -120,6 +124,10 @@ module.exports = function ({ AbstractLevel }) {
return this[kParent]
}

path (local = false) {
return local ? this[kLocalPath] : this[kGlobalPath]
}

async _open (options) {
// The parent db must open itself or be (re)opened by the user because
// a sublevel should not initiate state changes on the rest of the db.
Expand Down
30 changes: 29 additions & 1 deletion test/self/sublevel-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,36 @@ test('sublevel prefix and options', function (t) {
t.test('empty prefix', function (t) {
const sub = new NoopLevel().sublevel('')
t.is(sub.prefix, '!!')
t.same(sub.path(), [''])
t.end()
})

t.test('prefix without options', function (t) {
const sub = new NoopLevel().sublevel('prefix')
t.is(sub.prefix, '!prefix!')
t.same(sub.path(), ['prefix'])
t.end()
})

t.test('prefix and separator option', function (t) {
const sub = new NoopLevel().sublevel('prefix', { separator: '%' })
t.is(sub.prefix, '%prefix%')
t.same(sub.path(), ['prefix'])
t.end()
})

t.test('array name', function (t) {
const sub = new NoopLevel().sublevel(['a', 'b'])
t.is(sub.prefix, '!a!!b!')
const alt = new NoopLevel().sublevel('a').sublevel('b')

t.is(sub.prefix, '!a!!b!')
t.same(sub.path(), ['a', 'b'])
t.same(sub.path(true), ['a', 'b'])

t.is(alt.prefix, sub.prefix)
t.same(alt.path(), ['a', 'b'])
t.same(alt.path(true), ['b'])

t.end()
})

Expand All @@ -109,46 +119,64 @@ test('sublevel prefix and options', function (t) {
t.test('array name with single element', function (t) {
const sub = new NoopLevel().sublevel(['a'])
t.is(sub.prefix, '!a!')
t.same(sub.path(), ['a'])

const alt = new NoopLevel().sublevel('a')
t.is(alt.prefix, sub.prefix)
t.same(sub.path(), alt.path())

t.end()
})

t.test('array name and separator option', function (t) {
const sub = new NoopLevel().sublevel(['a', 'b'], { separator: '%' })
t.is(sub.prefix, '%a%%b%')
t.same(sub.path(), ['a', 'b'])

const alt = new NoopLevel().sublevel('a', { separator: '%' }).sublevel('b', { separator: '%' })
t.is(alt.prefix, sub.prefix)
t.same(alt.path(), ['a', 'b'])

t.end()
})

t.test('separator is trimmed from prefix', function (t) {
const sub1 = new NoopLevel().sublevel('!prefix')
t.is(sub1.prefix, '!prefix!')
t.same(sub1.path(), ['prefix'])

const sub2 = new NoopLevel().sublevel('prefix!')
t.is(sub2.prefix, '!prefix!')
t.same(sub2.path(), ['prefix'])

const sub3 = new NoopLevel().sublevel('!!prefix!!')
t.is(sub3.prefix, '!prefix!')
t.same(sub3.path(), ['prefix'])

const sub4 = new NoopLevel().sublevel('@prefix@', { separator: '@' })
t.is(sub4.prefix, '@prefix@')
t.same(sub4.path(), ['prefix'])

const sub5 = new NoopLevel().sublevel(['!!!a', 'b!!!'])
t.is(sub5.prefix, '!a!!b!')
t.same(sub5.path(), ['a', 'b'])

const sub6 = new NoopLevel().sublevel(['a@@@', '@@@b'], { separator: '@' })
t.is(sub6.prefix, '@a@@b@')
t.same(sub6.path(), ['a', 'b'])

t.end()
})

t.test('repeated separator can not result in empty prefix', function (t) {
const sub1 = new NoopLevel().sublevel('!!!!')
t.is(sub1.prefix, '!!')
t.same(sub1.path(), [''])

const sub2 = new NoopLevel().sublevel(['!!!!', '!!!!'])
t.is(sub2.prefix, '!!!!')
t.same(sub2.path(), ['', ''])

t.end()
})

Expand Down
9 changes: 9 additions & 0 deletions types/abstract-sublevel.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ declare class AbstractSublevel<TDatabase, TFormat, KDefault, VDefault>
*/
get prefix (): string

/**
* Get the path of the sublevel, which is its prefix without separators.
*
* @param local If true, exclude path of parent database. If false (the default) then
* recurse to form a fully-qualified path that travels from the root database to this
* sublevel.
*/
path (local?: boolean | undefined): string[]

/**
* Parent database. A read-only property.
*/
Expand Down

0 comments on commit 429df4b

Please sign in to comment.