airborne12 opened a new pull request, #67538:
URL: https://github.com/apache/doris/pull/67538

   ### What problem does this PR solve?
   
   Issue Number: close #xxx
   
   Related PR: #xxx
   
   Problem Summary:
   
   `LIKE '%literal%'` and `REGEXP` on text columns always scan every row today. 
This PR adds an **approximate gram index** for them, built on the existing 
inverted-index machinery, so that a regex/LIKE first prunes rows through the 
index and the original expression is then re-evaluated only on the surviving 
candidates. Results are always identical to the non-indexed evaluation: the 
index only ever produces a superset of the matching rows, and every index-side 
failure degrades to "no acceleration".
   
   No new index type, tokenizer type or `parser` value is introduced. The 
feature is switched on by giving the built-in `ngram` tokenizer a `mode`:
   
   ```sql
   CREATE INVERTED INDEX TOKENIZER gram_sparse_tok
       PROPERTIES ("type" = "ngram", "mode" = "sparse", "min_gram" = "3", 
"max_gram" = "16", "density" = "0.25");
   CREATE INVERTED INDEX ANALYZER gram_sparse PROPERTIES ("tokenizer" = 
"gram_sparse_tok");
   CREATE TABLE logs (id BIGINT, msg STRING,
       INDEX idx_msg (msg) USING INVERTED PROPERTIES ("analyzer" = 
"gram_sparse"))
       DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 8
       PROPERTIES ("replication_num" = "1", "inverted_index_storage_format" = 
"SNII");
   SELECT count(*) FROM logs WHERE msg REGEXP 'rpc error: code = 
(Unavailable|Internal)';
   ```
   
   How it works (BE):
   
   1. **Gram library** (`be/src/storage/index/inverted/gram/`): `GramScheme` 
(parameters), `GramExtractor` (ASCII runs are cut into byte grams — dense 
sliding window or sparse content-defined-boundary grams, `density` selects 
boundary probability — while every non-ASCII code point becomes its own 1-gram, 
so CJK text works without a language tokenizer), `GramQuery` (AND/OR/ALL/NONE 
tree with simplification and a text serialization), a RE2-subset regex parser, 
and `RegexGramCompiler` (Cox 2012 style derivation of the grams a match *must* 
contain, for both REGEXP and LIKE). A differential fuzz test checks the 
compiler against RE2 over hundreds of thousands of random pattern/text pairs 
and asserts it never drops a matching row.
   2. **Write path**: `NGramTokenizerFactory` returns a `GramTokenizer` when 
`mode` is set; gram-family analyzers are recognised by the SNII writer, which 
forces a docs-only index for them (`support_phrase` is ignored). Analyzers that 
carry token filters or char filters are deliberately treated as *not* 
gram-family, because their terms would no longer correspond to the raw column 
value.
   3. **Query path**: a new `InvertedIndexQueryType::GRAM_BOOLEAN_QUERY` 
evaluates a serialized `GramQuery` on the SNII index (df-first AND with early 
exit, OR union). `FunctionLike` / `FunctionRegexpLike` implement 
`evaluate_inverted_index`: constant pattern → compile → gram query → candidate 
bitmap flagged as **approximate**. Approximate results go into a separate table 
in `IndexExecContext`; `SegmentIterator` only intersects them into 
`_row_bitmap` when the conjunct root is the function itself, never marks the 
column as "index evaluated", and keeps the conjunct for re-evaluation. `NOT 
LIKE` / `NOT REGEXP` / OR-nested predicates are therefore never pruned. 
Push-down happens only for SNII readers; CLucene-format readers reject the new 
query type.
   4. **Metadata**: `SniiCoreMetadataPB.gram_scheme` is reserved 
(encoded/decoded, not yet written) for a later per-segment adaptive mode.
   
   FE:
   
   - `NGramTokenizerValidator` accepts and validates `mode` 
(`auto|sparse|dense`), `density` `[0.001, 1]`, `stop_gram_df` `[0, 1]`, 
`lower_case`, and the gram-family ranges of `min_gram`/`max_gram` (`mode` 
absent keeps the legacy behaviour byte for byte).
   - `IndexPolicyMgr` rejects gram tokenizers combined with token filters (use 
the tokenizer's own `lower_case=true` instead of a `lowercase` filter, because 
folding must happen before gram boundaries are computed).
   - `InvertedIndexUtil` requires `inverted_index_storage_format = SNII` for 
gram-family indexes, rejects `support_phrase = true` and index-level char 
filters, and defaults `support_phrase` to `false` (also for `CREATE INDEX` / 
`ALTER TABLE ADD INDEX`).
   
   Observability: `RowsGramIndexFiltered` and `GramIndexCandidateRows` in the 
scan profile; BE config `enable_gram_index_regexp` (default `true`) is the kill 
switch.
   
   Known limitations of this first step: `mode=auto` currently behaves as 
`sparse`; `stop_gram_df` is validated and persisted but does not prune 
high-frequency grams yet; `(?i)` patterns and LIKE with a custom `ESCAPE` are 
evaluated without the index; the speed-up depends on pattern selectivity and on 
how much of the column would otherwise be read (the index cuts scanned bytes by 
orders of magnitude on selective patterns; on a fully page-cached single node 
the wall-clock gain is smaller).
   
   ### Release note
   
   Regex / LIKE predicates on text columns can be accelerated by an inverted 
index built with the `ngram` tokenizer in `mode=sparse|dense` (SNII storage 
format). Query results are unchanged; unsupported patterns fall back to the 
normal evaluation.
   
   ### Check List (For Author)
   
   - Test <!-- At least one of them must be included. -->
       - [x] Regression test (`inverted_index_p0/gram/test_gram_regexp_like`: 
139 REGEXP/RLIKE/LIKE/NOT/compound queries compared with the index enabled and 
disabled, before and after DELETE, across two rowsets and three coexisting 
indexes on one column; profile asserts the gram index pruned rows)
       - [x] Unit Test (BE: gram scheme/extractor/query/regex AST/compiler + 
differential fuzz vs RE2, tokenizer, SNII writer gram family, core metadata, 
gram boolean query incl. an end-to-end test over a real SNII segment vs brute 
force, like/regexp index evaluation, IndexExecContext isolation; FE: 
PolicyValidatorTests, GramDdlValidationTest, InvertedIndexPropertiesTest)
       - [ ] Manual test (add detailed scripts or steps below)
       - [ ] No need to test or manual test. Explain why:
           - [ ] This is a refactor/code format and no logic has been changed.
           - [ ] Previous test can cover this change.
           - [ ] No code files have been changed.
           - [ ] Other reason <!-- Add your reason?  -->
   
   - Behavior changed:
       - [ ] No.
       - [x] Yes. New `ngram` tokenizer properties (`mode`, `density`, 
`stop_gram_df`, `lower_case`); new `GRAM_BOOLEAN_QUERY` inverted index query 
type; `LIKE`/`REGEXP` may use a gram-family SNII index (results unchanged); new 
BE config `enable_gram_index_regexp`; new profile counters 
`RowsGramIndexFiltered` / `GramIndexCandidateRows`. Existing `ngram` tokenizers 
without `mode` are unaffected.
   
   - Does this need documentation?
       - [ ] No.
       - [x] Yes. <!-- Add document PR link here. eg: 
https://github.com/apache/doris-website/pull/1214 -->
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label <!-- Add branch pick label that this PR should 
merge into -->
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   
   https://claude.ai/code/session_01METuP3aVRn8dfCU62PivnF
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to