Skip to content

Commit 93731c2

Browse files
authored
docs: refresh jsonpath guide phrasing (#33)
1 parent 8a06ad7 commit 93731c2

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

pages/docs/advanced/_meta.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ export default {
44
version_deep_dive: "Loro's Versioning Deep Dive: DAG, Frontiers, and Version Vectors",
55
undo: "Undo/Redo",
66
import_batch: "Batch Import",
7-
inspector: "Loro Inspector"
7+
inspector: "Loro Inspector",
8+
jsonpath: "JSONPath Queries"
89
}

pages/docs/advanced/jsonpath.mdx

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
---
2+
keywords: "jsonpath, querying, loro, crdt, advanced"
3+
description: "Learn how to query Loro documents using RFC 9535 JSONPath support."
4+
---
5+
6+
# JSONPath Queries
7+
8+
[JSONPath](https://www.rfc-editor.org/rfc/rfc9535.html) queries integrate
9+
directly with Loro documents so you can traverse complex data without writing
10+
ad hoc tree walkers. This guide outlines the query surface, highlights
11+
supported syntax, and walks through examples that you can adapt to your own
12+
application.
13+
14+
> JSONPath functions such as `count()`, `length()`, `value()`, `match()`, and
15+
> `search()` are parsed but not evaluated at runtime. Each currently returns
16+
> `JsonPathError::EvaluationError::Unimplemented`.
17+
18+
## Preparing Sample Data
19+
20+
The snippets below use the WASM bindings, but the same structure works with the
21+
Rust API. We create a bookstore dataset inside a `LoroDoc` so we can run queries
22+
against it.
23+
24+
> ⚠️ **Illustration only**: This approach stuffs plain JavaScript objects into a
25+
> document for convenience. In production code prefer composing `LoroMap` and
26+
> `LoroList` instances explicitly so you can control how data enters the CRDT and
27+
> benefit from fine-grained updates.
28+
29+
```ts twoslash
30+
import { LoroDoc } from "loro-crdt";
31+
32+
const doc = new LoroDoc();
33+
const testData = {
34+
books: [
35+
{ title: "1984", author: "George Orwell", price: 10, available: true, isbn: "978-0451524935" },
36+
{ title: "Animal Farm", author: "George Orwell", price: 8, available: true },
37+
{ title: "Brave New World", author: "Aldous Huxley", price: 12, available: false },
38+
{ title: "Fahrenheit 451", author: "Ray Bradbury", price: 9, available: true, isbn: "978-1451673318" },
39+
{ title: "The Great Gatsby", author: "F. Scott Fitzgerald", price: null, available: true },
40+
{ title: "To Kill a Mockingbird", author: "Harper Lee", price: 11, available: true },
41+
{ title: "The Catcher in the Rye", author: "J.D. Salinger", price: 10, available: false },
42+
{ title: "Lord of the Flies", author: "William Golding", price: 9, available: true },
43+
{ title: "Pride and Prejudice", author: "Jane Austen", price: 7, available: true },
44+
{ title: "The Hobbit", author: "J.R.R. Tolkien", price: 14, available: true }
45+
],
46+
featured_author: "George Orwell",
47+
min_price: 10,
48+
featured_authors: ["George Orwell", "Jane Austen"],
49+
};
50+
51+
const store = doc.getMap("store");
52+
Object.entries(testData).forEach(([key, value]) => store.set(key, value));
53+
doc.commit();
54+
```
55+
56+
## Executing JSONPath Queries
57+
58+
Run a query with the `JSONPath` method on WASM or `jsonpath` on Rust. The API
59+
returns a list of values for any matches. Container results currently surface as
60+
handlers internally, but that interface is still evolving and treated as
61+
opaque in this guide.
62+
63+
```ts twoslash
64+
import { LoroDoc } from "loro-crdt";
65+
// ---cut---
66+
const doc = new LoroDoc();
67+
// setup omitted for brevity
68+
const results = doc.JSONPath("$.store.books[*].title");
69+
console.log(results);
70+
// => ["1984", "Animal Farm", ...]
71+
```
72+
73+
```rust
74+
use loro::prelude::*;
75+
76+
let doc = LoroDoc::new();
77+
// setup omitted for brevity
78+
let titles = doc.jsonpath("$.store.books[*].title")?;
79+
for title in titles {
80+
if let ValueOrHandler::Value(value) = title {
81+
println!("{value}");
82+
}
83+
}
84+
```
85+
86+
### Supported Selectors and Filters
87+
88+
RFC 9535 syntax works end-to-end, including:
89+
90+
- **Selectors** – names, indices (positive/negative), slices, unions, wildcards,
91+
and recursive descent (`..`).
92+
- **Filters** – logical operators (`&&`, `||`, `!`), comparisons, membership via
93+
`in`, substring checks with `contains`, property existence, and nested
94+
subqueries through `$` (root) or `@` (current node).
95+
- **Functions**`count()`, `length()`, and `value()` are parsed, while `match()`
96+
and `search()` are reserved for expansion. All functions currently return an
97+
unimplemented evaluation error at runtime.
98+
99+
## Cookbook Examples
100+
101+
Once the document is populated you can combine selectors and filters to extract
102+
precisely what you need.
103+
104+
| Query | Description | Result |
105+
| ----- | ----------- | ------ |
106+
| `$.store.books[*].title` | All book titles. | `["1984", "Animal Farm", "Brave New World", "Fahrenheit 451", "The Great Gatsby", "To Kill a Mockingbird", "The Catcher in the Rye", "Lord of the Flies", "Pride and Prejudice", "The Hobbit"]` |
107+
| `$.store.books[?(@.available)].title` | Titles of books in stock. | `["1984", "Animal Farm", "The Great Gatsby", "To Kill a Mockingbird", "Lord of the Flies", "Pride and Prejudice", "The Hobbit"]` |
108+
| `$.store.books[?(@.author in $.store.featured_authors)].title` | Books by featured authors. | `["1984", "Animal Farm", "Pride and Prejudice"]` |
109+
| `$.store.books[?(@.price > 12)].title` | Books priced above 12. | `["The Hobbit"]` |
110+
| `$..price` | All price fields via recursive descent. | `[10, 8, 12, 9, null, 11, 10, 9, 7, 14]` |
111+
| `$.store.books[0:3].title` | Slice syntax for the first three titles. | `["1984", "Animal Farm", "Brave New World"]` |
112+
| `$.store.books[0,2,-1].title` | Union of specific indices. | `["1984", "Brave New World", "The Hobbit"]` |
113+
| `count($.store.books[?(@.available)])` | Planned helper to count available books *(currently returns an unimplemented error)*. | `JsonPathError::EvaluationError::Unimplemented` |
114+
| `length($.store.featured_authors)` | Planned array length helper *(currently returns an unimplemented error)*. | `JsonPathError::EvaluationError::Unimplemented` |
115+
| `$.store.books[?(@.isbn && @.price >= $.store.min_price)].title` | Filter by field existence and comparison. | `["1984"]` |
116+
| `$.store.books[?(!@.available)].title` | Negated availability filter. | `["Brave New World", "The Catcher in the Rye"]` |
117+
| `$.store.books[?(@.title contains "The")].author` | Authors with "The" in the title. | `["F. Scott Fitzgerald", "J.R.R. Tolkien"]` |
118+
119+
> JSONPath always returns values in document order. When a filter references
120+
> another query (such as `$.store.featured_authors`), the subquery is evaluated
121+
> for each candidate element.

0 commit comments

Comments
 (0)