Skip to content

[fix](be) Decouple SNII BM25 scoring from CommonGrams - #67134

Open
airborne12 wants to merge 1 commit into
apache:masterfrom
airborne12:fix-snii-scoring-without-common-grams
Open

[fix](be) Decouple SNII BM25 scoring from CommonGrams#67134
airborne12 wants to merge 1 commit into
apache:masterfrom
airborne12:fix-snii-scoring-without-common-grams

Conversation

@airborne12

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: None

Related PR: #66052

Problem Summary: score() over an ordinary analyzed SNII index failed with
"SNII semantic scoring metadata is missing". V1/V2/V3 rank the same index
(regression test_bm25_score.groovy), so SNII was the outlier.

CommonGrams stores gram tokens in its physical postings, so its
SniiStatsPB.sum_total_term_freq and per-document length are not the numbers
BM25 wants; it needs a SEMANTIC view of the collection. That view was introduced
inside the CommonGrams segment metadata, and every downstream capability check
was then written as "does this segment carry CommonGrams metadata" rather than
"does this segment carry scoring data". A phrase-query performance optimization
therefore became a prerequisite for a core feature, in five separate places:
the writer's tier decision, the writer's norms accumulation, the query-side
statistics gate, the per-segment stats provider, and compaction eligibility.

For an index that does NOT use CommonGrams there is no such divergence -- its
physical statistics ARE the semantic ones -- so the fix is to ask the right
question instead of adding a second mechanism. No metadata field and no proto
message is added.

Writer: an index reaches the scoring tier and persists per-document norms when
it is ANALYZED and keeps POSITIONS (_writes_norms()), not when it uses
CommonGrams. ARRAY columns are covered too; CommonGrams rejects ARRAY outright,
so an array text column could previously never be ranked on SNII. The plain
analyzer lane also never counted its tokens -- *semantic_length was only
incremented in the CommonGrams branch -- which is now done in consume_token.

Reader: SniiStatsProvider::open() and resolve_snii_scoring_segment() share
one predicate, "the index persists the BM25 inputs" = scoring tier + positions +
norms. A CommonGrams segment still has its semantic view validated before use.
Two DORIS_CHECKs that would have aborted the BE on the plain shape are removed,
and the term-df bound now uses the physical document count (proved equal to
scoring_doc_count for CommonGrams by validate_snii_scoring_metadata). The
analyzer fingerprint is a CommonGrams identity, so it is only compared when the
segment records one -- V1/V2/V3 perform no such check.

avgdl now divides by doc_count on both shapes. Norms are written for every
row (a null row contributes encode_norm(0)), so the denominator must span the
same rows the lengths do; the CommonGrams branch already used every row.

Compaction: a third streamed-merge kind, kPlainT3 -- scoring tier with norms and
no CommonGrams metadata. The existing norms remap is reused; only the metadata
seed and the semantic token count stay CommonGrams-only.

Segments written before this change keep working for filtering and are refused
for scoring rather than ranked at a guessed document length: they carry neither
norms nor freq regions, and a silent unit-length fallback would rank them on a
different scale from their siblings in the same table. Ranking them requires
rebuilding the index.

SIZE IMPACT, measured by section on a harmonic-df corpus (1k/20k/200k docs): an
analyzed index grows 30-38% against current master, roughly two thirds freq
region and one third norms. freq is a required BM25 input -- V1/V2/V3 and the
shipped SelectDB branch have always written it, and master dropped it for plain
positions indexes (G16-c) on the premise that such an index never scores, which
is the very defect fixed here. Measured against those two baselines the net
addition is norms alone, about +7% at 200k documents. The DICT region -- read on
every term lookup -- grows 13.2% at 1k docs, 8.0% at 20k and 0.0% at 200k, as
frequent postings move from inline to windowed and take their freq bytes out of
the dictionary block.

The two SniiWriterGoldenBytes digests for analyzed lanes were RE-HARVESTED. The
image changed for four reasons, not one: the freq region, the tier-dependent
dict entry layout, the norms region, and index_config. kGoldenKeywordDocsOnly is
unchanged, pinning that the analyzed lane is the only one affected.

Release note

Fix score() on SNII inverted indexes built with an ordinary analyzer: BM25
ranking no longer requires a CommonGrams analyzer. Indexes written before this
change must be rebuilt to be ranked.

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • SniiPlainIndexScoring.* (4 cases: scalar, ARRAY, null runs, no-positions guard)
      • CollectionStatisticsTest.SniiPlainAnalyzedIndexCollectsScoringStatistics
        enters through CollectionStatistics::collect and was verified RED against
        unmodified production code with the reported error text.
      • Full inverted-index suites: 3212 tests, 0 failures
      • ./run-be-ut.sh -j 160 --run
  • Behavior changed:

    • No.
    • Yes. An analyzed SNII index with phrase positions is now written at the
      scoring tier with norms and freq, and can be ranked. Segments written
      before this change are refused for scoring instead of erroring later.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

### What problem does this PR solve?

Issue Number: None

Related PR: apache#66052

Problem Summary: `score()` over an ordinary analyzed SNII index failed with
"SNII semantic scoring metadata is missing". V1/V2/V3 rank the same index
(regression test_bm25_score.groovy), so SNII was the outlier.

CommonGrams stores gram tokens in its physical postings, so its
SniiStatsPB.sum_total_term_freq and per-document length are not the numbers
BM25 wants; it needs a SEMANTIC view of the collection. That view was introduced
inside the CommonGrams segment metadata, and every downstream capability check
was then written as "does this segment carry CommonGrams metadata" rather than
"does this segment carry scoring data". A phrase-query performance optimization
therefore became a prerequisite for a core feature, in five separate places:
the writer's tier decision, the writer's norms accumulation, the query-side
statistics gate, the per-segment stats provider, and compaction eligibility.

For an index that does NOT use CommonGrams there is no such divergence -- its
physical statistics ARE the semantic ones -- so the fix is to ask the right
question instead of adding a second mechanism. No metadata field and no proto
message is added.

Writer: an index reaches the scoring tier and persists per-document norms when
it is ANALYZED and keeps POSITIONS (`_writes_norms()`), not when it uses
CommonGrams. ARRAY columns are covered too; CommonGrams rejects ARRAY outright,
so an array text column could previously never be ranked on SNII. The plain
analyzer lane also never counted its tokens -- `*semantic_length` was only
incremented in the CommonGrams branch -- which is now done in `consume_token`.

Reader: `SniiStatsProvider::open()` and `resolve_snii_scoring_segment()` share
one predicate, "the index persists the BM25 inputs" = scoring tier + positions +
norms. A CommonGrams segment still has its semantic view validated before use.
Two DORIS_CHECKs that would have aborted the BE on the plain shape are removed,
and the term-df bound now uses the physical document count (proved equal to
scoring_doc_count for CommonGrams by validate_snii_scoring_metadata). The
analyzer fingerprint is a CommonGrams identity, so it is only compared when the
segment records one -- V1/V2/V3 perform no such check.

`avgdl` now divides by doc_count on both shapes. Norms are written for every
row (a null row contributes encode_norm(0)), so the denominator must span the
same rows the lengths do; the CommonGrams branch already used every row.

Compaction: a third streamed-merge kind, kPlainT3 -- scoring tier with norms and
no CommonGrams metadata. The existing norms remap is reused; only the metadata
seed and the semantic token count stay CommonGrams-only.

Segments written before this change keep working for filtering and are refused
for scoring rather than ranked at a guessed document length: they carry neither
norms nor freq regions, and a silent unit-length fallback would rank them on a
different scale from their siblings in the same table. Ranking them requires
rebuilding the index.

SIZE IMPACT, measured by section on a harmonic-df corpus (1k/20k/200k docs): an
analyzed index grows 30-38% against current master, roughly two thirds freq
region and one third norms. freq is a required BM25 input -- V1/V2/V3 and the
shipped SelectDB branch have always written it, and master dropped it for plain
positions indexes (G16-c) on the premise that such an index never scores, which
is the very defect fixed here. Measured against those two baselines the net
addition is norms alone, about +7% at 200k documents. The DICT region -- read on
every term lookup -- grows 13.2% at 1k docs, 8.0% at 20k and 0.0% at 200k, as
frequent postings move from inline to windowed and take their freq bytes out of
the dictionary block.

The two SniiWriterGoldenBytes digests for analyzed lanes were RE-HARVESTED. The
image changed for four reasons, not one: the freq region, the tier-dependent
dict entry layout, the norms region, and index_config. kGoldenKeywordDocsOnly is
unchanged, pinning that the analyzed lane is the only one affected.

### Release note

Fix score() on SNII inverted indexes built with an ordinary analyzer: BM25
ranking no longer requires a CommonGrams analyzer. Indexes written before this
change must be rebuilt to be ranked.

### Check List (For Author)

- Test
    - [ ] Regression test
    - [x] Unit Test
    - [ ] Manual test (add detailed scripts or steps below)
    - [ ] No need to test or manual test. Explain why:
        - SniiPlainIndexScoring.* (4 cases: scalar, ARRAY, null runs, no-positions guard)
        - CollectionStatisticsTest.SniiPlainAnalyzedIndexCollectsScoringStatistics
          enters through CollectionStatistics::collect and was verified RED against
          unmodified production code with the reported error text.
        - Full inverted-index suites: 3212 tests, 0 failures
        - ./run-be-ut.sh -j 160 --run

- Behavior changed:
    - [ ] No.
    - [x] Yes. An analyzed SNII index with phrase positions is now written at the
      scoring tier with norms and freq, and can be ranked. Segments written
      before this change are refused for scoring instead of erroring later.

- Does this need documentation?
    - [x] No.
    - [ ] Yes.

### Check List (For Reviewer who merge this PR)

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label
@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

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17566	3092	3086	3086
q2	2193	253	212	212
q3	10155	923	517	517
q4	4673	250	201	201
q5	7674	578	378	378
q6	142	117	95	95
q7	552	508	385	385
q8	9234	910	909	909
q9	3507	2443	2439	2439
q10	6492	864	720	720
q11	398	201	180	180
q12	609	264	208	208
q13	18109	1542	1172	1172
q14	160	151	140	140
q15	q16	434	396	370	370
q17	1407	906	819	819
q18	3071	2278	2252	2252
q19	1114	948	801	801
q20	366	286	199	199
q21	5380	1673	1728	1673
q22	328	268	231	231
Total cold run time: 93564 ms
Total hot run time: 16987 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3556	3460	3428	3428
q2	516	408	375	375
q3	2244	2425	2191	2191
q4	1214	1194	916	916
q5	2230	2157	2140	2140
q6	176	130	87	87
q7	1026	917	902	902
q8	1633	1446	1435	1435
q9	3203	3178	3165	3165
q10	1869	1792	1631	1631
q11	356	275	250	250
q12	451	437	352	352
q13	1500	1538	1201	1201
q14	184	181	168	168
q15	q16	398	399	367	367
q17	3669	3355	3269	3269
q18	4941	4460	4859	4460
q19	1131	895	867	867
q20	1007	957	825	825
q21	3820	3088	3206	3088
q22	387	358	328	328
Total cold run time: 35511 ms
Total hot run time: 31445 ms

@hello-stephen

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

query5	4287	407	338	338
query6	389	136	129	129
query7	4949	407	238	238
query8	305	140	119	119
query9	8670	2929	2917	2917
query10	374	221	174	174
query11	5392	1070	948	948
query12	126	72	76	72
query13	1212	439	346	346
query14	6109	2207	2108	2108
query14_1	1998	1972	1981	1972
query15	174	127	115	115
query16	914	368	356	356
query17	807	455	366	366
query18	2327	337	240	240
query19	170	139	109	109
query20	75	70	75	70
query21	206	102	88	88
query22	5522	5228	5217	5217
query23	6640	6283	6197	6197
query23_1	6041	6121	5956	5956
query24	7301	1108	777	777
query24_1	754	805	791	791
query25	437	324	275	275
query26	1238	231	136	136
query27	2773	422	249	249
query28	4712	1507	1499	1499
query29	952	438	371	371
query30	260	148	133	133
query31	826	397	335	335
query32	133	79	76	76
query33	473	237	189	189
query34	996	834	477	477
query35	412	409	349	349
query36	577	556	525	525
query37	121	82	71	71
query38	1025	859	815	815
query39	487	509	497	497
query39_1	457	464	466	464
query40	208	94	82	82
query41	62	57	60	57
query42	82	74	74	74
query43	247	241	219	219
query44	1014	555	560	555
query45	146	102	96	96
query46	798	886	515	515
query47	759	770	707	707
query48	306	287	232	232
query49	523	231	183	183
query50	766	274	196	196
query51	8231	8048	8318	8048
query52	68	66	58	58
query53	196	204	141	141
query54	227	297	166	166
query55	75	61	54	54
query56	196	162	159	159
query57	697	654	650	650
query58	198	158	162	158
query59	1233	1261	1126	1126
query60	239	192	159	159
query61	132	120	116	116
query62	376	211	180	180
query63	171	140	151	140
query64	2689	697	560	560
query65	1652	1624	1646	1624
query66	1840	266	211	211
query67	9930	9820	9724	9724
query68	2844	1178	731	731
query69	339	223	196	196
query70	674	604	628	604
query71	253	173	168	168
query72	2324	1914	1556	1556
query73	679	635	340	340
query74	1845	1215	1130	1130
query75	1191	1101	960	960
query76	2298	742	530	530
query77	247	258	217	217
query78	4079	3670	3194	3194
query79	2732	855	556	556
query80	1595	330	267	267
query81	491	160	133	133
query82	628	122	99	99
query83	272	214	191	191
query84	291	107	88	88
query85	777	353	295	295
query86	402	179	173	173
query87	1053	977	924	924
query88	2766	2151	2105	2105
query89	279	194	177	177
query90	2018	130	129	129
query91	133	123	102	102
query92	81	67	71	67
query93	1493	1145	746	746
query94	616	277	222	222
query95	544	325	231	231
query96	823	604	267	267
query97	1083	1090	1022	1022
query98	171	138	129	129
query99	446	347	308	308
Total cold run time: 178572 ms
Total hot run time: 82379 ms

@hello-stephen

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

query1	0.01	0.00	0.00
query2	0.07	0.04	0.03
query3	0.24	0.09	0.12
query4	1.60	0.10	0.10
query5	0.18	0.15	0.15
query6	1.27	0.67	0.72
query7	0.04	0.01	0.00
query8	0.04	0.03	0.03
query9	0.29	0.22	0.22
query10	0.35	0.36	0.34
query11	0.16	0.12	0.11
query12	0.14	0.11	0.12
query13	0.29	0.29	0.32
query14	0.46	0.46	0.45
query15	0.38	0.36	0.35
query16	0.20	0.22	0.23
query17	0.73	0.71	0.69
query18	0.17	0.16	0.17
query19	1.20	1.14	1.22
query20	0.01	0.01	0.01
query21	15.42	0.17	0.11
query22	5.03	0.04	0.04
query23	16.17	0.27	0.09
query24	3.23	0.32	0.27
query25	0.13	0.04	0.03
query26	0.75	0.16	0.13
query27	0.02	0.03	0.04
query28	3.65	0.57	0.28
query29	12.44	3.19	2.58
query30	0.25	0.11	0.12
query31	2.76	0.38	0.18
query32	3.51	0.30	0.23
query33	1.48	1.44	1.41
query34	15.46	2.16	1.80
query35	1.76	1.72	1.72
query36	0.47	0.30	0.28
query37	0.06	0.04	0.04
query38	0.04	0.03	0.03
query39	0.03	0.02	0.03
query40	0.12	0.08	0.07
query41	0.08	0.02	0.03
query42	0.03	0.02	0.03
query43	0.03	0.03	0.03
Total cold run time: 90.75 s
Total hot run time: 14.68 s

@hello-stephen

Copy link
Copy Markdown
Contributor

BE UT Coverage Report

Increment line coverage 100.00% (4/4) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 62.52% (29190/46692)
Line Coverage 47.55% (305531/642602)
Region Coverage 43.23% (246847/571035)
Branch Coverage 44.79% (114911/256582)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 100.00% (4/4) 🎉

Increment coverage report
Complete coverage report

Category Coverage
Function Coverage 76.03% (34384/45225)
Line Coverage 60.98% (387390/635257)
Region Coverage 57.22% (325524/568871)
Branch Coverage 58.00% (148408/255857)

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