From e4a50a1dbcbdb8bee7ea26b6e9732e735f17dd3b Mon Sep 17 00:00:00 2001 From: generall Date: Mon, 21 Sep 2026 08:20:51 +0200 Subject: [PATCH] Test substring fallback on a text-indexed field, document estimator params A text index cannot serve `substring`, so on a field that has only a text index the condition runs through the payload fallback; only strict mode may reject it. Pin that in the OpenAPI suite and reword the strict-mode unit test comment, which read as if the text index itself blocked the query. Also spell out what `keys` and `postings` mean in `keys_union_cardinality`. Co-Authored-By: Claude Fable 5.1 --- lib/collection/src/problems/unindexed_field.rs | 4 +++- tests/openapi/test_substring_match.py | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/collection/src/problems/unindexed_field.rs b/lib/collection/src/problems/unindexed_field.rs index fc0ad9f8f6..4ece7c428f 100644 --- a/lib/collection/src/problems/unindexed_field.rs +++ b/lib/collection/src/problems/unindexed_field.rs @@ -743,7 +743,9 @@ mod tests { infer_index_from_field_condition(&condition), vec![FieldIndexType::KeywordMatch], ); - // A text index is not enough: it holds tokens, not raw values. + // A text index holds tokens, not raw values, so it cannot serve the + // condition and does not satisfy strict mode. Without strict mode the + // condition still runs through the payload fallback. let text = PayloadFieldSchema::FieldType(PayloadSchemaType::Text); assert!(!schema_capabilities(&text).contains(&FieldIndexType::KeywordMatch)); } diff --git a/tests/openapi/test_substring_match.py b/tests/openapi/test_substring_match.py index 3ada3d4889..04aeb0cf43 100644 --- a/tests/openapi/test_substring_match.py +++ b/tests/openapi/test_substring_match.py @@ -155,13 +155,23 @@ def test_substring_match_count(): # --------------------------------------------------------------------------- -# 3. Strict mode: substring filtering requires a keyword index. A text -# index is not enough, since it stores tokens rather than raw values. +# 3. A text index on the field cannot serve substring (it stores tokens, not +# raw values), so the condition falls back to the payload scan and still +# works. Only strict mode may reject it. +# --------------------------------------------------------------------------- + +def test_substring_match_with_text_index_falls_back(): + _create_index("description", "text") + for substring in SUBSTRING_PROBES: + assert _scroll_ids(_substring_filter("description", substring)) == expected_ids(substring), substring + + +# --------------------------------------------------------------------------- +# 4. Strict mode: substring filtering requires a keyword index. A text +# index does not count, since it cannot serve the condition. # --------------------------------------------------------------------------- def test_strict_mode_requires_keyword_index(): - _create_index("description", "text") - _set_strict_mode({ "enabled": True, "unindexed_filtering_retrieve": False,