Ray's Knowledge Base

A new query leaf form must be handled in every place that reads query leaves

PitfallVerified 27 Sep 2026Holds project: dftracer-utils
Pitfall. The symptom, what causes it, and the fix that was run and seen to work.

Symptom#

After adding any(path) (array membership, stage 7c), the View returned the right rows, but the reader and the Arrow reader returned 0 rows for the same query. Other leaf consumers could misread the new leaf as a plain path.

Cause#

The query AST is read in many places, and each has its own code for a leaf:

  • the two evaluators (the JSON-DOM evaluator and the other evaluator);
  • the reader's ondemand value maps (store_positions, store_referenced) in the trace reader and in the Arrow reader;
  • the raw pre-filter (ranges_of, needles);
  • the chunk pruner (ChunkPruner, with a per-file rewrite of any() to an OR over catalog positions);
  • subsumption, the mask, the aggregation tier and the Conditions (is_leaf).

The reader paths do not go through the View's evaluator, so a leaf form that only the evaluators know matches nothing there.

Fix#

When you add or change a leaf form (a new FieldNode flag, operator or node type), grep for every consumer of FieldNode/CompareNode/field.path and decide for each one: support it, or decline it (treat the leaf as residual, no pruning). For any(): the evaluators, the reader value maps and the pruner support it; subsumption, the mask, the aggregation tier and the Conditions decline it; the pre-filter skips it.

Test the new form through each read path: View collect, sink_json, the reader, the Arrow reader, and with the index on and off.

Evidence#

  • Stage 7c (array-membership, commit 9668d017 feat(query): filter array elements with any(path)): the reader and Arrow reader returned 0 rows until their value maps handled any(); tests test_any_view.cpp and the parser, evaluator, builder, subsumption, fuzz and Python tests cover the paths.