Skip to content

[improvement](be) Skip dictionary entry bodies of non-matching terms in SNII term expansion - #69600

Merged
airborne12 merged 2 commits into
apache:masterfrom
airborne12:snii-expand-terms-accept-key
Oct 26, 2026
Merged

airborne12 merged 2 commits into
apache:masterfrom
airborne12:snii-expand-terms-accept-key

Conversation

@airborne12

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: MATCH_REGEXP, wildcard, suffix and contains patterns without a literal
prefix make SniiIndexSource::expand_terms enumerate the whole dictionary of every segment.
LogicalIndexReader::visit_prefix_terms always passed an empty accept_key to
DictBlockReader::visit_prefix_range, so the body of every dictionary entry was decoded
(decode_dict_entry_rest) and the caller then rejected almost all of them with the
required-text test and the pattern match. DictBlockReader already supports a key-only
accept_key that skips the body of a rejected term.

visit_prefix_terms now takes an optional accept_key, and expand_terms passes its term test
(required text, then pattern match) as that predicate. Terms are tested exactly as before, only
earlier, and the visitor still sees only accepted terms, so max_expansions and early stop count
the same terms. Other callers keep the default (accept all).

Measured on 99M AgentLogs rows (722 segments, tenant and status filters, token regexp
'transi.', hot median): SNII 6028 ms -> 945 ms, V3 unchanged at about 590 ms. Result counts
are identical to V3 for regexp, leading-wildcard regexp and phrase-prefix queries. Patterns with
a literal prefix (for example '^transi.
') seek into the dictionary and are unchanged: SNII and
V3 are level at about 100 ms before and after. Gdb sampling showed 58% of the time in
decode_dict_entry_rest before and about 5% after.

Release note

Faster MATCH_REGEXP / wildcard term expansion on SNII inverted indexes.

Check List (For Author)

  • Test: Unit Test (SniiRegexpQueryTest.AcceptKeySkipsBodiesOfRejectedTerms, SniiRegexpQueryTest.UnanchoredRegexpDecodesOnlyMatchedBodies; SniiB2T07Dict* and SniiRegexpQueryTest* pass on branch-4.2). Manual test (count equality against V3 on 99M rows, before/after timing).
  • Behavior changed: No
  • Does this need documentation: No

🤖 Generated with Claude Code

…in SNII term expansion

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: MATCH_REGEXP, wildcard, suffix and contains patterns without a literal
prefix make SniiIndexSource::expand_terms enumerate the whole dictionary of every segment.
LogicalIndexReader::visit_prefix_terms always passed an empty accept_key to
DictBlockReader::visit_prefix_range, so the body of every dictionary entry was decoded
(decode_dict_entry_rest) and the caller then rejected almost all of them with the
required-text test and the pattern match. DictBlockReader already supports a key-only
accept_key that skips the body of a rejected term.

visit_prefix_terms now takes an optional accept_key, and expand_terms passes its term test
(required text, then pattern match) as that predicate. Terms are tested exactly as before, only
earlier, and the visitor still sees only accepted terms, so max_expansions and early stop count
the same terms. Other callers keep the default (accept all).

Measured on 99M AgentLogs rows (722 segments, tenant and status filters, token regexp
'transi.*', hot median): SNII 6028 ms -> 945 ms, V3 unchanged at about 590 ms. Result counts
are identical to V3 for regexp, leading-wildcard regexp and phrase-prefix queries. Patterns with
a literal prefix (for example '^transi.*') seek into the dictionary and are unchanged: SNII and
V3 are level at about 100 ms before and after. Gdb sampling showed 58% of the time in
decode_dict_entry_rest before and about 5% after.

### Release note

Faster MATCH_REGEXP / wildcard term expansion on SNII inverted indexes.

### Check List (For Author)

- Test: Unit Test (SniiRegexpQueryTest.AcceptKeySkipsBodiesOfRejectedTerms, SniiRegexpQueryTest.UnanchoredRegexpDecodesOnlyMatchedBodies; SniiB2T07Dict* and SniiRegexpQueryTest* pass on branch-4.2). Manual test (count equality against V3 on 99M rows, before/after timing).
- Behavior changed: No
- Does this need documentation: No

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@airborne12

Copy link
Copy Markdown
Member Author

run buildall

@airborne12

Copy link
Copy Markdown
Member Author

/review

@airborne12
airborne12 marked this pull request as ready for review October 26, 2026 03:10

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static review of PR #69600 at a22e758 found no actionable issue in the four changed files or their related dictionary/query/posting paths. The existing required-text and pattern checks now run before body decoding, while accepted terms retain their ordering, metadata and expansion limits. No existing inline findings require carrying forward; no additional user focus was supplied.

Critical checkpoint conclusions:

  • Goal and proof: the change reuses the dictionary's existing key predicate. The two new tests check accept-all/selective/reject-all/early-stop decoding counts and an unanchored regexp's expanded term and document results. Their assertions support the intended work reduction by inspection.
  • Scope and clarity: focused optional-argument forwarding, relocation of the existing predicate, and two related tests. No unrelated production change.
  • Concurrency and locks: callbacks remain synchronous; matcher state is query-local, shared reader data remains immutable, and no thread, lock or lock-order change is introduced.
  • Lifecycle and static initialization: captured references and term views remain valid through matching; resident ownership or a shared block pin covers each scan. Accepted entries own their strings/payloads. No new ownership cycle or static initialization dependency.
  • Configuration: no configuration item or dynamic-update behavior changed.
  • Compatibility: the internal reader argument has an accept-all default for existing callers. No persisted format or external protocol change affecting rolling upgrades.
  • Thrift/protobuf: no definitions or field/enum identifiers changed.
  • Parallel paths: scored/unscored expansion and phrase-edge consumers use the updated source. CLucene retains equivalent matching logic; prefix collection and compaction probes retain their default predicate. Full dictionary and range scans remain appropriate for their callers.
  • Special conditions: required-text rejection still precedes pattern matching. Existing key skipping preserves front coding and entry boundaries; prefix exhaustion and accepted-hit stopping remain separate. The new comments explain why matching moves earlier.
  • Test coverage: inspected new tests and existing pattern/source/T07/phrase tests covering the five pattern kinds, limits, no-match/invalid patterns, namespace bypass, mixed entry layouts, anchors and resident/on-demand enumeration. Tests were not executed. A single combined selective-predicate/multiple-on-demand-block/cap test is absent; the component tests and static control-flow trace cover those transitions separately.
  • Expected results: the fixture has 11 terms; order/ordinal are the two ord-prefixed terms, and only ordinal contains rdin and has document IDs 0 through 8999. New expected lists/counts agree with the fixture. No generated result file changed.
  • Observability: existing decoder counters directly expose the intended work reduction; existing dictionary errors and query instrumentation remain. No new logging or metric is necessary for this local change.
  • Transactions and persistence: no transaction, EditLog, visible-version, replay or failover behavior changed.
  • Data writes and crash behavior: no write path, atomicity boundary or crash-cleanup ownership changed. Existing visibility and delete-bitmap filtering remain outside this term predicate.
  • FE/BE variables: no new variable or transport path.
  • Performance and memory: keys still enumerate as needed, with the same matcher sequence up to the accepted-term cap. Rejected bodies avoid payload materialization and hit copies; accepted entries retain existing caching. No significant new retained allocation or memory-accounting scope.
  • Other correctness/error checks: block CRC validation still precedes scanning, requested-body decoding and Status propagation remain intact, and SQL null/bitmap handling is unchanged. No further substantiated issue remained after the risk recheck.

Review converged in 2 rounds with no unresolved correctness candidates. All conclusions are from static inspection: no builds, tests, format/static-analysis checks, runtime validation or benchmark reproduction were performed. The PR's reported timing improvements were not independently measured.

Review converged after 2 round(s).

@shuke987

Copy link
Copy Markdown
Contributor

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28959 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit a22e758aa2b2faa1d97d9c126713827d97a0de89, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17596	5188	5138	5138
q2	2147	304	255	255
q3	10220	1428	863	863
q4	4688	463	338	338
q5	7438	811	561	561
q6	186	186	159	159
q7	773	796	608	608
q8	9256	1351	1190	1190
q9	5540	4235	4308	4235
q10	6836	1284	1033	1033
q11	444	257	236	236
q12	644	403	305	305
q13	18076	3616	3039	3039
q14	269	276	248	248
q15	q16	724	710	658	658
q17	1191	755	575	575
q18	7019	6172	6129	6129
q19	1104	977	606	606
q20	377	347	227	227
q21	5001	2275	2463	2275
q22	351	309	281	281
Total cold run time: 99880 ms
Total hot run time: 28959 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5624	5675	5670	5670
q2	699	623	585	585
q3	5199	5365	4911	4911
q4	2049	2166	1634	1634
q5	5277	5144	5830	5144
q6	253	199	162	162
q7	2254	2030	1897	1897
q8	3085	2753	2713	2713
q9	8087	7817	7939	7817
q10	3762	3706	3263	3263
q11	789	464	438	438
q12	703	716	552	552
q13	3594	4001	3264	3264
q14	325	323	294	294
q15	q16	697	704	699	699
q17	8445	7422	7040	7040
q18	13021	12157	13088	12157
q19	988	852	837	837
q20	2152	2165	1978	1978
q21	6250	5228	5229	5228
q22	513	475	409	409
Total cold run time: 73766 ms
Total hot run time: 66692 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 151173 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit a22e758aa2b2faa1d97d9c126713827d97a0de89, data reload: false

query5	4314	592	442	442
query6	433	224	198	198
query7	4797	442	228	228
query8	322	164	150	150
query9	8782	3118	3097	3097
query10	475	314	254	254
query11	5917	3832	3583	3583
query12	145	92	89	89
query13	1253	429	319	319
query14	6132	3933	3650	3650
query14_1	3403	3407	3393	3393
query15	203	199	180	180
query16	963	401	399	399
query17	867	711	556	556
query18	2425	418	317	317
query19	236	157	131	131
query20	87	82	80	80
query21	205	128	107	107
query22	12894	12970	12859	12859
query23	14107	12774	12487	12487
query23_1	12519	12492	12589	12492
query24	7412	820	462	462
query24_1	493	475	467	467
query25	529	405	335	335
query26	1244	253	133	133
query27	2764	439	265	265
query28	4591	1964	1942	1942
query29	1565	589	472	472
query30	298	214	183	183
query31	1001	926	803	803
query32	142	92	89	89
query33	522	300	236	236
query34	928	863	487	487
query35	726	749	657	657
query36	828	832	733	733
query37	112	101	80	80
query38	1765	1710	1664	1664
query39	788	781	741	741
query39_1	723	727	734	727
query40	213	126	97	97
query41	81	65	63	63
query42	86	84	85	84
query43	345	358	310	310
query44	1306	671	692	671
query45	183	178	179	178
query46	853	963	565	565
query47	2940	3030	2839	2839
query48	289	298	219	219
query49	575	388	295	295
query50	674	269	212	212
query51	10500	10758	10624	10624
query52	81	81	68	68
query53	184	211	150	150
query54	244	208	193	193
query55	71	64	60	60
query56	223	207	220	207
query57	1676	1607	1549	1549
query58	282	261	252	252
query59	2137	2127	1941	1941
query60	278	240	228	228
query61	145	138	141	138
query62	392	337	288	288
query63	184	153	167	153
query64	2696	932	805	805
query65	3345	3246	3265	3246
query66	1762	403	294	294
query67	20163	20094	20104	20094
query68	2959	1022	621	621
query69	386	289	242	242
query70	888	778	760	760
query71	290	241	210	210
query72	2686	2490	2317	2317
query73	513	528	295	295
query74	4614	4524	4316	4316
query75	2317	2299	1913	1913
query76	2321	1020	604	604
query77	342	370	262	262
query78	9694	9663	9208	9208
query79	883	841	533	533
query80	482	421	371	371
query81	519	312	278	278
query82	203	125	104	104
query83	193	228	181	181
query84	285	115	94	94
query85	744	411	359	359
query86	257	236	228	228
query87	1962	1985	1793	1793
query88	3565	2646	2651	2646
query89	310	277	239	239
query90	2107	172	171	171
query91	159	149	129	129
query92	97	82	83	82
query93	984	997	583	583
query94	450	317	270	270
query95	572	341	363	341
query96	665	485	232	232
query97	2456	2430	2348	2348
query98	174	153	156	153
query99	764	771	650	650
Total cold run time: 233243 ms
Total hot run time: 151173 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.27 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit a22e758aa2b2faa1d97d9c126713827d97a0de89, data reload: false

query1	0.00	0.00	0.00
query2	0.10	0.05	0.05
query3	0.26	0.09	0.09
query4	1.61	0.10	0.10
query5	0.23	0.24	0.24
query6	1.17	0.69	0.67
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.39	0.36	0.35
query10	0.58	0.61	0.57
query11	0.19	0.14	0.13
query12	0.19	0.15	0.14
query13	0.50	0.49	0.49
query14	1.02	1.01	1.00
query15	0.62	0.61	0.62
query16	0.35	0.35	0.35
query17	1.15	1.12	1.11
query18	0.21	0.19	0.18
query19	2.09	2.00	1.93
query20	0.02	0.02	0.01
query21	15.50	0.19	0.13
query22	4.96	0.05	0.05
query23	16.27	0.33	0.12
query24	3.00	0.66	0.38
query25	0.11	0.06	0.06
query26	1.33	0.22	0.15
query27	0.06	0.05	0.06
query28	3.36	0.80	0.35
query29	12.52	4.24	3.27
query30	0.28	0.14	0.09
query31	2.78	0.56	0.32
query32	3.22	0.60	0.51
query33	3.12	3.20	3.18
query34	15.33	4.03	3.40
query35	3.36	3.37	3.36
query36	0.54	0.45	0.43
query37	0.09	0.07	0.06
query38	0.05	0.04	0.04
query39	0.03	0.04	0.03
query40	0.17	0.15	0.14
query41	0.08	0.03	0.02
query42	0.04	0.03	0.03
query43	0.04	0.03	0.03
Total cold run time: 97.02 s
Total hot run time: 24.27 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 66.79% (32112/48077)
Line Coverage 51.62% (339460/657654)
Region Coverage 47.06% (275165/584739)
Branch Coverage 48.73% (129682/266112)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100% (0/0) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.18% (35953/46585)
Line Coverage 62.42% (406102/650627)
Region Coverage 58.80% (342591/582619)
Branch Coverage 59.80% (158739/265460)

@airborne12
airborne12 merged commit 6979f7f into apache:master Oct 26, 2026
43 checks passed
@airborne12
airborne12 deleted the snii-expand-terms-accept-key branch October 26, 2026 10:11
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

github-actions Bot pushed a commit that referenced this pull request Oct 26, 2026
…in SNII term expansion (#69600)

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: MATCH_REGEXP, wildcard, suffix and contains patterns
without a literal
prefix make SniiIndexSource::expand_terms enumerate the whole dictionary
of every segment.
LogicalIndexReader::visit_prefix_terms always passed an empty accept_key
to
DictBlockReader::visit_prefix_range, so the body of every dictionary
entry was decoded
(decode_dict_entry_rest) and the caller then rejected almost all of them
with the
required-text test and the pattern match. DictBlockReader already
supports a key-only
accept_key that skips the body of a rejected term.

visit_prefix_terms now takes an optional accept_key, and expand_terms
passes its term test
(required text, then pattern match) as that predicate. Terms are tested
exactly as before, only
earlier, and the visitor still sees only accepted terms, so
max_expansions and early stop count
the same terms. Other callers keep the default (accept all).

Measured on 99M AgentLogs rows (722 segments, tenant and status filters,
token regexp
'transi.*', hot median): SNII 6028 ms -> 945 ms, V3 unchanged at about
590 ms. Result counts
are identical to V3 for regexp, leading-wildcard regexp and
phrase-prefix queries. Patterns with
a literal prefix (for example '^transi.*') seek into the dictionary and
are unchanged: SNII and
V3 are level at about 100 ms before and after. Gdb sampling showed 58%
of the time in
decode_dict_entry_rest before and about 5% after.
yiguolei pushed a commit that referenced this pull request Oct 27, 2026
…ching terms in SNII term expansion #69600 (#69637)

### What problem does this PR solve?

Issue Number: None

Related PR: #69600

Problem Summary: Backport of #69600 to branch-4.2. MATCH_REGEXP,
wildcard, suffix and contains patterns without a literal prefix make
`SniiIndexSource::expand_terms` enumerate the whole dictionary of every
segment. `LogicalIndexReader::visit_prefix_terms` passed an empty
`accept_key`, so the body of every dictionary entry was decoded before
the caller rejected almost all of them. `visit_prefix_terms` now takes
an optional `accept_key`, and `expand_terms` passes its term test as
that predicate, so a rejected term is skipped before its body is
decoded. Terms are tested exactly as before, only earlier.

Measured on master's parent line (99M AgentLogs rows, 722 segments):
token regexp `transi.*` on SNII 6028 ms -> 945 ms, V3 about 590 ms.

### Branch adaptations

None. `git cherry-pick -x 6979f7f` applied cleanly to the current
branch-4.2 and touches the same four files as the original:
`logical_index_reader.{h,cpp}`, `snii_index_source.cpp` and
`snii_query_test.cpp`.

### Validation

- The same change (before the one-line clang-format fix of the
declaration in `logical_index_reader.h`) was built and run on an earlier
branch-4.2 base (`ecbc5feab9`): `SniiB2T07Dict*` and
`SniiRegexpQueryTest*`, 31 tests passed, including the two new tests.
- On master, `Snii*` passed (1373 tests) and the master CI passed all
checks.
- Not rebuilt locally on the current branch-4.2 tip; this PR's BE UT run
covers that.

### Release note

Faster MATCH_REGEXP / wildcard term expansion on SNII inverted indexes.

### Check List (For Author)

- Test: Unit Test
- Behavior changed: No
- Does this need documentation: No

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: martin.foo838 <martin.foo838@gmail.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
yiguolei pushed a commit that referenced this pull request Oct 27, 2026
…ching terms in SNII term expansion #69600 (#69638)

Cherry-picked from #69600

Co-authored-by: Jack <jiangkai@selectdb.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. dev/4.2.0-merged reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants