Querying Objects#
o get and o list take a kpath — the same path syntax the rest of the system
uses: what !at(...) walks in a match, what logd indexes by, what a
watch names, and what an error prints when it says where it is.
o get spec.replicas deploy.tony
o list 'items[*].name' inventory.tony
o get 'items(WIDGET).qty' inventory.tony
A path is a sequence of segments, and each segment says what KIND of thing it steps through — which is what "kinded path" means:
| segment | steps into | example |
|---|---|---|
.field |
an object | spec.replicas, .replicas |
[i] |
a dense array | items[0] |
{i} |
a sparse array, by key | items{42} |
(key) |
a keyed array, by identity | items(WIDGET) |
.* [*] {*} (*) |
all of them, at that step | items[*].name |
.. |
any depth below here, this node included | ..name, spec..image |
A segment names only children of its kind. .* names an object's fields and not the
entries of a sparse array, which {*} names, and {*} names nothing in a dense array,
whose elements [*] names. That is what makes a path an address: every node has one
spelling, so a document can be rebuilt from its paths and its leaves, and the path a
query answers with is the path a write is rooted at.
The leading . is optional at the start, so spec.replicas and .spec.replicas are
the same path, and by the same rule a bare . is the whole document. It is optional
only there: a field after an element or a key still takes its dot, so items[0].name
is a path and items[0]name is not. The root is
written ., or as the empty path — o get . doc.tony and o get '' doc.tony ask
the same thing. Giving no path at all is a usage error rather than the root: a
missing path and a path naming everything are different mistakes. A key or a field that needs quoting takes quotes: pr."1".votes. A * at the start
of a segment is always the wildcard, so a field whose name begins with one is written
quoted — "*" is the field called *, * is every field there is.
A sparse array is an object whose keys are numbers, so {i} names the value under
the key i rather than the ith value: in !sparsearray {3: a, 7: b}, {7} is b,
which sits second.
get and list#
get answers with the single node a path names, and list with every node it names.
A wildcard therefore belongs in a list: get refuses [*] or .. rather than
picking one of the things they matched.
o list ..image deploy.tony # every image, wherever it is
o list 'spec..name' deploy.tony # every name under spec, at any depth
o list 'a..' doc.tony # a and everything under it
A list answers each node once, in document order — a node before what is under it
— whichever way the path reached it. a..b..c can reach one c two ways, through
a.b and through a.b.b; the answer has it once. That is the set logd's match
answers for the same path over the store, in the same order.
-paths answers with where each node is rather than what it is, in this same
syntax — so the answer to one query is the input to the next:
$ o list -paths ..image deploy.tony
- spec.containers[0].image
- spec.containers[1].image
$ o get 'spec.containers[0].image' deploy.tony
nginx
Both read a stream of documents and ask the path of each. See What a write must be for the paths logd accepts on the writing side, which are the same syntax.
A note on $#
An earlier syntax, closer to JSONPath, began every path with $ — $.field[3]. That
sigil said nothing, since every path had it, and the syntax could not name a keyed or
a sparse element at all.
A leading $ is still accepted, so $.spec.replicas works and existing scripts keep
running. It is dropped rather than treated as a field name, which is what a kpath
would otherwise make of it.
That syntax spelled any-depth with three dots -- $...x -- because $..x was a
parse error. kpath spells it .., and the three-dot form is read as it.
Where .. may not go#
A .. is a question: it names the nodes at any depth rather than a step to one. So
it belongs in a query and nowhere a path has to name a place -- what a patch is
rooted at, what a watch names, what a retain rule expires, what logd indexes by. Those
refuse it, and say so:
"a..c": `..` names nodes at any depth, which is a question and not a place:
a path here has to name one
A read answers it: logd's match takes a .. and answers the set it names, each node
once, in document order, one node at a time --
the same set, in the same order, that o list answers over the document. Both take a
depth that bounds how many segments the path's descents may take between them: a.. at
depth 1 is a and its children, of every kind, wherever a is.
An empty field name is still sayable, in quotes: a."".x. That is the canonical
spelling, and what .. used to parse as before it meant depth.
A wildcard — .*, [*], {*}, (*) — is a question of the same family, asked one
level deep rather than at any depth, and the same rule sorts where it goes: a read
answers the set it names at any segment, one node at a time,
and a write, a watch and a retain rule's non-final segments refuse
it, because each needs one place.