CatalogPath

A location shape inside a document: the same list of steps as a app.oreshkov.rabosh.variant.VariantPath, except that array indices are collapsed into a single CatalogStep.AnyElement.

{"items":[{"sku":"a"},{"sku":"b"}]}

$ object
$.items array
$.items[*] object, twice
$.items[*].sku string, twice

Why this is not VariantPath. That type's contract is that it names exactly one location, which is what makes it usable as an index key and what lets Variant.select return one value. Adding a wildcard to it would break that guarantee for every existing caller. So the catalog gets its own type, and the relationship is one-way: every VariantPath has a CatalogPath shape, and a CatalogPath describes a set of VariantPaths.

That set is not abstract. forEachNodeIn enumerates it against a document — which is the only way it can be enumerated, since [*] stands for as many locations as that document has elements. The direction stays one-way: there is no CatalogPath.toVariantPath, and there cannot be.

And one step the data cannot produce. CatalogStep.AnyDescendant, spelled .., is a pattern rather than a collapse: $..["@type"] names that field wherever it sits, at any depth, which is the only sound way to ask about a corpus whose nesting is the content designer's rather than the schema's. Enumerating the shapes instead was measured and refused — on one 46 MB corpus, 72% of tagged elements belong to a type occupying more than one shape, one type occupies 49, and a shape missing from the list is a document missing from a result with nothing to report it. A sketch never emits this step, an index and a predicate leaf may carry it, and a projection still may not.

Why the indices are collapsed at all. A ten-thousand-element array would otherwise produce ten thousand paths, exhaust the path budget on its own, and push everything genuinely worth modelling into the overflow bucket. Collapsing also produces the path an inverted index over array elements actually wants — $.tags[*] is the thing somebody queries, $.tags[7] is not. ClickHouse's JSON type and the Variant shredding specification both make the same choice.

Paths are ordered so that a report is stable between runs: field before element at the same position, then by field name, then shorter first.

PATHS.md compares this grammar with the other three, which is where to look before spelling one expression for a filter and an extraction at once: [*] means array elements here and every child in RFC 9535, and a backslash is literal here and an escape there. Neither divergence has a diagnostic, and both are what toJsonPath and Companion.parseJsonPath exist to cross.

Constructors

Link copied to clipboard
constructor(steps: List<CatalogStep>)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
val depth: Int

How many steps from the root.

Link copied to clipboard

true for the path that selects the document itself.

Link copied to clipboard

Functions

Link copied to clipboard
open operator override fun compareTo(other: CatalogPath): Int
Link copied to clipboard
open operator override fun equals(other: Any?): Boolean
Link copied to clipboard
fun CatalogPath.forEachNodeIn(document: Variant, sink: (VariantNode) -> Unit)

Every node this path stands for in document, in document order.

Link copied to clipboard
open override fun hashCode(): Int
Link copied to clipboard

The same, materialised.

Link copied to clipboard
operator fun plus(step: CatalogStep): CatalogPath

This path extended by step.

Link copied to clipboard

Whether this path is other or lies underneath it.

Link copied to clipboard

This shape as an RFC 9535 query: $['items'][:]['sku'].

Link copied to clipboard
open override fun toString(): String

The canonical expression for this path; parse round-trips it, for every step list this type admits.