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(type="issue").order_by("weight")          # ascending, stable
doc.query().where(type="issue").order_by("weight").reverse() # descending
doc.query().where(type="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).

| terminal | returns | |---|---| | 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-likelen(q), q[0], and iteration all work.

n_open = doc.query().where(type="issue", status="open").count()
by_status = doc.query().where(type="issue").group_by("status")     # {"open": [...], "done": [...]}
first_bug = doc.query().where(type="issue", kind="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.