Skip to content

fix(index): sort JSON-path values once after extraction, not the raw column#7835

Draft
wjones127 wants to merge 1 commit into
lance-format:mainfrom
wjones127:fix/json-index-value-sort-7485
Draft

fix(index): sort JSON-path values once after extraction, not the raw column#7835
wjones127 wants to merge 1 commit into
lance-format:mainfrom
wjones127:fix/json-index-value-sort-7485

Conversation

@wjones127

Copy link
Copy Markdown
Contributor

Problem

A JSON-path scalar index (target_index_type=btree) returns wrong results when the
indexed path holds float64 values that are not exactly representable (e.g. 40.1,
-3.2): ranges come back empty and equality misses rows.

Fixes #7485.

Root cause

The btree trainer requires its input sorted by value (page min/max are taken from the
first/last row of each page). scan_training_data sorts by the raw JSON column when
the target requests TrainingOrdering::Values, but JsonIndexPlugin extracts a
different value (the value at path) from that column downstream, so the extracted
stream is not actually sorted by value. Silently corrupted page stats then cause range
and equality lookups to skip pages that hold matching rows.

Relationship to #7493, #7771, #7819

All three open PRs diagnose this correctly and fix it by adding a second SortExec
after JSON extraction, on top of the raw-column sort the scanner already performs. That
raw-column sort is wasted work — it sorts a key nothing downstream uses.

This PR instead has JsonTrainingRequest::criteria() override the target's
TrainingOrdering::Values to TrainingOrdering::None when delegating to the scanner
(the scanner's sort can't help the extracted value anyway), and sorts the extracted
(value, row_id) stream once, in train_index, right before handing it to the target
trainer. Same correctness fix, one sort instead of two.

Testing

  • test_json_float_btree_index_unsorted_input (rust/lance-index/src/scalar/json.rs):
    builds a real JSON/btree index through the plugin trainer/registry with the issue's
    repro values fed in raw storage order, and asserts range/equality results across 5
    cases. Also asserts JsonTrainingRequest::criteria().ordering == TrainingOrdering::None.
  • test_json_index_non_exact_floats (python/python/tests/test_scalar_index.py): the
    issue's repro end to end via create_scalar_index + json_get_float filters.

cargo fmt --all, cargo clippy -p lance-index --tests -- -D warnings, and
uv run make lint are clean.

…column

A JSON-path scalar index trains its target (e.g. btree) on the value
extracted from the JSON column at `path`, not on the raw JSON column
itself. The scanner's upstream sort only orders by the raw column, so
for targets that require value-ordered input (btree, whose per-page
min/max come from the first/last row), the extracted stream was not
actually sorted by value. This silently corrupted page min/max stats,
so range and equality queries missed rows -- particularly floats not
exactly representable in float64, whose storage order diverges most
from their numeric order.

`JsonTrainingRequest::criteria()` now overrides the target's `Values`
ordering to `None` when delegating to the scanner, since a raw-column
sort can't help the extracted value anyway. `train_index` sorts the
extracted `(value, row_id)` stream itself, once, right before handing
it to the target trainer.

Fixes lance-format#7485
@github-actions github-actions Bot added A-python Python bindings A-index Vector index, linalg, tokenizer bug Something isn't working labels Jul 17, 2026
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 323a05ff-95c1-4b39-83fa-9c985496f8e2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.54839% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rust/lance-index/src/scalar/json.rs 93.54% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-index Vector index, linalg, tokenizer A-python Python bindings bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON-path scalar index returns wrong results for non-exact float values (ranges empty; equality misses)

1 participant