Problem
Doris may fall back to Flat Search even when a metric-compatible Lance vector index exists on the queried column.
Lance permits multiple named logical vector indexes on the same vector column. For example:
vec_l2 column=vector, metric=L2
vec_cosine column=vector, metric=COSINE
For a vector_search() request with metric=cosine, Doris should be able to select vec_cosine. Currently, if vec_l2 appears first in the index metadata returned to the FE, Doris selects all segments belonging to vec_l2, checks their metric afterwards, and falls back to fragment-based search when the metric does not match. It does not continue looking for vec_cosine.
The query result remains correct because Lance executes Flat Search with the requested metric, but the available ANN index is silently missed. Query latency and resource usage can therefore depend on index metadata ordering.
Current implementation
LanceScanNode.selectIndexSegments() first locks onto the first logical index covering the vector field:
if (selectedIndexName == null) {
selectedIndexName = segment.getIndexName();
}
if (selectedIndexName.equals(segment.getIndexName())) {
selectedSegments.add(segment);
}
Only after that selection does createIndexSegmentSplits() call metricMatches():
List<LanceIndexSegmentInfo> matchingSegments = selectIndexSegments(
metadata.getIndexSegments(), vectorFieldId);
if (matchingSegments.isEmpty() || !metricMatches(vectorSearchParam, matchingSegments)) {
return Optional.empty();
}
Relevant code:
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/source/LanceScanNode.java
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/LanceMetadataLoader.java
The FE already loads the information needed to make a metric-aware decision:
- logical index name
- indexed field IDs
- physical segment UUIDs
- index metric
- fragment coverage
This is not a Lance index-format limitation. Doris can select a logical index by sending that index's physical segment UUIDs to the BE; no new Lance index_name scanner parameter is required for automatic metric-aware selection.
Expected behavior
When use_index=true, Doris should:
- Group physical index segments by logical index name.
- Keep logical indexes covering the requested vector field.
- Resolve the requested metric using Doris semantics (
L2 when the query metric is omitted or DEFAULT).
- Select a logical index whose segments use the requested metric.
- Route only that logical index's segment UUIDs to the BE.
- Fall back to Flat Search only when no compatible logical index can be planned safely.
If more than one logical index matches both the field and metric, Doris should either define a deterministic documented policy or later expose an optional index_name query property. Explicit user selection is a separate enhancement; it is not required to fix the missed compatible-index case.
Legacy index metadata without a metric or fragment bitmap should continue to use the existing conservative fallback unless a safe compatibility path is defined.
Reproduction
- Create a Lance dataset with one vector column.
- Create two named logical indexes on that column, one using L2 and one using cosine, ensuring the L2 index is returned first by
describeIndices().
- Run Doris
vector_search() with metric=cosine and use_index=true.
- Inspect
EXPLAIN.
Current behavior:
lanceSearchIndexSegments=0
and the query falls back to fragment search, even though the cosine index exists.
Expected behavior:
lanceSearchIndexSegments>0
with only the vec_cosine physical segments assigned to indexed splits.
Acceptance criteria
Related
Problem
Doris may fall back to Flat Search even when a metric-compatible Lance vector index exists on the queried column.
Lance permits multiple named logical vector indexes on the same vector column. For example:
For a
vector_search()request withmetric=cosine, Doris should be able to selectvec_cosine. Currently, ifvec_l2appears first in the index metadata returned to the FE, Doris selects all segments belonging tovec_l2, checks their metric afterwards, and falls back to fragment-based search when the metric does not match. It does not continue looking forvec_cosine.The query result remains correct because Lance executes Flat Search with the requested metric, but the available ANN index is silently missed. Query latency and resource usage can therefore depend on index metadata ordering.
Current implementation
LanceScanNode.selectIndexSegments()first locks onto the first logical index covering the vector field:Only after that selection does
createIndexSegmentSplits()callmetricMatches():Relevant code:
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/source/LanceScanNode.javafe/fe-core/src/main/java/org/apache/doris/datasource/lance/LanceMetadataLoader.javaThe FE already loads the information needed to make a metric-aware decision:
This is not a Lance index-format limitation. Doris can select a logical index by sending that index's physical segment UUIDs to the BE; no new Lance
index_namescanner parameter is required for automatic metric-aware selection.Expected behavior
When
use_index=true, Doris should:L2when the query metric is omitted orDEFAULT).If more than one logical index matches both the field and metric, Doris should either define a deterministic documented policy or later expose an optional
index_namequery property. Explicit user selection is a separate enhancement; it is not required to fix the missed compatible-index case.Legacy index metadata without a metric or fragment bitmap should continue to use the existing conservative fallback unless a safe compatibility path is defined.
Reproduction
describeIndices().vector_search()withmetric=cosineanduse_index=true.EXPLAIN.Current behavior:
and the query falls back to fragment search, even though the cosine index exists.
Expected behavior:
with only the
vec_cosinephysical segments assigned to indexed splits.Acceptance criteria
DEFAULTmetric follows the documented Doris L2 default.EXPLAINsegment planning and annprobes/efdiscriminator rather than only comparing indexed and flat results.Related