Class reference

Query

Querying a document

query() turns a Document into something you interrogate. It returns a lazy, composable Query: you chain filters and ordering, then end with a terminal that walks the document. Nothing is evaluated until the terminal runs, and filter-only pipelines stop early. It is read-only, a query never mutates the document.

auto open_highs = doc.query("/tracker/*")
    .where(byType("issue") && field("status").eq("open") && field("sev").eq("high"))
    .orderBy("area");
for (const Element* e : open_highs.toList()) std::puts(e->path().str().c_str());

query() is additive: find() still returns its eager list. Pass a path glob to scope the source, or no argument for the whole document. A Query borrows its document, do not keep one past the document's life, and do not mutate the document while iterating. (In C++, query() on a temporary document is a compile error.)

where: filtering

where narrows the result. Chain it more than once; the conditions combine with AND.

Python accepts three forms, freely mixed:

doc.query().where(type="issue", status="open")          # keyword sugar: type token + property equality
doc.query().where(kg.field("sev") == "high")            # a field expression
doc.query().where(kg.field("weight") > 3)               # comparisons
doc.query().where(lambda h: h.get_int("weight") > 3)    # an escape-hatch lambda, given a Handle

C++ uses predicate builders composed with &&, ||, !:

doc.query().where(byType("issue") && field("status").eq("open"));
doc.query().where(field("sev").eq("high") || field("sev").eq("med"));
doc.query().where([](const Element& e){ return e.type() == "issue"; });   // escape hatch

Field predicates

field("name") builds a predicate from a property. Methods are canonical in both languages; Python also allows the operators.

reads
eq(v) / ==equal
ne(v) / !=present and unequal
lt le gt ge / < <= > >=ordered comparison
in_([...]) (Python) / in({...}) (C++)equal to any
contains(s) · startswith(s)substring / prefix (string properties)
exists()the property is present

Other builders: by_type(t) / byType(t) (the element's type token), under(path) (strict descendants), name_is(name) / nameIs, meta(key).eq(v), has_meta(key) / hasMeta.

Comparison semantics (the rules worth knowing)

Ordering, limiting

doc.query().where(byType("issue")).orderBy("weight");            // ascending, stable
doc.query().where(byType("issue")).orderBy("weight").reverse();  // descending
doc.query().where(byType("issue")).limit(10);

order_by(field) is ascending and stable; chain reverse() for descending. Elements whose key is missing or incomparable sort last, keeping document order among themselves. limit(n) takes the first n.

Terminals

A terminal evaluates the pipeline against the document as of that call (a Query is a recipe, not a frozen snapshot, call to_list() to freeze).

terminalreturns
iterate / to_list()the matched elements (Handles in Python, const Element* in C++)
paths()their paths
first()the first match, or None/nullptr
one()exactly one match, else raises (PrismError / std::invalid_argument)
count()how many matched
any()whether any matched (short-circuits)
group_by(field)a dict/map of string key → matched elements (string properties or the type token)

In Python a Query is list-like, len(q), q[0], and iteration all work.

auto n_open = doc.query().where(byType("issue") && field("status").eq("open")).count();
auto by_status = doc.query().where(byType("issue")).groupBy("status");   // {"open": [...], "done": [...]}
const Element* first_bug = doc.query().where(byType("issue") && field("kind").eq("bug")).first();

Laziness

Building a query scans nothing. A filter-only pipeline runs in a single pass and short-circuits, first() and any() stop at the first match. A buffering stage (order_by, reverse) consumes the whole result before the terminal. Re-running a terminal re-walks the document, so two terminals on one query can differ if the document changed between them; to_list() freezes a snapshot.

The query is implemented natively in kinogaki-core (C++) and mirrored in the Python package; both are exercised by one shared fixture so they stay in lockstep.