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

   ### What problem does this PR solve?
   
   Related PR: #69332 (lance-c dependency upgrade)
   
   Ordinary SQL against Lance external tables does not use the existing Lance 
search path for vector Top-N, and `score()` cannot produce Lance BM25 
relevance. Users currently need explicit `vector_search()` / 
`full_text_search()` TVFs.
   
   This PR adds automatic rewrites on branch-4.1, reusing the existing 
fixed-snapshot search planner, index-segment execution, global candidate merge, 
and deferred payload reads. It upgrades lance-c to 
`4fafcd64525b274b113208390bbcca86bbbc093b`, as in #69332, and directly uses 
`lance_scanner_set_row_addr_allowlist()` introduced by lance-c #96.
   
   ### SQL examples and behavior changes
   
   The examples assume a Lance table with a three-dimensional Float32 embedding 
and a supported full-text index on `body`.
   
   #### 1. Approximate vector Top-N
   
   ```sql
   SELECT doc_id,
          l2_distance_approximate(embedding, [0.1, 0.2, 0.3]) AS distance
   FROM lance_catalog.docs.documents
   WHERE tenant_id = 7
   ORDER BY distance ASC
   LIMIT 10 OFFSET 5;
   ```
   
   Before: ordinary Lance scanning evaluates the scalar distance and Doris 
performs Top-N; this SQL does not request native Lance vector search.
   
   After: the optimizer creates a native Lance L2 search with the original 
scalar predicate applied before candidate selection. Each search split requests 
15 candidates with offset 0; Doris merges them and applies the final global 
`OFFSET 5 LIMIT 10`. Compatible Lance vector indexes can serve the approximate 
search through the existing search planner. Candidate selection may therefore 
be approximate rather than an exhaustive scalar ranking.
   
   The SQL result remains Euclidean distance: Lance's squared L2 result is 
converted with `CAST(sqrt(_distance) AS FLOAT)`.
   
   Inner-product queries use the corresponding descending order:
   
   ```sql
   SELECT doc_id,
          inner_product_approximate(embedding, [0.1, 0.2, 0.3]) AS similarity
   FROM lance_catalog.docs.documents
   WHERE tenant_id = 7
   ORDER BY similarity DESC
   LIMIT 10;
   ```
   
   This becomes native Lance dot search. The projection converts Lance dot 
distance with `1.0f - _distance`, so the SQL output remains an inner-product 
similarity and higher is better.
   
   #### 2. BM25 full-text Top-N
   
   ```sql
   SELECT doc_id, score() AS relevance
   FROM lance_catalog.docs.documents
   WHERE body MATCH_ANY 'database search'
     AND tenant_id = 7
   ORDER BY relevance DESC
   LIMIT 10;
   ```
   
   Before: `score()` on this Lance external scan is unsupported.
   
   After: one top-level MATCH predicate becomes a native prepared Lance 
full-text query, `score()` reads its generated BM25 score, and Doris merges the 
highest-scoring candidates. The native prepared query preserves the 
dataset-wide scoring context across index segments and row-ID filtering.
   
   - `MATCH_ANY` maps to a match query with OR.
   - `MATCH_ALL` maps to a match query with AND.
   - `MATCH_PHRASE` maps to a phrase query using the stored analyzer and 
position information.
   - A lower score bound such as `AND score() >= 0.5` is supported as a 
residual over the generated score. Upper bounds and other score predicates are 
rejected because filtering a pruned Top-K with those predicates could lose 
valid results.
   
   #### 3. Scalar WHERE to native row-ID prefilter
   
   For the scalar predicates in the examples, the execution path is:
   
   ```text
   Bound Lance snapshot + validated Substrait predicate
       -> row-ID-only native scan (no user-column projection)
       -> native UINT64 _rowid allowlist
       -> vector / prepared full-text search
       -> Doris global Top-N and final projection
   ```
   
   The candidate scanner and search scanner use the same pinned dataset 
snapshot. A query-local cache shares the candidate list across search readers 
using the dataset URI, version, and filter as its key. An empty allowlist 
selects zero rows.
   
   The list is capped at 65,536 IDs. Broader predicates discard the entire 
partial list and retain the complete native Substrait prefilter on the search 
scanner. This bounds the C++ candidate list and preserves correctness. Profile 
counters expose prefilter time, candidate count, cache hits, and fallback count.
   
   ### Scope and compatibility
   
   - Automatic rewriting requires a direct Lance scan, an optional filter, a 
projection, and a single search ordering key with LIMIT. It does not rewrite 
across JOIN/EXISTS, add secondary sort keys, or support the internal-table 
`SEARCH()` DSL.
   - Vector rewriting supports physical Float32 fixed-size-list vectors and 
constant query arrays. Exact `l2_distance()`, reversed vector ordering, 
incompatible vector types, and vector queries with unsupported residual 
predicates keep scalar execution.
   - BM25 requires one supported top-level MATCH on a direct string column, a 
literal query, the stored analyzer, and `ORDER BY score() DESC`. Remaining 
scalar predicates must be fully convertible by the existing Lance predicate 
converter. Unsupported scored-query shapes fail explicitly rather than silently 
selecting incorrect candidates.
   - The rewrite preserves bound snapshot/version metadata and output 
expression IDs, retains column pruning, and disables SQL result caching for 
rewritten searches.
   - Existing explicit search TVF SQL-filter behavior is preserved.
   
   ### Validation
   
   - FE reactor build and the relevant Maven test suite passed; after the final 
compatibility guard, all 126 focused tests passed, including 12 new optimizer 
tests.
   - FE Checkstyle passed.
   - Changed BE production and test translation units passed C++ syntax checks; 
all affected C++ files passed clang-format 16 checks.
   - Dependency download/MD5 and Foyer patch lifecycle tests passed.
   - Rebuilt the exact pinned lance-c revision with the Foyer patch; native 
runtime smoke tests against that library passed for high-bit UINT64 row IDs, 
vector allowlists, empty allowlists, and prepared FTS allowlists with unchanged 
BM25 scores.
   - Added BE tests for query-local cache reuse, empty masks, and the 
broad-filter fallback retaining the nearest row. Added an external-table 
regression suite comparing automatic SQL with explicit TVFs and scalar distance 
values.
   - Full BE unit-test execution and cluster regression execution remain for 
CI; they were not run locally.
   
   ### Release note
   
   Support automatic native Lance search for eligible approximate vector Top-N 
and MATCH/BM25 SQL on Lance external tables.
   
   ### Check List (For Author)
   
   - Test
       - [x] Regression test added (execution pending CI)
       - [x] Unit Test added (FE passed; BE execution pending CI)
       - [x] Manual native API smoke tests
   - Behavior changed:
       - [x] Yes. Eligible approximate vector queries use native search; 
eligible scored MATCH queries become supported, as shown above.
   - Does this need documentation?
       - [x] Yes. The SQL forms and current boundaries are described above; 
user documentation should accompany the feature release.
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label
   


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