DocsAdvanced TopicsJSONPath Queries

JSONPath Queries

JSONPath queries integrate directly with Loro documents so you can traverse complex data without writing ad hoc tree walkers. This guide outlines the query surface, highlights supported syntax, and walks through examples that you can adapt to your own application.

JSONPath functions such as count(), length(), value(), match(), and search() are parsed but not evaluated at runtime. Each currently returns JsonPathError::EvaluationError::Unimplemented.

Preparing Sample Data

The snippets below use the WASM bindings, but the same structure works with the Rust API. We create a bookstore dataset inside a LoroDoc so we can run queries against it.

⚠️ Illustration only: This approach stuffs plain JavaScript objects into a document for convenience. In production code prefer composing LoroMap and LoroList instances explicitly so you can control how data enters the CRDT and benefit from fine-grained updates.

import {  } from "loro-crdt";
 
const  = new ();
const  = {
  : [
    { : "1984", : "George Orwell", : 10, : true, : "978-0451524935" },
    { : "Animal Farm", : "George Orwell", : 8, : true },
    { : "Brave New World", : "Aldous Huxley", : 12, : false },
    { : "Fahrenheit 451", : "Ray Bradbury", : 9, : true, : "978-1451673318" },
    { : "The Great Gatsby", : "F. Scott Fitzgerald", : null, : true },
    { : "To Kill a Mockingbird", : "Harper Lee", : 11, : true },
    { : "The Catcher in the Rye", : "J.D. Salinger", : 10, : false },
    { : "Lord of the Flies", : "William Golding", : 9, : true },
    { : "Pride and Prejudice", : "Jane Austen", : 7, : true },
    { : "The Hobbit", : "J.R.R. Tolkien", : 14, : true }
  ],
  : "George Orwell",
  : 10,
  : ["George Orwell", "Jane Austen"],
};
 
const  = .("store");
.().(([, ]) => .(, ));
.();

Executing JSONPath Queries

Run a query with the JSONPath method on WASM or jsonpath on Rust. The API returns a list of values for any matches. Container results currently surface as handlers internally, but that interface is still evolving and treated as opaque in this guide.

const  = new ();
// setup omitted for brevity
const  = .("$.store.books[*].title");
.();
// => ["1984", "Animal Farm", ...]
use loro::prelude::*;
 
let doc = LoroDoc::new();
// setup omitted for brevity
let titles = doc.jsonpath("$.store.books[*].title")?;
for title in titles {
    if let ValueOrHandler::Value(value) = title {
        println!("{value}");
    }
}

Supported Selectors and Filters

RFC 9535 syntax works end-to-end, including:

  • Selectors – names, indices (positive/negative), slices, unions, wildcards, and recursive descent (..).
  • Filters – logical operators (&&, ||, !), comparisons, membership via in, substring checks with contains, property existence, and nested subqueries through $ (root) or @ (current node).
  • Functionscount(), length(), and value() are parsed, while match() and search() are reserved for expansion. All functions currently return an unimplemented evaluation error at runtime.

Cookbook Examples

Once the document is populated you can combine selectors and filters to extract precisely what you need.

QueryDescriptionResult
$.store.books[*].titleAll 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"]
$.store.books[?(@.available)].titleTitles of books in stock.["1984", "Animal Farm", "The Great Gatsby", "To Kill a Mockingbird", "Lord of the Flies", "Pride and Prejudice", "The Hobbit"]
$.store.books[?(@.author in $.store.featured_authors)].titleBooks by featured authors.["1984", "Animal Farm", "Pride and Prejudice"]
$.store.books[?(@.price > 12)].titleBooks priced above 12.["The Hobbit"]
$..priceAll price fields via recursive descent.[10, 8, 12, 9, null, 11, 10, 9, 7, 14]
$.store.books[0:3].titleSlice syntax for the first three titles.["1984", "Animal Farm", "Brave New World"]
$.store.books[0,2,-1].titleUnion of specific indices.["1984", "Brave New World", "The Hobbit"]
count($.store.books[?(@.available)])Planned helper to count available books (currently returns an unimplemented error).JsonPathError::EvaluationError::Unimplemented
length($.store.featured_authors)Planned array length helper (currently returns an unimplemented error).JsonPathError::EvaluationError::Unimplemented
$.store.books[?(@.isbn && @.price >= $.store.min_price)].titleFilter by field existence and comparison.["1984"]
$.store.books[?([email protected])].titleNegated availability filter.["Brave New World", "The Catcher in the Rye"]
$.store.books[?(@.title contains "The")].authorAuthors with “The” in the title.["F. Scott Fitzgerald", "J.R.R. Tolkien"]

JSONPath always returns values in document order. When a filter references another query (such as $.store.featured_authors), the subquery is evaluated for each candidate element.