Skip to content

fix(engine): term/match_phrase on any .keyword multi-field matched 0 docs - #44

Merged
xerj-org merged 1 commit into
xerj-org:mainfrom
Vinz2168:fix/get-field-value-multifield-fallback
Jul 27, 2026
Merged

fix(engine): term/match_phrase on any .keyword multi-field matched 0 docs#44
xerj-org merged 1 commit into
xerj-org:mainfrom
Vinz2168:fix/get-field-value-multifield-fallback

Conversation

@Vinz2168

Copy link
Copy Markdown
Collaborator

Summary

term/match_phrase/etc. queries against any .keyword multi-field (category.keyword, manufacturer.keyword, ...) silently matched zero documents, regardless of value — one of the single most common ES/Kibana operations, used by essentially every dashboard filter and "click cell to filter" interaction. Likely the most impactful fix in this batch.

Root cause

get_field_value (index.rs, used by doc_matches_query's brute-force scan path) had no fallback for ES multi-field names. _source only ever stores the parent field's raw value — there's never a literal "category.keyword" key to walk into — so a query on the dotted multi-field name could never resolve a value.

The identical field works fine in a terms aggregation, because aggregations resolve fields through get_nested_field (aggs.rs), which already strips the trailing multi-field segment and retries against the parent. The query side never got the same treatment.

Confirmed via real Kibana traffic (captured in docker logs) and direct curl reproduction: term/match_phrase on manufacturer.keyword, customer_full_name.keyword, category.keyword all returned 0 hits, on both scalar- and array-valued underlying fields, even though the identical field's terms aggregation bucketed correctly.

Fix

Added the same "strip trailing segment, retry against parent" fallback to get_field_valueguarded so it only applies when the parent resolves to a leaf value (scalar, or an array with no objects). A real multi-field's parent is always a leaf text/keyword value.

An earlier, unguarded version of this fix caused a real regression (5 YAML exists_query/flattened test failures): a genuine nested/object field that's simply absent on a document must NOT be masked by returning its parent objectexists: {field: "obj.inner_field"} would otherwise wrongly report the field present just because the parent object exists. The is_leaf guard fixes that.

Test plan

  • cargo build --release -p xerj-engine -p xerj-api -p xerj-server
  • cargo fmt --check / cargo clippy --no-deps -- -D warnings — clean
  • cargo test --release -p xerj-engine --lib — 156/156 passed
  • term on manufacturer.keyword/customer_full_name.keyword/category.keyword: now return correct non-zero counts (1370, 1, 2024, 1218, 1055, ...), matching their terms-aggregation buckets exactly — previously all 0
  • Full ES-compat YAML conformance suite: 1360 passed, 0 failed, 3 skipped — no regressions (specifically re-verified the exists_query/flattened cases that an earlier unguarded version of this fix broke)

🤖 Generated with Claude Code

…docs

`get_field_value` (index.rs, used by `doc_matches_query`'s brute-force
scan path for `term`/`match_phrase`/etc.) had no fallback for ES
multi-field names. A query on `category.keyword`, `manufacturer.keyword`,
or any other `.keyword` sub-field could never resolve a value, because
`_source` only ever stores the PARENT field's raw value -- there's
never a literal `"category.keyword"` key to walk into.

The identical field works fine in a `terms` aggregation, because
aggregations resolve fields through `get_nested_field` (aggs.rs),
which already strips the trailing multi-field segment and retries
against the parent. The query side (`doc_matches_query`) never got
the same treatment, so `term`/`match_phrase` filters on a `.keyword`
field -- one of the single most common ES/Kibana operations, used by
essentially every dashboard filter and "click cell to filter"
interaction -- silently matched zero documents regardless of value.

Confirmed via real Kibana traffic (docker logs) and direct `curl`
reproduction: `term`/`match_phrase` on `manufacturer.keyword`,
`customer_full_name.keyword`, `category.keyword` all returned 0 hits
before this fix, on both scalar- and array-valued underlying fields,
even though the same field's `terms` aggregation bucketed correctly.

Fix: added the same "strip trailing segment, retry against parent"
fallback to `get_field_value`, guarded so it only applies when the
parent resolves to a LEAF value (scalar, or an array with no objects)
-- a real multi-field's parent is always a leaf text/keyword value. A
first version without this guard caused a real regression (5 YAML
`exists_query`/`flattened` test failures): a genuine nested/object
field that's simply absent on a document must NOT be masked by
returning its parent OBJECT, or `exists: {field: "obj.inner_field"}`
would wrongly report the field present just because the parent object
exists.

Verified: `term` queries on `manufacturer.keyword` /
`customer_full_name.keyword` / `category.keyword` now return the
correct non-zero counts, matching their `terms`-aggregation buckets
exactly (1370, 1, 2024, 1218, 1055, ...). Full ES-compat YAML
conformance suite: 1360 passed, 0 failed, 3 skipped -- no regressions
(the is_leaf guard specifically fixes the exists-query regression the
unguarded version introduced).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016XJaZygeuRfZfUg2B8tPKU
@xerj-org
xerj-org merged commit 03bcf57 into xerj-org:main Jul 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants