Skip to content

[fix](inverted index) Align phrase position defaults with analysis - #69199

Merged
airborne12 merged 5 commits into
apache:masterfrom
airborne12:cir22263-phrase-default-master-v2
Oct 18, 2026
Merged

airborne12 merged 5 commits into
apache:masterfrom
airborne12:cir22263-phrase-default-master-v2

Conversation

@airborne12

@airborne12 airborne12 commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: CIR-22263

Related PR: #69192

Problem Summary: FE persists support_phrase=true for new tokenized indexes and leaves exact indexes docs-only. BE must treat an omitted property as false because older persisted analyzer indexes can omit it despite having docs-only postings; this also preserves TOKENIZE output. Built-in lowercase normalization emits one term, so phrase predicates can use docs-only postings and BM25 can use unit frequency and document length. Custom normalizers can emit multiple terms, so new indexes retain positions by default, including when a legacy custom policy shadows the built-in name.

This PR changes phrase-position defaults and normalizer compatibility. PR #69192 adds direct SNII compaction of compatible docs-only postings. Neither change addresses the tokenized T2 merge hotspot identified in CIR-22263.

Release note

New exact and built-in lowercase-normalized indexes default to docs-only postings. Custom normalizers retain phrase positions by default. Lowercase-normalized indexes retain phrase matching and relevance scoring without stored positions.

Check List (For Author)

  • Test
    • Unit Test: targeted FE default and policy-binding tests; 11 targeted BE postings, scoring-statistics, reader, and missing-norm tests.
    • Complete PR diff passed the English description check, clang-format 16, Checkstyle, and BE build hygiene checks.
  • Behavior changed:
    • Yes. Exact indexes omit positions by default; custom normalizers retain positions; built-in lowercase normalization keeps phrase and scoring behavior with docs-only postings.
  • Does this need documentation?
    • No.

Remote Clang Formatter and CheckStyle passed on the updated head. Automated code review is pending.

@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

@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 all four changed files and their FE DDL/persistence, CLucene/SNII writer, query/scoring, compaction, and SQL TOKENIZE consumers at ee2af95. Four P2 findings are reported inline: normalizer phrase predicates fail, normalizer scores silently become zero and affect score filters, TOKENIZE output changes, and existing BE unit fixtures retain incompatible expectations. No preexisting inline comments or additional user focus were supplied.

Critical checkpoint conclusions:

  • Goal and proof: the local defaults align with tokenization and explicit overrides survive; added helper/constructor assertions match those local rules, but the inline findings expose missing downstream handling.
  • Scope and reuse: the four-file edit is focused and reuses parser/alias getters; TOKENIZE needs a separate output-policy decision.
  • Concurrency and locks: no new threads, shared mutations, lock acquisition, lock-order change, or expanded critical section; constructor properties are copied before mutation.
  • Lifecycle and initialization: no new ownership, globals, cross-TU initialization dependencies, or changed analyzer/writer release order.
  • Configuration: no new configuration or reload behavior.
  • Compatibility: explicit stored phrase flags remain unchanged through Gson, Thrift, and PB conversion. Ordinary base-to-head creation/rolling-BE paths do not establish a separate legacy-omission bug. SQL output, normalizer phrase predicates, and normalizer scoring/result compatibility are the verified issues above.
  • Thrift/protobuf field IDs: no definitions changed; cross-branch ID comparison is not applicable.
  • Parallel paths: CLucene/SNII, scalar/array, cloud/shared-nothing, query/scoring, and compaction paths were checked; numeric BKD does not use phrase positions.
  • Conditions: parser validation/canonicalization, alias priority, empty analyzer, explicit flags, and gram-family forced-false defaults were checked. Normalizer FULLTEXT phrase and scoring capability gates require the inline corrections.
  • Test coverage: new FE/BE unit cases cover local property maps; end-to-end normalizer phrase/scoring and TOKENIZE compatibility cases are missing. Existing default-sensitive BE fixtures need correction.
  • Test results: no goldens were changed; existing TOKENIZE goldens contradict the new behavior. No build, unit test, regression test, or formatter was executed, as required by the review contract; author test claims are not independent execution evidence.
  • Observability: existing phrase errors and compaction eligibility diagnostics cover these local changes; no additional metrics or high-volume logs are required.
  • Persistence and failover: existing property serialization/journal paths remain intact; no added transaction, visible-version, delete-bitmap, or failover transition.
  • Writes and atomicity: existing writer/publication lifecycles are preserved; direct SNII compaction still validates physical source tiers/positions, with rebuild fallback.
  • FE-BE variable passing: no new field; existing property-map converters preserve explicit values and omissions.
  • Performance and memory: bounded property lookups, no new row-scale scan, large allocation, ownership risk, or toolchain/ABI dependency. Docs-only savings are intended, subject to the normalizer query finding.
  • Other concerns: full changed scope reviewed; no additional user focus was supplied.

Review converged after three rounds: every reviewer returned NO_NEW_VALUABLE_FINDINGS in round 3 on this complete four-comment set, all changed scope was covered, and every candidate/risk has an evidence-based disposition. No unresolved static coverage remains. Remaining validation limit: static analysis only, with no executed runtime evidence.

Review converged after 3 round(s).

|| this.properties.containsKey(InvertedIndexProperties.INVERTED_INDEX_NORMALIZER_NAME_KEY)) {
if (!InvertedIndexProperties.INVERTED_INDEX_PARSER_NONE.equalsIgnoreCase(getInvertedIndexParser())
|| StringUtils.isNotEmpty(this.properties.get(
InvertedIndexProperties.INVERTED_INDEX_ANALYZER_NAME_KEY))) {

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.

[P2] Preserve phrase predicates on normalizer-only indexes

For PROPERTIES("normalizer"="lowercase") with omitted support_phrase, this removes the previously injected true. The BE still classifies that index as FULLTEXT because should_analyzer() includes normalizers, so a query such as v MATCH_PHRASE 'foo' over a stored FOO now fails with "phrase queries require setting support_phrase = true" in match.cpp / inverted_index_iterator.cpp; prefix/edge predicates are gated too. INDEX_INVALID_PARAMETERS is terminal rather than a scan fallback. A normalizer emits one keyword, and singleton phrase lowering already produces a term/expansion query that needs no positions. Please adapt those capability gates for docs-only normalizers and cover these predicates alongside the new default.

|| this.properties.containsKey(InvertedIndexProperties.INVERTED_INDEX_PARSER_KEY_ALIAS)
|| this.properties.containsKey(InvertedIndexProperties.INVERTED_INDEX_ANALYZER_NAME_KEY)
|| this.properties.containsKey(InvertedIndexProperties.INVERTED_INDEX_NORMALIZER_NAME_KEY)) {
if (!InvertedIndexProperties.INVERTED_INDEX_PARSER_NONE.equalsIgnoreCase(getInvertedIndexParser())

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.

[P2] Preserve normalizer scoring when changing position defaults

Removing the default true also makes IndexReaderHelper::is_need_similarity_score() false for normalizer-only indexes. For a new lowercase-normalizer index containing FOO, SELECT id, score() AS s FROM t WHERE v MATCH 'foo' AND score()>0 ORDER BY s DESC LIMIT 10 previously returned a positive-scoring hit. Now MatchPredicateCollector skips the field and TextIndexReader disables scoring; CollectionSimilarity substitutes zero for missing entries, so the score filter silently removes that matching row. This query has no phrase predicate, so fixing the phrase gates does not address it. Preserve scoring independently from phrase-position storage, or keep the previous effective setting until this case is supported, and cover normalizer scoring/min-score queries.

}
return INVERTED_INDEX_PARSER_PHRASE_SUPPORT_NO;
const auto analyzer = properties.find(INVERTED_INDEX_ANALYZER_NAME_KEY);
return get_parser_string_from_properties(properties) != INVERTED_INDEX_PARSER_NONE ||

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.

[P2] Keep TOKENIZE's omitted-position default unchanged

FunctionTokenize::execute_impl also calls this helper (be/src/exprs/function/function_tokenize.cpp:247), and _do_tokenize uses the result to include "position" in every JSON object. An existing call such as TOKENIZE('alpha beta', '"parser"="english"') therefore changes from token-only output to output with positions, although this PR is scoped to index defaults. The unchanged test_tokenize and custom-analyzer regression goldens still expect token-only objects. Give TOKENIZE its own omitted=false behavior, preserving explicit true, so the physical-index default does not silently change this SQL output contract.


properties[INVERTED_INDEX_PARSER_KEY] = INVERTED_INDEX_PARSER_ENGLISH;
EXPECT_EQ(get_parser_phrase_support_string_from_properties(properties),
INVERTED_INDEX_PARSER_PHRASE_SUPPORT_YES);

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.

[P2] Update existing BE fixtures for the new phrase default

The new helper expectation is correct, but SniiWriterTest.BuiltinAnalyzerNameStillInitialises (be/test/storage/index/inverted/snii/snii_writer_test.cpp:696-712) still passes only parser=english and expects kDocsOnly because support_phrase is absent. SniiIndexColumnWriter::init now selects kDocsPositions for that exact input, so the existing assertion fails. CollectionStatisticsTest.CollectSkipsIndexWithoutSimilarityScore likewise omits the flag and expects the old false scoring gate. Update the default-sensitive expectations, and set explicit support_phrase=false in fixtures intended to exercise disabled phrase/scoring behavior.

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage `` 🎉
Increment coverage report
Complete coverage report

### What problem does this PR solve?

Issue Number: CIR-22263

Related PR: apache#69192

Problem Summary: Inferring phrase positions from an omitted support_phrase property changes how older docs-only indexes are read and adds positions to TOKENIZE output. Newly created tokenized indexes already persist support_phrase=true in FE. Keep an absent property false when reading persisted metadata, while allowing normalizer-only single-term indexes to use phrase predicates and similarity scoring without stored positions. Update SHOW INDEX expectations for parser=none.

### Release note

Existing indexes with an omitted support_phrase property retain their stored docs-only interpretation.

### Check List (For Author)

- Test: Unit Test: focused FE IndexTest and two BE tests passed.
- Behavior changed: Yes. Legacy omitted phrase metadata remains docs-only and normalizer-only queries remain available.
- Does this need documentation: No
airborne12 added a commit to airborne12/apache-doris that referenced this pull request Oct 16, 2026
### What problem does this PR solve?

Issue Number: CIR-22263

Related PR: apache#69199

Problem Summary: Inferring phrase positions from a parser when support_phrase is absent changes how older docs-only indexes are read and adds positions to TOKENIZE output. Newly created tokenized indexes already persist support_phrase=true in FE. Keep an absent property false when reading persisted metadata, while allowing normalizer-only single-term indexes to use phrase predicates and similarity scoring without stored positions. Update SHOW INDEX expectations for parser=none.

### Release note

Existing indexes with an omitted support_phrase property retain their stored docs-only interpretation.

### Check List (For Author)

- Test: Unit Test: focused FE IndexTest and two BE tests passed.
- Behavior changed: Yes. Legacy omitted phrase metadata remains docs-only and normalizer-only queries remain available.
- Does this need documentation: No
@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17581	5697	5536	5536
q2	2122	289	251	251
q3	10247	1408	853	853
q4	4683	464	356	356
q5	7481	792	526	526
q6	187	189	154	154
q7	764	797	603	603
q8	9243	1304	1058	1058
q9	5475	4252	4275	4252
q10	6828	1294	1013	1013
q11	435	257	238	238
q12	647	402	284	284
q13	18097	3615	3013	3013
q14	281	273	247	247
q15	q16	735	737	678	678
q17	1210	756	577	577
q18	7117	6242	6176	6176
q19	1134	960	629	629
q20	396	344	225	225
q21	5640	2547	2611	2547
q22	389	352	300	300
Total cold run time: 100692 ms
Total hot run time: 29516 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	6622	6600	6671	6600
q2	736	613	591	591
q3	5512	5737	5180	5180
q4	2044	2140	1538	1538
q5	5846	5802	5733	5733
q6	242	191	146	146
q7	2276	2012	1840	1840
q8	3124	2710	2721	2710
q9	8183	8048	7970	7970
q10	3631	3559	3236	3236
q11	606	397	368	368
q12	650	679	504	504
q13	3221	3505	3000	3000
q14	291	283	258	258
q15	q16	662	672	614	614
q17	7870	7003	6876	6876
q18	13068	12272	13007	12272
q19	894	789	788	788
q20	2157	2162	1930	1930
q21	5766	4705	4768	4705
q22	492	440	405	405
Total cold run time: 73893 ms
Total hot run time: 67264 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 151788 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 ee2af95611ae10a237a096aa40503b714db5722f, data reload: false

query5	4309	599	467	467
query6	433	207	193	193
query7	4813	442	224	224
query8	316	170	153	153
query9	8783	4001	3987	3987
query10	511	311	264	264
query11	5991	3778	3525	3525
query12	150	90	87	87
query13	1250	433	343	343
query14	6184	3995	3720	3720
query14_1	3511	3499	3503	3499
query15	210	197	183	183
query16	957	438	416	416
query17	864	664	540	540
query18	2442	421	307	307
query19	225	165	137	137
query20	90	85	83	83
query21	217	134	111	111
query22	12898	13091	12811	12811
query23	13450	12880	12440	12440
query23_1	12660	12574	12449	12449
query24	7103	831	477	477
query24_1	484	474	473	473
query25	538	417	373	373
query26	1274	253	139	139
query27	2768	440	271	271
query28	4626	1979	1962	1962
query29	1583	595	459	459
query30	304	226	189	189
query31	993	928	805	805
query32	171	100	93	93
query33	526	312	250	250
query34	902	850	497	497
query35	738	761	642	642
query36	853	815	748	748
query37	131	106	93	93
query38	1784	1752	1685	1685
query39	790	796	747	747
query39_1	745	744	748	744
query40	229	113	98	98
query41	72	71	68	68
query42	89	89	80	80
query43	379	376	333	333
query44	1328	695	701	695
query45	176	186	161	161
query46	862	996	564	564
query47	3010	2886	2914	2886
query48	286	301	214	214
query49	569	400	297	297
query50	683	268	209	209
query51	10311	10265	10409	10265
query52	76	77	70	70
query53	186	203	154	154
query54	229	189	180	180
query55	72	68	69	68
query56	221	216	220	216
query57	1616	1580	1499	1499
query58	293	262	253	253
query59	2155	2205	1996	1996
query60	297	245	220	220
query61	145	147	147	147
query62	393	338	292	292
query63	193	161	161	161
query64	2715	949	782	782
query65	3285	3237	3226	3226
query66	1782	455	298	298
query67	20117	20041	20033	20033
query68	2957	1000	611	611
query69	383	287	250	250
query70	859	785	780	780
query71	312	224	209	209
query72	2715	2575	2277	2277
query73	504	520	302	302
query74	4546	4459	4283	4283
query75	2323	2282	1927	1927
query76	2303	1016	597	597
query77	337	400	269	269
query78	9854	9701	9171	9171
query79	1010	870	514	514
query80	1246	427	341	341
query81	583	320	265	265
query82	1020	132	106	106
query83	306	189	181	181
query84	311	126	95	95
query85	864	431	362	362
query86	413	233	233	233
query87	2024	1953	1855	1855
query88	3613	2719	2657	2657
query89	357	293	259	259
query90	1920	184	175	175
query91	160	144	128	128
query92	97	92	91	91
query93	1048	1017	573	573
query94	671	305	270	270
query95	584	340	318	318
query96	669	516	232	232
query97	2452	2420	2305	2305
query98	164	153	144	144
query99	778	758	657	657
Total cold run time: 235454 ms
Total hot run time: 151788 ms

@airborne12

Copy link
Copy Markdown
Member Author

run buildall

@airborne12

Copy link
Copy Markdown
Member Author

/review

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.10	0.05	0.05
query3	0.27	0.12	0.12
query4	1.61	0.11	0.12
query5	0.26	0.25	0.26
query6	1.17	0.69	0.68
query7	0.04	0.00	0.00
query8	0.06	0.04	0.03
query9	0.42	0.36	0.35
query10	0.61	0.60	0.58
query11	0.20	0.14	0.14
query12	0.19	0.14	0.15
query13	0.51	0.50	0.50
query14	1.04	1.01	1.00
query15	0.65	0.60	0.61
query16	0.36	0.35	0.32
query17	1.10	1.13	1.08
query18	0.23	0.22	0.22
query19	2.02	1.99	2.00
query20	0.02	0.01	0.01
query21	15.50	0.28	0.15
query22	4.54	0.06	0.06
query23	16.26	0.32	0.12
query24	3.19	0.72	0.37
query25	0.11	0.07	0.04
query26	1.18	0.25	0.15
query27	0.05	0.04	0.04
query28	1.78	0.61	0.39
query29	12.52	4.13	3.34
query30	0.27	0.14	0.14
query31	2.78	0.57	0.33
query32	3.23	0.62	0.53
query33	3.19	3.16	3.31
query34	15.54	3.99	3.36
query35	3.34	3.32	3.34
query36	0.55	0.44	0.43
query37	0.09	0.06	0.06
query38	0.05	0.03	0.03
query39	0.04	0.02	0.03
query40	0.17	0.14	0.14
query41	0.08	0.03	0.03
query42	0.03	0.02	0.02
query43	0.05	0.03	0.03
Total cold run time: 95.41 s
Total hot run time: 24.44 s

@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.

Reviewed all six changed files at e145fed, including FE creation/persistence/transport, both BE posting writers and query paths, and affected tests. One new P2 performance regression is reported inline: permitted token-expanding custom normalizers lose SNII phrase-index acceleration when positions are omitted.

The existing normalizer-scoring concern still applies to SNII: the helper now enables scoring, but docs-only writers omit positions/norms and collection-statistics admission rejects them. This is already covered and is not reposted. The prior singleton-normalizer phrase gate is fixed on this head; mixed upgraded-FE/old-BE normalizer capability concerns remain covered by the existing phrase/scoring threads. The prior TOKENIZE and BE-fixture concerns no longer apply because the physical omitted-property default remains false. All existing findings are P2; there are no existing P0/P1 IDs to carry forward.

Critical checkpoint conclusions:

Checkpoint Conclusion
Goal and evidence Exact-index defaults and legacy omission are handled locally; custom-normalizer position requirements and known SNII scoring remain problematic. Added tests establish metadata/helper choices only.
Scope and clarity Changes are focused on defaults, capability selection and related tests.
Concurrency and locks No new threads, shared mutations, locks or lock-order changes.
Lifecycle and initialization No new resource ownership, release ordering or static initialization.
Configuration No new process configuration; explicit existing index properties remain authoritative.
Compatibility Legacy omission and persisted explicit values retain physical interpretation; mixed-version normalizer caveats are described above.
Thrift/protobuf IDs No protocol definitions, IDs, enum values or required fields changed.
Parallel paths CREATE TABLE/ADD/CREATE INDEX, local/cloud property transport, both scoring overloads and both posting writers were traced.
Conditions and comments The new universal single-term normalizer assumption is false for allowed expanding filters; see inline finding. Explicit true/false handling is preserved.
Test coverage FE/BE unit additions cover defaults and helper dispatch; end-to-end split-normalizer phrases/scoring and mixed-version queries are missing. No tests were executed.
Expected results The two changed SHOW INDEX strings match sorted parser=none properties. No .out changes or additional stale metadata expectations were identified.
Observability Existing status/fallback paths remain; no new distributed operation requiring new metrics. The inline regression can silently lose index acceleration.
Persistence and transactions Existing Gson serialization and full property-map transport remain; no journal/transaction-ordering change.
Writes and crash behavior Posting detail changes for newly created indexes; no new rowset, visibility, delete-bitmap, segment-ID, atomicity or crash-recovery mechanism.
FE-BE variables No new field; generic map conversion preserves omitted versus explicit values.
Performance and memory Bounded metadata lookups add no material allocation/hot-loop concern; the inline finding proves a selective phrase-to-row-scan regression.
Other module checks No new heavy project-header edge, PCH/template/unity/gate change, ownership or error-propagation issue identified. No build-artifact ABI validation was performed.

No additional user focus was supplied. Review was static only, as required: no builds, tests or source modifications. The inline finding is established by SNII control flow; no particular external CLucene runtime outcome is claimed. All changed files, risk focuses and candidates have explicit dispositions; no unresolved candidate remains.

Review converged after 2 round(s).

static bool is_normalizer_only(const std::map<std::string, std::string>& properties) {
const auto normalizer = properties.find(INVERTED_INDEX_NORMALIZER_NAME_KEY);
const auto analyzer = properties.find(INVERTED_INDEX_ANALYZER_NAME_KEY);
// A normalizer keeps the whole value as one term, so phrase queries need no positions.

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.

[P2] Retain positions for token-expanding custom normalizers

A normalizer property does not guarantee one output term: CREATE INVERTED INDEX NORMALIZER split_norm PROPERTIES("token_filter"="word_delimiter") passes FE validation and turns foo-bar into foo and bar. With the new FE default, an SNII index using this normalizer stores no positions, but this predicate admits v MATCH_PHRASE 'foo-bar'. Lowering retains a two-slot phrase, SniiIndexSource::open_terms(..., positions=true, ...) returns NotSupported, and SegmentIterator falls back to row evaluation. Previously this query used the positional index; now selective phrase queries scan the data. Preserve positions for normalizers that can expand tokens, or enforce a single-token invariant before choosing docs-only storage, and cover this with a real postings test. This differs from the existing lowercase-normalizer thread: its singleton gate is fixed, while this permitted multi-token case needs physical positions.

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17629	5636	5545	5545
q2	2160	315	277	277
q3	10172	1391	845	845
q4	4691	471	344	344
q5	7434	796	532	532
q6	186	189	158	158
q7	749	808	598	598
q8	9243	1340	1078	1078
q9	5571	4264	4279	4264
q10	6824	1296	1027	1027
q11	431	253	236	236
q12	638	411	282	282
q13	18087	3511	3032	3032
q14	275	272	249	249
q15	q16	741	729	671	671
q17	1190	746	572	572
q18	7083	6246	6172	6172
q19	1113	944	614	614
q20	386	334	227	227
q21	5026	2254	2363	2254
q22	361	307	263	263
Total cold run time: 99990 ms
Total hot run time: 29240 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	6155	6157	6146	6146
q2	677	576	537	537
q3	5210	5402	4919	4919
q4	2048	2112	1598	1598
q5	5280	5137	6038	5137
q6	247	194	160	160
q7	2361	2043	1900	1900
q8	3200	2801	2722	2722
q9	8080	8002	8249	8002
q10	3768	3717	3328	3328
q11	644	457	410	410
q12	730	736	543	543
q13	3576	3864	3196	3196
q14	316	310	296	296
q15	q16	716	731	692	692
q17	8322	7471	6870	6870
q18	13210	12265	13039	12265
q19	934	789	790	789
q20	2195	2175	1909	1909
q21	5958	4777	4852	4777
q22	491	462	436	436
Total cold run time: 74118 ms
Total hot run time: 66632 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 152009 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 e145fedbed78b1ebe14ef682a206010d8ee6bf3c, data reload: false

query5	4307	627	461	461
query6	439	221	201	201
query7	4859	459	238	238
query8	328	185	174	174
query9	9068	4036	4003	4003
query10	494	315	264	264
query11	6000	3779	3565	3565
query12	151	93	89	89
query13	1306	452	371	371
query14	6064	4015	3731	3731
query14_1	3517	3478	3521	3478
query15	218	206	194	194
query16	966	444	417	417
query17	837	644	551	551
query18	2417	429	315	315
query19	204	167	143	143
query20	86	84	84	84
query21	211	143	114	114
query22	13007	12969	12800	12800
query23	13529	12749	12438	12438
query23_1	12546	12541	12598	12541
query24	7130	835	474	474
query24_1	473	472	475	472
query25	543	420	371	371
query26	1295	264	140	140
query27	2749	435	264	264
query28	4641	1944	1946	1944
query29	1592	610	452	452
query30	292	216	180	180
query31	997	922	790	790
query32	148	86	90	86
query33	502	300	235	235
query34	961	857	495	495
query35	763	761	659	659
query36	855	811	760	760
query37	130	97	84	84
query38	1824	1725	1711	1711
query39	791	799	740	740
query39_1	723	736	742	736
query40	215	111	93	93
query41	64	61	65	61
query42	88	81	81	81
query43	368	385	339	339
query44	1282	690	697	690
query45	180	168	159	159
query46	845	968	562	562
query47	2946	3030	2951	2951
query48	298	325	220	220
query49	569	402	290	290
query50	671	275	217	217
query51	10420	10395	10563	10395
query52	80	88	69	69
query53	181	203	150	150
query54	243	213	177	177
query55	70	68	63	63
query56	217	234	199	199
query57	1621	1588	1536	1536
query58	291	296	253	253
query59	2090	2136	1943	1943
query60	297	243	222	222
query61	148	144	148	144
query62	389	334	285	285
query63	192	160	169	160
query64	2777	955	759	759
query65	3290	3215	3234	3215
query66	1768	414	304	304
query67	20208	20260	19973	19973
query68	2894	992	604	604
query69	384	289	240	240
query70	879	805	833	805
query71	296	233	208	208
query72	2802	2604	2252	2252
query73	515	519	296	296
query74	4583	4466	4313	4313
query75	2350	2301	1945	1945
query76	2287	1015	615	615
query77	342	370	277	277
query78	9648	9675	9119	9119
query79	994	847	510	510
query80	532	409	355	355
query81	521	321	265	265
query82	579	134	113	113
query83	300	192	174	174
query84	314	122	97	97
query85	834	432	368	368
query86	328	238	223	223
query87	2023	1966	1840	1840
query88	3655	2703	2688	2688
query89	346	291	254	254
query90	1924	176	182	176
query91	178	151	124	124
query92	100	84	79	79
query93	1034	1008	565	565
query94	442	306	248	248
query95	584	336	328	328
query96	665	523	238	238
query97	2417	2409	2296	2296
query98	161	151	153	151
query99	784	763	639	639
Total cold run time: 234234 ms
Total hot run time: 152009 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.10	0.05	0.05
query3	0.26	0.12	0.09
query4	1.62	0.13	0.13
query5	0.24	0.25	0.25
query6	1.17	0.68	0.69
query7	0.04	0.01	0.01
query8	0.05	0.04	0.03
query9	0.41	0.36	0.34
query10	0.58	0.60	0.63
query11	0.20	0.15	0.13
query12	0.18	0.15	0.14
query13	0.49	0.49	0.51
query14	1.01	1.01	1.02
query15	0.63	0.60	0.60
query16	0.36	0.36	0.36
query17	1.20	1.13	1.16
query18	0.22	0.20	0.21
query19	2.09	1.92	1.93
query20	0.02	0.01	0.01
query21	15.48	0.25	0.14
query22	4.38	0.05	0.06
query23	16.33	0.32	0.12
query24	3.01	0.57	1.21
query25	0.12	0.08	0.05
query26	1.84	0.23	0.15
query27	0.05	0.04	0.03
query28	3.19	0.63	0.40
query29	12.51	4.26	3.37
query30	0.28	0.14	0.13
query31	2.77	0.57	0.34
query32	3.23	0.61	0.52
query33	3.28	3.26	3.24
query34	15.44	4.03	3.42
query35	3.34	3.35	3.35
query36	0.57	0.46	0.44
query37	0.09	0.07	0.06
query38	0.06	0.04	0.03
query39	0.04	0.02	0.02
query40	0.17	0.14	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.04	0.03	0.04
Total cold run time: 97.23 s
Total hot run time: 24.84 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (14/14) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 66.34% (31693/47772)
Line Coverage 51.08% (333627/653109)
Region Coverage 46.43% (269526/580550)
Branch Coverage 48.15% (126932/263620)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (14/14) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.17% (35728/46295)
Line Coverage 62.40% (403183/646133)
Region Coverage 58.83% (340292/578462)
Branch Coverage 59.76% (157169/262979)

### What problem does this PR solve?

Issue Number: None

Related PR: apache#69199

Problem Summary: A custom normalizer can split one value into multiple terms, so omitting phrase positions by default can turn indexed phrase queries into scans. A built-in lowercase normalizer emits one term, but its docs-only postings have no frequencies or norms, which caused scoring statistics collection to reject the index. Keep positions for custom normalizers and use unit frequency and document length when scoring built-in lowercase postings without positions or norms.

### Release note

Custom normalizers retain phrase positions by default. Built-in lowercase normalizer indexes keep phrase matching and positive relevance scores with docs-only postings.

### Check List (For Author)

- Test: FE unit tests for normalizer defaults and policy binding; BE unit tests for SNII postings, collection statistics, reader capabilities, and missing norms; C++ format and build hygiene checks.
- Behavior changed: Yes. Custom normalizers retain positions by default, and docs-only lowercase postings remain eligible for scoring.
- Does this need documentation: No
@airborne12

Copy link
Copy Markdown
Member Author

run buildall

@airborne12

Copy link
Copy Markdown
Member Author

/review

@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 all 14 changed paths at 4a10ba3 found no new substantiated defect. The main reviewer, FE reviewer, BE reviewer, and separate scoring-risk reviewer completed the full scope; all three subagents returned NO_NEW_VALUABLE_FINDINGS. The review converged after one round, with every candidate and initial risk given an evidence-backed disposition.

Reviewed flow: DDL validation → persisted Index properties → create/alter task transfer → physical SNII/CLucene writers → MATCH/SEARCH lowering → collection statistics/postings cursors → BM25 scoring and pruning.

Critical checkpoint conclusions:

  1. Goal and evidence: exact and builtin lowercase indexes can omit positions; parsers/analyzers and custom normalizers retain their appropriate defaults. The changed tests cover metadata defaults, policy shadows, physical SNII statistics, missing norms, and actual split-normalizer positions. This is static evidence; no runtime correctness claim is made.
  2. Scope: the changes are focused on defaults, capability admission, corresponding scoring support, and tests. No unrelated functional change was found.
  3. Concurrency and locking: statement-local maps and query-local statistics retain existing ownership; policy lookups use existing read locks. No new thread, shared state, lock ordering, or heavy operation under a new lock was introduced.
  4. Lifecycle and memory: no new significant allocation, resource owner, cache-handle lifetime, or cross-translation-unit initializer was introduced. The test policy-manager fixture restores the prior pointer using the adjacent pattern.
  5. Configuration: no new configuration item. Explicit support_phrase=true/false remains authoritative; existing norms and variant skip-norm settings reach the changed admission logic.
  6. Compatibility: stored explicit properties survive replay and cloning, and the physical omitted=false default remains unchanged. The supported BE-before-FE upgrade order preserves the old-FE/new-BE window. No persisted format definition changes.
  7. Thrift/protobuf identifiers: no definitions changed; cross-branch identifier comparison is not applicable.
  8. Parallel paths: CREATE TABLE, ADD/CREATE INDEX late-default write-back, MATCH/SEARCH singleton phrase lowering, both scoring-enum overloads, SNII/CLucene, variant inheritance, and TOKENIZE were traced. Direct and typed ARRAY normalizers are rejected; dynamic array inheritance removes the normalizer.
  9. Conditional checks and errors: policy-aware validation keeps custom lowercase shadows out of the absent-property shortcut; singleton lowering avoids physical position requests. Status/Result failures still propagate. No new silent invariant failure was found.
  10. Test coverage: all changed FE/BE tests and the metadata regression were inspected. Full scalar ranking and real CLucene end-to-end execution were not performed; the added tests do not independently prove those complete runtime flows. No builds, tests, source changes, or submodule initialization were performed in this review.
  11. Expected results: the SHOW INDEX expectation correctly removes only the unnecessary support_phrase default for parser=none. No .out/golden file changed.
  12. Observability: existing property display, unsupported errors, and collection-statistics logs cover the changed local paths; no missing critical new metric was identified.
  13. Transactions and persistence: existing Gson/journal/property-transfer paths preserve the metadata map. Transaction visibility, visible versions, delete bitmaps, and failover state machines are unchanged.
  14. Data writes and atomicity: physical position selection still follows stored metadata; no new transactional or crash-recovery branch was introduced.
  15. FE–BE variables: no new protocol variable. The shared converter transfers the same stored properties on create and alter.
  16. Performance: singleton queries avoid positional I/O; docs-only unknown block bounds use conservative BM25 saturation bounds. Pinned CLucene source confirms absent frequency buffers correctly select unit frequency. No new superlinear work or substantial memory-accounting issue was found.
  17. Other concerns and user focus: no additional user focus was supplied. No remaining unresolved candidate or uncovered changed path remains.

Existing review context was independently revalidated without reposting: 4294093294 (normalizer phrase admission), 4294093301 (normalizer scoring), 4294093310 (TOKENIZE output), 4294093318 (default-sensitive BE fixtures), and 4295209408 (token-expanding custom-normalizer positions) are addressed for their reported triggers at this head. All existing findings are P2; there are no existing P0/P1 findings to carry forward.

Remaining limits: this review is static. Physically normless CLucene uses the documented doc_count token-count estimate, so NULL/empty-heavy collections can score differently from SNII's physical token totals; numerical cross-format equality is not claimed for that newly admitted approximate path. Ordinary CLucene retains norms. This explicit approximation was not treated as a separate verified regression.

Review converged after 1 round(s).

@airborne12

Copy link
Copy Markdown
Member Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17596	5570	5594	5570
q2	2090	343	245	245
q3	10247	1412	849	849
q4	4686	471	340	340
q5	7428	802	526	526
q6	189	184	157	157
q7	744	816	604	604
q8	9241	1357	1157	1157
q9	5559	4292	4316	4292
q10	6811	1305	1019	1019
q11	433	253	238	238
q12	635	393	283	283
q13	18103	3603	2969	2969
q14	281	273	260	260
q15	q16	726	722	682	682
q17	1179	764	583	583
q18	6980	6259	6199	6199
q19	1127	964	614	614
q20	395	345	227	227
q21	4525	2228	2381	2228
q22	357	309	273	273
Total cold run time: 99332 ms
Total hot run time: 29315 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	6187	6085	6133	6085
q2	690	588	558	558
q3	5180	5361	4996	4996
q4	2030	2180	1547	1547
q5	5297	5151	5943	5151
q6	251	199	154	154
q7	2322	2066	1848	1848
q8	3217	2828	2806	2806
q9	8050	8039	7905	7905
q10	4046	3706	3348	3348
q11	628	454	428	428
q12	699	729	510	510
q13	3646	3951	3218	3218
q14	309	319	285	285
q15	q16	726	694	628	628
q17	8321	7099	6821	6821
q18	13131	12241	13105	12241
q19	930	805	826	805
q20	2178	2171	1917	1917
q21	5979	4799	4909	4799
q22	497	494	431	431
Total cold run time: 74314 ms
Total hot run time: 66481 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 151917 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 4a10ba340a6f6841e123afd8304e884b85a7f2f6, data reload: false

query5	4316	580	439	439
query6	424	221	206	206
query7	4809	441	224	224
query8	343	193	151	151
query9	8767	3947	3921	3921
query10	454	312	257	257
query11	6044	3797	3516	3516
query12	147	103	84	84
query13	1251	451	315	315
query14	6108	4060	3759	3759
query14_1	3478	3501	3456	3456
query15	210	197	185	185
query16	939	446	418	418
query17	855	665	555	555
query18	2422	433	322	322
query19	208	165	138	138
query20	86	87	82	82
query21	208	136	112	112
query22	13091	13082	12903	12903
query23	13410	12662	12439	12439
query23_1	12630	12566	12512	12512
query24	7102	850	474	474
query24_1	471	487	468	468
query25	532	418	367	367
query26	1294	265	141	141
query27	2744	442	271	271
query28	4612	1922	1957	1922
query29	1568	606	460	460
query30	300	244	188	188
query31	1017	925	807	807
query32	150	97	94	94
query33	512	308	255	255
query34	888	849	499	499
query35	747	762	657	657
query36	832	794	734	734
query37	143	104	90	90
query38	1793	1771	1680	1680
query39	788	803	759	759
query39_1	733	735	753	735
query40	242	129	99	99
query41	72	71	69	69
query42	98	84	86	84
query43	375	392	333	333
query44	1321	697	713	697
query45	190	178	167	167
query46	864	964	568	568
query47	3004	2940	2919	2919
query48	305	316	266	266
query49	573	393	299	299
query50	663	277	219	219
query51	10409	10268	10186	10186
query52	79	79	73	73
query53	181	201	154	154
query54	247	190	178	178
query55	72	69	65	65
query56	237	213	208	208
query57	1598	1598	1554	1554
query58	284	269	250	250
query59	2107	2149	1984	1984
query60	292	243	228	228
query61	153	149	147	147
query62	382	337	287	287
query63	196	157	164	157
query64	2722	977	808	808
query65	3299	3238	3222	3222
query66	1768	415	298	298
query67	20060	20204	20018	20018
query68	2930	998	620	620
query69	390	284	248	248
query70	896	763	786	763
query71	290	233	206	206
query72	2810	2596	2278	2278
query73	513	518	287	287
query74	4575	4479	4297	4297
query75	2334	2299	1943	1943
query76	2287	1027	602	602
query77	351	362	277	277
query78	9940	9740	9166	9166
query79	1016	851	512	512
query80	1111	449	342	342
query81	573	335	276	276
query82	573	140	110	110
query83	307	196	182	182
query84	314	121	102	102
query85	846	430	384	384
query86	351	239	233	233
query87	2015	1962	1816	1816
query88	3579	2664	2659	2659
query89	337	300	260	260
query90	1815	186	177	177
query91	161	157	133	133
query92	103	90	86	86
query93	974	998	574	574
query94	526	297	271	271
query95	578	399	320	320
query96	660	518	243	243
query97	2428	2428	2291	2291
query98	160	151	165	151
query99	778	765	645	645
Total cold run time: 234606 ms
Total hot run time: 151917 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.10	0.05	0.05
query3	0.26	0.11	0.12
query4	1.62	0.12	0.12
query5	0.25	0.24	0.24
query6	1.17	0.68	0.68
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.40	0.36	0.34
query10	0.60	0.58	0.61
query11	0.21	0.15	0.14
query12	0.18	0.15	0.14
query13	0.51	0.51	0.50
query14	1.04	1.04	1.03
query15	0.68	0.64	0.64
query16	0.36	0.35	0.32
query17	1.21	1.18	1.28
query18	0.22	0.21	0.20
query19	2.13	2.00	1.90
query20	0.02	0.01	0.02
query21	15.53	0.26	0.15
query22	4.48	0.05	0.06
query23	16.29	0.32	0.12
query24	2.71	0.63	0.81
query25	0.11	0.06	0.04
query26	1.79	0.24	0.15
query27	0.04	0.03	0.05
query28	2.61	0.65	0.42
query29	12.50	4.17	3.31
query30	0.28	0.14	0.13
query31	2.77	0.59	0.33
query32	3.23	0.62	0.52
query33	3.10	3.19	3.23
query34	15.53	3.98	3.39
query35	3.34	3.32	3.34
query36	0.55	0.47	0.42
query37	0.10	0.06	0.07
query38	0.04	0.04	0.03
query39	0.04	0.03	0.03
query40	0.18	0.15	0.14
query41	0.09	0.03	0.03
query42	0.03	0.03	0.03
query43	0.04	0.03	0.03
Total cold run time: 96.44 s
Total hot run time: 24.78 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 0.54% (5/926) 🎉
Increment coverage report
Complete coverage report

### What problem does this PR solve?

Issue Number: None

Related PR: apache#69199

Problem Summary: The missing-norm fallback treated a stored zero norm as an absent norm, changing existing CLucene query scores and failing BE unit tests. An empty norms span already identifies postings without stored norms. Preserve stored norm bytes while retaining the unit fallback for absent norms.

### Release note

None

### Check List (For Author)

- Test: Header hygiene and clang-format checks passed. Local BE UT compilation was blocked by a missing libpaimon_c.a in the third-party installation.
- Behavior changed: Yes. Restore the prior interpretation of stored zero norms.
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: None

Related PR: apache#69199

Problem Summary: CLucene supplies zero-filled norm arrays for fields that omit norms. The shared postings API must distinguish those placeholders from stored norm bytes. Pass the physical norm capability from each reader partition and leave the norm span empty when norms are absent, so scoring uses the unit fallback. Preserve stored norm bytes and update query expectations for no-norm fields.

### Release note

Queries over CLucene indexes without norms use the unit norm for scoring.

### Check List (For Author)

- Test: Header hygiene and clang-format checks passed. Local BE UT compilation was blocked by a missing libpaimon_c.a in the third-party installation.
- Behavior changed: Yes. Scoring over CLucene indexes without norms uses the unit norm.
- Does this need documentation: No
@airborne12

Copy link
Copy Markdown
Member Author

run buildall

@airborne12

Copy link
Copy Markdown
Member Author

/review

airborne12 added a commit that referenced this pull request Oct 18, 2026
### What problem does this PR solve?

Issue Number: CIR-22263

Problem Summary: SNII streamed compaction rejects compatible docs-only
(T1) postings when the destination does not request phrase positions,
then rebuilds those indexes from raw columns. This PR merges compatible
T1 postings directly and retains raw rebuild for mixed or incompatible
sources. Gram sources also retain raw rebuild because they can omit
common postings and need their gram scheme preserved.

Cloud compaction can assign nonzero physical destination segment IDs.
The streamed SNII path now looks up its destination writer by physical
ID, matching the existing CLucene path; looking it up by ordinal caused
a BE crash in Cloud P0.

This removes one repeated fallback seen in the CIR logs. The main T2
merge hotspot for tokenized indexes remains a separate performance
question. Phrase-position defaults are handled in #69199.

### Release note

Compatible docs-only SNII indexes use streamed compaction.

### Check List (For Author)

- Test
- [x] Unit Test: 17 SNII eligibility tests and the nonzero-segment-ID
compaction test passed on the updated commit; 18 compaction-focused BE
tests passed before the follow-up fix.
- [x] Regression test: `test_storage_format_snii` passed on the combined
source tree before this PR was split.
- [x] Manual test: complete FE and BE builds and local health checks
passed before the split.
- Behavior changed:
- [x] Yes. Compatible T1 postings are streamed; gram sources rebuild
from raw columns.
- Does this need documentation?
    - [x] No.

The complete PR diff passes the English description check and
clang-format 16 on the merged target tree. The updated commit is
awaiting remote CI.

@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.

No new actionable findings for head 47b7eac4b28bc1e9cce14ea2fbd46da0fbb587d7. The review converged in 1 round: the FE reviewer, BE reviewer, and separate risk reviewer all returned NO_NEW_VALUABLE_FINDINGS against the same empty inline set. Main-agent verification covered all 17 changed paths (12 BE, 4 FE, 1 regression) and resolved every initial risk with evidence.

The reviewed flow was:

DDL validation / policy resolution -> persisted index properties
  -> CLucene or SNII writer capabilities -> MATCH/SEARCH index selection
  -> phrase lowering / collection statistics -> postings cursor -> BM25

Required checkpoint conclusions:

  1. Goal and evidence: FE retains positions for tokenized/custom-normalized indexes and defaults exact/builtin-lowercase indexes to docs-only. Singleton lowercase phrase/scoring capability follows the writer and reader contracts. Added FE defaults/shadow-policy tests, physical SNII writer/statistics tests, and CLucene cursor expectations support these paths; runtime behavior was not executed in this review.
  2. Scope and reuse: production changes are confined to default selection, shared capability checks, scoring admission and missing-norm signaling; existing policy-resolution and property-transport mechanisms are reused.
  3. Concurrency and locks: no new threads or lock order. FE mutates statement-local property maps using established policy-manager locking; the added BE flag is cursor-local. No new heavy operation under a shared lock was found.
  4. Lifecycle and initialization: iterator ownership and borrowed position-pointer lifetimes are preserved through partition moves. The test policy-manager fixture restores its previous pointer after writer destruction. No new static-initialization dependency or memory-owning production object is added.
  5. Configuration: no new runtime configuration. Explicit support_phrase values remain authoritative; existing norms and variant skip-norm policy still determine physical norms.
  6. Compatibility: existing explicit property values survive Gson read/write, replay, clone and wire conversion; BE still treats an omitted phrase flag as false for legacy postings and TOKENIZE. No new storage-format or symbol contract was found. The supported upgrade sequence installs BEs before FEs. Official upgrade order.
  7. Thrift/protobuf: no definitions, field IDs or enum values change.
  8. Parallel paths: CREATE TABLE, ADD INDEX and standalone CREATE INDEX propagation were checked; both phrase gates, both scoring overloads, CLucene and SNII paths, and MATCH/SEARCH collection consumers were traced.
  9. Conditions and errors: custom policies that shadow lowercase receive explicit positional capability. Singleton phrase/prefix/edge lowering requires no positions. ARRAY validation, VARIANT array property stripping and analyzer-key mismatch prevent the suspected multivalue bypass. No newly ignored error/status was found.
  10. Test coverage: positive/negative defaults, explicit overrides, mixed builtin spelling, legacy custom shadows, physical split-normalizer positions, docs-only scoring statistics and absent/stored-zero norms are covered by inspected tests. Full end-to-end normalized phrase/min-score execution remains unvalidated; no builds or tests were permitted.
  11. Test results: the changed parser=none metadata strings follow the constructor defaults. No-norm fixture score expectations correctly use encoded length 1; a real stored zero norm remains zero. No .out file changes are included.
  12. Observability: existing statistics logging and failure context remain available; no new distributed operation or critical metrics requirement was identified.
  13. Transactions and persistence: selected defaults are persisted through existing index metadata paths. Transaction visibility, visible versions and delete bitmaps are unaffected; no new journal/failover boundary is introduced.
  14. Data writes and crash atomicity: no production writer transaction/publication or atomic-finish path changes. No new crash leak or partially published state was identified.
  15. FE-BE propagation: no new transmitted variable; existing property maps carry the selected normalizer and phrase flags through both wire converters.
  16. Performance: policy work is limited to DDL; missing-norm capability is read per term/physical partition, not per document. No new large allocation or redundant row scan was found. Missing-norm block bounds remain conservative for unit document length.
  17. Additional-risk recheck: pinned CLucene dependency behavior was inspected read-only without initializing submodules. NULL/empty values, legacy names, VARIANT arrays, partition switches, absent frequency buffers and pruning were rechecked. Normless CLucene deliberately uses a document-count estimate for token totals; exact numerical parity with norm-bearing or SNII indexes is not established by this review. No unresolved actionable candidate or uncovered changed path remains.

All five existing P2 threads were independently rechecked and are addressed on this head; none is reposted:

  • 4294093294: singleton lowercase phrase gates now admit the position-free lowering.
  • 4294093301: lowercase MATCH scoring now passes both capability checks and collection admission.
  • 4294093310: omitted phrase support remains false in the shared parser helper, preserving TOKENIZE output.
  • 4294093318: that omitted=false behavior preserves the named existing BE fixture expectations.
  • 4295209408: custom normalizers retain positions, including exact builtin-name shadows; the writer test verifies split terms and positions.

No existing P0/P1 blocker was found. The user focus file contained no additional focus points. This was a static-only review: no source modifications, builds or test runs. Convergence describes completed review coverage and candidate disposition; it does not claim runtime validation.

Review converged after 1 round(s).

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17596	5717	5531	5531
q2	2154	325	257	257
q3	10189	1431	840	840
q4	4679	478	336	336
q5	7428	826	528	528
q6	194	185	152	152
q7	759	800	608	608
q8	9269	1360	1067	1067
q9	5524	4293	4249	4249
q10	6815	1298	996	996
q11	438	259	235	235
q12	630	399	298	298
q13	18099	3717	3039	3039
q14	279	274	252	252
q15	q16	749	731	668	668
q17	1213	750	585	585
q18	7067	6242	6181	6181
q19	1097	955	608	608
q20	395	334	222	222
q21	5461	2476	2639	2476
q22	405	353	304	304
Total cold run time: 100440 ms
Total hot run time: 29432 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	6570	6650	6518	6518
q2	735	626	616	616
q3	5589	5774	5390	5390
q4	2029	2137	1571	1571
q5	5688	5698	5624	5624
q6	245	186	144	144
q7	2256	2039	1849	1849
q8	3094	2700	2779	2700
q9	8067	7894	7575	7575
q10	3662	3615	3269	3269
q11	603	422	372	372
q12	652	691	556	556
q13	3249	3620	2977	2977
q14	303	286	267	267
q15	q16	661	682	619	619
q17	7854	6944	6909	6909
q18	13101	12288	13002	12288
q19	921	809	789	789
q20	2182	2145	1929	1929
q21	5916	4745	4874	4745
q22	511	471	400	400
Total cold run time: 73888 ms
Total hot run time: 67107 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 152778 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 47b7eac4b28bc1e9cce14ea2fbd46da0fbb587d7, data reload: false

query5	4310	616	471	471
query6	428	210	190	190
query7	4801	454	232	232
query8	318	170	155	155
query9	8781	4020	4001	4001
query10	463	306	261	261
query11	5975	3789	3526	3526
query12	143	93	87	87
query13	1259	464	333	333
query14	6141	4042	3785	3785
query14_1	3591	3513	3545	3513
query15	210	202	186	186
query16	963	446	410	410
query17	858	653	544	544
query18	2420	429	312	312
query19	202	170	145	145
query20	88	90	83	83
query21	231	139	108	108
query22	13127	13060	12921	12921
query23	13376	12934	12518	12518
query23_1	12561	12529	12571	12529
query24	7117	869	474	474
query24_1	470	478	485	478
query25	549	435	369	369
query26	1270	260	141	141
query27	2768	439	288	288
query28	4612	1956	1949	1949
query29	1582	598	465	465
query30	300	225	192	192
query31	1011	927	809	809
query32	146	96	95	95
query33	530	312	257	257
query34	919	860	504	504
query35	751	774	664	664
query36	834	822	701	701
query37	137	106	94	94
query38	1795	1780	1709	1709
query39	788	804	756	756
query39_1	744	743	752	743
query40	230	117	96	96
query41	73	69	68	68
query42	87	85	83	83
query43	370	389	328	328
query44	1346	712	715	712
query45	185	181	174	174
query46	838	991	589	589
query47	3053	3032	2876	2876
query48	305	355	216	216
query49	569	397	294	294
query50	690	280	219	219
query51	10709	10557	10714	10557
query52	80	78	73	73
query53	201	215	153	153
query54	228	200	200	200
query55	72	71	64	64
query56	234	224	218	218
query57	1606	1600	1578	1578
query58	290	252	261	252
query59	2112	2128	1979	1979
query60	280	242	226	226
query61	142	146	150	146
query62	397	338	287	287
query63	208	168	178	168
query64	2745	987	779	779
query65	3332	3257	3256	3256
query66	1777	439	308	308
query67	20348	20264	19965	19965
query68	2945	1018	621	621
query69	378	297	252	252
query70	865	791	786	786
query71	287	228	210	210
query72	2761	2861	2309	2309
query73	540	536	292	292
query74	4580	4480	4300	4300
query75	2339	2297	1933	1933
query76	2332	1022	613	613
query77	345	373	283	283
query78	9946	9672	9137	9137
query79	1028	865	524	524
query80	1220	452	336	336
query81	586	318	268	268
query82	606	137	106	106
query83	307	199	188	188
query84	324	129	101	101
query85	868	435	371	371
query86	411	245	221	221
query87	2021	1976	1826	1826
query88	3641	2703	2694	2694
query89	353	298	267	267
query90	1862	191	184	184
query91	160	152	133	133
query92	100	90	83	83
query93	1065	993	572	572
query94	644	323	276	276
query95	582	346	313	313
query96	674	523	230	230
query97	2464	2442	2327	2327
query98	167	152	144	144
query99	776	783	636	636
Total cold run time: 236055 ms
Total hot run time: 152778 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.10	0.05	0.05
query3	0.27	0.12	0.11
query4	1.61	0.12	0.12
query5	0.29	0.23	0.25
query6	1.17	0.67	0.68
query7	0.04	0.01	0.01
query8	0.06	0.03	0.03
query9	0.42	0.34	0.36
query10	0.59	0.60	0.60
query11	0.21	0.15	0.14
query12	0.18	0.15	0.14
query13	0.50	0.50	0.50
query14	1.03	1.02	1.02
query15	0.62	0.61	0.64
query16	0.32	0.33	0.35
query17	1.12	1.13	1.14
query18	0.24	0.20	0.21
query19	2.03	2.02	2.03
query20	0.02	0.02	0.01
query21	15.49	0.29	0.14
query22	4.29	0.05	0.05
query23	16.25	0.32	0.12
query24	3.26	0.55	0.40
query25	0.13	0.06	0.04
query26	1.03	0.26	0.15
query27	0.05	0.04	0.04
query28	1.69	0.68	0.41
query29	12.56	4.25	3.33
query30	0.28	0.14	0.13
query31	2.78	0.58	0.35
query32	3.22	0.63	0.53
query33	3.13	3.29	3.24
query34	15.53	4.07	3.38
query35	3.34	3.35	3.34
query36	0.57	0.49	0.48
query37	0.10	0.06	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.17	0.14	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: 94.94 s
Total hot run time: 24.74 s

yiguolei pushed a commit that referenced this pull request Oct 18, 2026
### What problem does this PR solve?

Issue Number: CIR-22263

Related PR: #69199

Problem Summary: FE persists `support_phrase=true` for new tokenized
indexes and leaves exact indexes docs-only. FE and BE retain the
docs-only interpretation of an omitted property on older persisted
analyzer indexes, preserving their postings and `TOKENIZE` output.
Built-in `lowercase` normalization emits one term, so phrase predicates
can use docs-only postings and BM25 can use unit frequency and document
length. Custom normalizers can emit multiple terms, so new indexes
retain positions by default, including when a legacy custom policy
shadows the built-in name.

This is the `branch-4.1` adaptation of #69199. It uses the branch's
CLucene scoring path and the keyword-index scoring support already
merged into `branch-4.1`. This branch has no SNII streamed compaction
path from #69192, so this PR does not eliminate docs-only rebuilds or
the tokenized T2 merge hotspot in CIR-22263.

### Release note

New exact and built-in lowercase-normalized indexes default to docs-only
postings. Custom normalizers retain phrase positions by default.
Lowercase-normalized indexes retain phrase matching and relevance
scoring without stored positions.

### Check List (For Author)

- Test
- [x] Unit Test: two targeted FE default and policy-binding tests and
eight BE collection-statistics, query-scoring, postings, and reader
tests on the current target branch.
- [x] Complete PR diff passed the English description check,
clang-format 16, and Checkstyle on the merged target tree.
- Behavior changed:
- [x] Yes. Exact indexes omit positions by default; custom normalizers
retain positions; built-in lowercase normalization keeps phrase and
scoring behavior with docs-only postings.
- Does this need documentation?
    - [x] No.

Remote checks on the updated head are pending.
@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 71.43% (5/7) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 71.43% (5/7) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (14/14) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 77.05% (35693/46327)
Line Coverage 62.27% (402679/646619)
Region Coverage 58.67% (339671/578905)
Branch Coverage 59.68% (157113/263275)

@eldenmoon eldenmoon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@airborne12
airborne12 merged commit 7a9d745 into apache:master Oct 18, 2026
36 checks passed
@airborne12
airborne12 deleted the cir22263-phrase-default-master-v2 branch October 18, 2026 12:33
airborne12 added a commit to airborne12/apache-doris that referenced this pull request Oct 19, 2026
…69192)

### What problem does this PR solve?

Issue Number: CIR-22263

Problem Summary: SNII streamed compaction rejects compatible docs-only
(T1) postings when the destination does not request phrase positions,
then rebuilds those indexes from raw columns. This PR merges compatible
T1 postings directly and retains raw rebuild for mixed or incompatible
sources. Gram sources also retain raw rebuild because they can omit
common postings and need their gram scheme preserved.

Cloud compaction can assign nonzero physical destination segment IDs.
The streamed SNII path now looks up its destination writer by physical
ID, matching the existing CLucene path; looking it up by ordinal caused
a BE crash in Cloud P0.

This removes one repeated fallback seen in the CIR logs. The main T2
merge hotspot for tokenized indexes remains a separate performance
question. Phrase-position defaults are handled in apache#69199.

### Release note

Compatible docs-only SNII indexes use streamed compaction.

### Check List (For Author)

- Test
- [x] Unit Test: 17 SNII eligibility tests and the nonzero-segment-ID
compaction test passed on the updated commit; 18 compaction-focused BE
tests passed before the follow-up fix.
- [x] Regression test: `test_storage_format_snii` passed on the combined
source tree before this PR was split.
- [x] Manual test: complete FE and BE builds and local health checks
passed before the split.
- Behavior changed:
- [x] Yes. Compatible T1 postings are streamed; gram sources rebuild
from raw columns.
- Does this need documentation?
    - [x] No.

The complete PR diff passes the English description check and
clang-format 16 on the merged target tree. The updated commit is
awaiting remote CI.

(cherry picked from commit 8db0540)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants