Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,11 @@ The CLI formats are edge formats:
- `frame`: binary-safe key/value records for lossless export and restore.

NDJSON key fields must be strings. Compound key parts may not contain
the one-byte `--key-sep`, which defaults to `:`. Input records and values are
limited to 64 MiB.
the one-byte `--key-sep`, which defaults to `:`. Text input records, raw values,
and each key/value in `frame` or `kcat` records are limited to 64 MiB.

Scans are ordered by raw key bytes. `range` is half-open:
Scans are ordered by raw key bytes, ascending by default. `--reverse` reads
from the high end of the selected index. Range bounds are half-open:

```text
start <= key < end
Expand All @@ -121,17 +122,15 @@ pbl init
pbl put <collection> <key> <value>
pbl get <collection> <key>
pbl del <collection> <key>
pbl drop <collection>
```

Ordered reads:

```text
pbl scan <collection>
pbl prefix <collection> <prefix>
pbl range <collection> <start> <end>
pbl keys <collection>
pbl values <collection>
pbl export <collection> [--format frame]
pbl scan <collection> [--prefix <prefix>] [--start <start>] [--end <end>]
[--reverse] [--limit <n>] [--format kv|ndjson|raw|frame]
pbl count <collection> [--prefix <prefix>] [--start <start>] [--end <end>]
```

Streaming workflows:
Expand All @@ -141,7 +140,6 @@ pbl import <collection> --format kv|line|ndjson|raw
pbl get-many <collection>
pbl del-many <collection>
pbl exists <collection>
pbl lookup <collection>
pbl join <collection> --on <field> --as <field>
pbl apply <collection> --format kcat|frame
```
Expand Down
25 changes: 12 additions & 13 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ A non-empty Pebble database without pbl format metadata is rejected.

## Durability

Single-key writes sync by default. Bulk commands (`import`, `apply`, and
`del-many`) batch records and do not sync each batch unless `--sync` is set.
Single-key writes and collection drops sync by default. Bulk commands (`import`,
`apply`, and `del-many`) batch records and do not sync each batch unless `--sync`
is set.
Use `--no-sync` on single-key writes when throughput matters more than crash
durability.

Expand All @@ -67,10 +68,11 @@ key<TAB>value
`--key-field` flags build a compound key joined with the one-byte `--key-sep`,
which defaults to `:`. Compound key parts may not contain that separator.
Point operations, imports, apply streams, and stream lookups reject empty user
keys. NDJSON output is normalized as needed to one JSON value per line. Input
records and values are limited to 64 MiB. When JSON is wrapped with a key or
attached by lookup/join, numeric literals retain their precision. Output may
compact whitespace and escape characters; object member order is not a contract.
keys. NDJSON output is normalized as needed to one JSON value per line. Text
input records, raw values, and each key/value in frame or kcat records are limited
to 64 MiB. When JSON is wrapped with a key or attached by join, numeric literals
retain their precision. Output may compact whitespace and escape characters;
object member order is not a contract.

`frame` output is a binary-safe sequence accepted by `apply --format frame`.
Use it when an export must preserve arbitrary key and value bytes.
Expand All @@ -87,19 +89,17 @@ pbl init
pbl put <collection> <key> <value>
pbl get <collection> <key>
pbl del <collection> <key>
pbl drop <collection>
```

See [commands/core.md](commands/core.md).

Ordered reads:

```text
pbl scan <collection>
pbl prefix <collection> <prefix>
pbl range <collection> <start> <end>
pbl keys <collection>
pbl values <collection>
pbl export <collection>
pbl scan <collection> [--prefix <prefix>] [--start <start>] [--end <end>]
[--reverse] [--limit <n>] [--format kv|ndjson|raw|frame]
pbl count <collection> [--prefix <prefix>] [--start <start>] [--end <end>]
```

See [commands/ordered-reads.md](commands/ordered-reads.md).
Expand All @@ -120,7 +120,6 @@ Stream commands:
pbl get-many <collection>
pbl del-many <collection>
pbl exists <collection>
pbl lookup <collection>
pbl join <collection> --on <field> --as <field>
```

Expand Down
18 changes: 9 additions & 9 deletions docs/commands/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

This directory holds the expanded command reference for `pbl`.

- [Core commands](core.md): `init`, `put`, `get`, `del`
- [Ordered reads](ordered-reads.md): `scan`, `prefix`, `range`, `keys`,
`values`, `export`
- [Core commands](core.md): `init`, `put`, `get`, `del`, `drop`
- [Ordered reads](ordered-reads.md): `scan`, `count`
- [Import and apply](import-apply.md): `import`, `apply`
- [Stream commands](streams.md): `get-many`, `del-many`, `exists`, `lookup`,
`join`
- [Stream commands](streams.md): `get-many`, `del-many`, `exists`, `join`
- [Metadata commands](metadata.md): `collections`, `info`, `stats`

For common workflows, see [../usage.md](../usage.md). For the compact CLI
Expand All @@ -21,17 +19,19 @@ contract, global flags, formats, and exit codes, see [../cli.md](../cli.md).
- Stream commands preserve input order.
- One Pebble directory stores every logical collection.
- `--db` overrides `PBL_DB`; if neither is set, `.pbl` is used.
- Single-key writes sync by default. Bulk commands (`import`, `apply`, and
`del-many`) do not sync each batch unless `--sync` is set.
- Single-key writes and collection drops sync by default. Bulk commands
(`import`, `apply`, and `del-many`) do not sync each batch unless `--sync` is set.
- `--limit 0` means no limit.
- Input records and values are limited to 64 MiB.
- Text input records, raw values, and each binary-format key/value are limited
to 64 MiB.
- User keys passed to point and stream operations must be non-empty.
- Bulk commands commit incrementally; an error can leave earlier batches stored.

## Formats

`raw` is a value without a key wrapper. `get` adds a newline unless
`--no-newline` is set. `scan --format raw` requires `--values-only`.
`--no-newline` is set. `scan --format raw` concatenates values without added
newlines.

`line` is one input record per line.

Expand Down
21 changes: 19 additions & 2 deletions docs/commands/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ Reads one value. Default output is raw value bytes plus a newline.
Flags:

- `--format`: choose raw value, `key<TAB>value`, or NDJSON output.
- `--with-key`: wrap NDJSON output with `_key` and `_value`; KV output always
includes the key.
- `--with-key`: wrap NDJSON output with `_key` and `_value`; requires NDJSON
format. KV output always includes the key.
- `--missing`: choose whether missing keys exit 2, emit nothing, or emit null.
- `--no-newline`: suppress the added newline for raw output.

Expand All @@ -79,3 +79,20 @@ Flags:

Behind the scenes: `--fail-missing` checks existence before deleting. Without it,
Pebble deletion is used directly.

## drop

```text
pbl drop <collection> [--sync|--no-sync]
```

Deletes every record and the collection metadata. Success writes no stdout.
An absent collection is success, so repeated drops are idempotent. The database
must exist. A later put, import, or apply can recreate the collection.

The operation syncs by default. `--no-sync` skips fsync.

Behind the scenes: a single Pebble batch deletes the collection key range and
its metadata atomically. No per-key scan or delete list is needed. Other
collections and database metadata are preserved. Pebble compaction reclaims disk
space later; drop does not force a compaction.
7 changes: 4 additions & 3 deletions docs/commands/import-apply.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ pbl import <collection> --format kv|line|ndjson|raw
[--key-sep <sep>]
[--batch-size <n>]
[--batch-bytes <size>]
[--replace|--ignore-duplicates|--fail-on-duplicate]
[--ignore-duplicates|--fail-on-duplicate]
[--sync|--no-sync]
```

Imports records from stdin.
Imports records from stdin. Existing values are replaced by default.

Flags:

Expand All @@ -28,7 +28,6 @@ Flags:
- `--batch-size`: maximum records per write batch.
- `--batch-bytes`: approximate bytes per write batch, accepting plain numbers,
`K`, `KB`, `M`, or `MB`.
- `--replace`: replace existing values; this is the default.
- `--ignore-duplicates`: keep the first existing or input value for each key.
- `--fail-on-duplicate`: exit 4 on existing or repeated input keys.
- `--sync`: fsync every committed batch.
Expand All @@ -51,6 +50,8 @@ pbl apply <collection> --format kcat|frame
```

Applies an ordered stream of puts and deletes. Success writes no stdout.
Keys and values are each limited to 64 MiB, independently, so a maximum-sized
raw value can be exported and restored with its key.

Flags:

Expand Down
116 changes: 47 additions & 69 deletions docs/commands/ordered-reads.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,94 +4,72 @@

```text
pbl scan <collection>
[--prefix <prefix>] [--start <start>] [--end <end>]
[--reverse] [--limit <n>]
[--format kv|ndjson|raw|frame]
[--limit <n>]
[--keys-only|--values-only]
[--include-key]
[--keys-only|--values-only] [--with-key]
```

Emits all records in raw key-byte order. Default output is `key<TAB>value`.
Emits records in raw key-byte order, ascending by default.

Flags:
Selection flags compose by intersection:

- `--format`: choose `kv`, `ndjson`, raw values, or binary-safe `frame` records.
- `--limit`: maximum records to emit; `0` means no limit.
- `--keys-only`: emit only keys.
- `--values-only`: emit only values.
- `--include-key`: include `_key` beside `_value` in NDJSON output.
- `--prefix`: include keys beginning with these bytes; an empty prefix matches all.
- `--start`: inclusive lower bound, compared against the complete key.
- `--end`: exclusive upper bound, compared against the complete key.
- Omit either bound to leave that side open. `--end ''` selects no keys.
- Equal bounds or a disjoint prefix/range produce no records. A start greater
than the end is a usage error.

`frame` emits binary-safe put records containing both key and value. It cannot
be combined with output-shaping flags. NDJSON output is validated and normalized
as needed so each value occupies one line. Wrapping a value with `--include-key`
preserves numeric literals, including large integers; nested objects are not
reordered into a canonical representation.
`--reverse` emits descending keys within the same selection. `--limit` caps the
number of emitted records in the chosen direction; `0` means no limit. An absent
collection emits nothing; the database must exist.

Behind the scenes: pbl scans only the selected collection keyspace inside the
shared Pebble directory.
Output choices:

## prefix
- Default `kv`: `key<TAB>value` followed by a newline.
- `--keys-only` or `--values-only`: one key or value per line, using `kv` format.
- `--format ndjson`: validate stored JSON and emit one JSON value per line.
- `--format ndjson --with-key`: wrap each value as `{"_key":...,"_value":...}`.
Numeric literals retain their precision; object member order is not a contract.
- `--format raw`: concatenate value bytes without separators or added newlines.
- `--format frame`: binary-safe put records containing both key and value.

```text
pbl prefix <collection> <prefix> [scan flags]
```

Emits records whose keys start with `<prefix>`, in raw key-byte order.

Behind the scenes: pbl turns the prefix into a bounded Pebble iterator range.

## range

```text
pbl range <collection> <start> <end> [scan flags]
```

Emits a half-open range:
`--keys-only` and `--values-only` cannot be combined with another format.
`--with-key` requires NDJSON. Use frame format for arbitrary bytes; line and KV
output do not escape embedded tabs or newlines.

```text
start <= key < end
```

Behind the scenes: this is an ordered scan over collection data keys from start
inclusive to end exclusive.
Behind the scenes: selection becomes one bounded Pebble iterator. Reverse scans
start at its high bound and limits stop iteration early. Key-only scans do not
fetch values.

## keys
## count

```text
pbl keys <collection>
[--prefix <p>]
[--range-start <s> --range-end <e>]
[--limit <n>]
pbl count <collection> [--prefix <prefix>] [--start <start>] [--end <end>]
```

Emits keys, one per line. Without filters, it scans the full collection. Range
flags must be used together.
Prints the exact number of matching live keys and a newline. Uses the same
selection rules as `scan`. An empty or absent collection prints `0`; the database
must exist. Embedded newlines in keys do not affect the count.

Behind the scenes: `keys` uses the same scan, prefix, and range paths as
`scan`, then prints only keys.
Behind the scenes: count visits matching keys without fetching values. It uses
bounded memory and time proportional to the matching key scan; counts are not
stored or estimated.

## values
## Examples

```text
pbl values <collection>
[--prefix <p>]
[--range-start <s> --range-end <e>]
[--limit <n>]
```
```sh
# The latest ten events for one user, assuming sortable timestamps in the keys.
pbl scan events --prefix 'u123:' --reverse --limit 10 --format ndjson

Emits values, one per line, in key order. Range flags must be used together.
# Events for one user from January onward.
pbl scan events --prefix 'u123:' --start 'u123:2026-01-01'

Behind the scenes: `values` uses the same scan, prefix, and range paths as
`scan`, then prints only values.
# Count events before February.
pbl count events --prefix 'u123:' --end 'u123:2026-02-01'

## export

```text
pbl export <collection> [scan flags]
pbl export <collection> --format frame
# Lossless export and restore, including binary keys and values.
pbl scan artifacts --format frame > artifacts.frame
pbl apply restored --format frame < artifacts.frame
```

Exports records with the same flags and behavior as `scan`.

Behind the scenes: `export` is the `scan` path with a clearer name for backup or
pipeline use. Frame output is lossless for arbitrary key and value bytes and is
accepted by `apply --format frame`.
Loading
Loading