extract

fun extract(document: Variant, sink: (pathIndex: Int, value: Variant) -> Unit)

Reports every (pathIndex, value) this document contributes.

A path may be reported more than once for one document — $.tags[*] over three tags reports three values — and a duplicate reports the same value twice. Both are correct: an inverted index's posting list is a set of ordinals, and a column stores one slot per occurrence.

The raw Variant is reported, not a term, so that one walk serves both index kinds. An inverted index turns it into a ValueSignature; a column turns it into a ColumnValue. This is what makes "the recheck runs the same walk that built the index" true of both — a second, differently-shaped traversal would be a second definition of what a path means, and the two would eventually disagree about an array or a nested null.

A JSON null is reported like any other value. It is present, which is what makes EXISTS exact: a document with {"note": null} has a note.

The value is a view over bytes valid only for the duration of the call; anything kept must be copied.


fun extract(document: Variant, onTruncated: (pathIndex: Int) -> Unit, sink: (pathIndex: Int, value: Variant) -> Unit)

The same walk, reporting every path a budget stopped it short of.

onTruncated fires with a pathIndex when IndexOptions.maxChildren or IndexOptions.maxDepth cut a container this path was still a candidate under — the case where the values reported for it are a prefix of the values the document holds there. Nothing else distinguishes that from a complete walk: the sink reports what was found and cannot report what was never looked at.

The one caller that must use this overload is the one writing an index. A dictionary built from a prefix of a path's values, in a segment that then reads as covered, is an index that deletes documents from a result — and because the recheck and the scan would truncate at the same element, both differential oracles would agree with the shortfall. So IndexCatalog marks the segment not covered for that index rather than writing the sidecar, which is the escape IndexOptions.maxTermsPerSegment already takes and for the same reason. A reader's walk has no budget to fire — see reading — so nothing on the query path has to ask.

Conservative in the direction that costs a scan rather than a document. A truncated object reports every candidate still alive at it, because deciding which of them a skipped field would have matched means reading the names the bound exists to avoid reading; a truncated array reports only the candidates the wildcard step kept, which is exact.

A path may be reported more than once for one document, and once per document that truncated it. The caller wants a set; this reports events.