This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new f42ebf277ce branch-4.1: [feature](fe) Support ANN indexes on MoW
tables (#67155)
f42ebf277ce is described below
commit f42ebf277cee24f8b00a2e012c028cb33f6f36a2
Author: Jack <[email protected]>
AuthorDate: Thu Aug 27 14:28:35 2026 +0800
branch-4.1: [feature](fe) Support ANN indexes on MoW tables (#67155)
### What problem does this PR solve?
Issue Number: N/A
Related PR: #61994
Problem Summary:
This backports ANN index support for unique-key merge-on-write tables to
branch-4.1. FE validation previously restricted ANN indexes to DUP_KEYS
tables even though merge-on-write scans filter deleted rows before ANN
candidates are evaluated. The change permits ANN indexes only for
UNIQUE_KEYS tables with merge-on-write enabled, preserves rejection for
merge-on-read tables, and covers both the Nereids and legacy branch-4.1
CREATE INDEX validation paths.
### Release note
Support ANN indexes on unique-key merge-on-write tables.
### Check List (For Author)
- Test
- [x] Regression test
- [x] Unit Test
- [ ] 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
- Verification:
-
`ORC_FORMAT_URL=http://archive.apache.org/dist/orc/orc-format-1.0.0/orc-format-1.0.0.tar.gz
./build.sh --be --fe -j 160`
- `./run-fe-ut.sh --run
org.apache.doris.nereids.trees.plans.commands.IndexDefinitionTest,org.apache.doris.alter.SchemaChangeHandlerTest`
- `./run-regression-test.sh --run -d ann_index_p0 -s
ann_index_on_mow,build_ann_index_test,create_ann_index_test,create_tbl_with_ann_index_test
-parallel 4`
- Behavior changed:
- [ ] No.
- [x] Yes.
- 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
Co-authored-by: ivin <[email protected]>
---
.../apache/doris/alter/SchemaChangeHandler.java | 8 ++-
.../java/org/apache/doris/analysis/IndexDef.java | 7 ++-
.../trees/plans/commands/info/IndexDefinition.java | 7 ++-
.../data/ann_index_p0/ann_index_on_mow.out | 31 +++++++++++
.../suites/ann_index_p0/ann_index_on_mow.groovy | 64 ++++++++++++++++++++++
.../ann_index_p0/build_ann_index_test.groovy | 45 +++++++++++++++
.../ann_index_p0/create_ann_index_test.groovy | 63 ++++++++++++++-------
.../create_tbl_with_ann_index_test.groovy | 22 ++++++++
8 files changed, 221 insertions(+), 26 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
index a354af64741..c038fcd0e24 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java
@@ -2806,8 +2806,12 @@ public class SchemaChangeHandler extends AlterHandler {
}
if (indexDef.isAnnIndex()) {
- if (olapTable.getKeysType() != KeysType.DUP_KEYS) {
- throw new AnalysisException("ANN index can only be built on
table with DUP_KEYS");
+ if (olapTable.getKeysType() != KeysType.DUP_KEYS
+ && !(olapTable.getKeysType() == KeysType.UNIQUE_KEYS
+ && olapTable.getEnableUniqueKeyMergeOnWrite())) {
+ throw new AnalysisException(
+ "ANN index can only be built on table with DUP_KEYS or
UNIQUE_KEYS"
+ + " with merge-on-write enabled");
}
AnnIndexPropertiesChecker.checkProperties(indexDef.getProperties());
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/IndexDef.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/IndexDef.java
index d64b65bc64a..abcbe848582 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/IndexDef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/IndexDef.java
@@ -255,8 +255,11 @@ public class IndexDef {
if (!itemType.isFloatingPointType()) {
throw new AnalysisException("ANN index column item type must
be float type");
}
- if (keysType != KeysType.DUP_KEYS) {
- throw new AnalysisException("ANN index can only be used in
DUP_KEYS table");
+ if (keysType != KeysType.DUP_KEYS
+ && !(keysType == KeysType.UNIQUE_KEYS &&
enableUniqueKeyMergeOnWrite)) {
+ throw new AnalysisException(
+ "ANN index can only be used in DUP_KEYS table or
UNIQUE_KEYS table with"
+ + " merge-on-write enabled");
}
if (invertedIndexFileStorageFormat ==
TInvertedIndexFileStorageFormat.V1) {
throw new AnalysisException("ANN index is not supported in
index format V1");
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/IndexDefinition.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/IndexDefinition.java
index 1d151801f92..8ab371db208 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/IndexDefinition.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/IndexDefinition.java
@@ -148,8 +148,11 @@ public class IndexDefinition {
if (!itemType.isFloatType()) {
throw new AnalysisException("ANN index column item type must
be float type, invalid index: " + name);
}
- if (keysType != KeysType.DUP_KEYS) {
- throw new AnalysisException("ANN index can only be used in
DUP_KEYS table");
+ if (keysType != KeysType.DUP_KEYS
+ && !(keysType == KeysType.UNIQUE_KEYS &&
enableUniqueKeyMergeOnWrite)) {
+ throw new AnalysisException(
+ "ANN index can only be used in DUP_KEYS table or
UNIQUE_KEYS table with"
+ + " merge-on-write enabled");
}
return;
}
diff --git a/regression-test/data/ann_index_p0/ann_index_on_mow.out
b/regression-test/data/ann_index_p0/ann_index_on_mow.out
new file mode 100644
index 00000000000..d0e14b53133
--- /dev/null
+++ b/regression-test/data/ann_index_p0/ann_index_on_mow.out
@@ -0,0 +1,31 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !sql_1 --
+1 [1, 2, 3] 11
+2 [4, 5, 6] 22
+3 [7, 8, 9] 33
+
+-- !sql_2 --
+1 0.0
+
+-- !sql_3 --
+1 [10, 20, 30] 111
+2 [40, 50, 60] 222
+3 [70, 80, 90] 333
+
+-- !sql_4 --
+1 0.0
+
+-- !sql_5 --
+1 [10, 20, 30] 111
+2 [40, 50, 60] 222
+3 [70, 80, 90] 333
+
+-- !sql_6 --
+
+-- !sql_7 --
+1 [10, 20, 30] \N 1 4
+2 [40, 50, 60] \N 1 4
+3 [70, 80, 90] \N 1 4
+
+-- !sql_8 --
+
diff --git a/regression-test/suites/ann_index_p0/ann_index_on_mow.groovy
b/regression-test/suites/ann_index_p0/ann_index_on_mow.groovy
new file mode 100644
index 00000000000..a14a116c533
--- /dev/null
+++ b/regression-test/suites/ann_index_p0/ann_index_on_mow.groovy
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("ann_index_on_mow") {
+ sql "drop table if exists ann_index_on_mow"
+ test {
+ sql """
+ CREATE TABLE ann_index_on_mow (
+ id INT NOT NULL COMMENT "",
+ vec ARRAY<FLOAT> NOT NULL COMMENT "",
+ value INT NULL COMMENT "",
+ INDEX ann_idx (vec) USING ANN PROPERTIES(
+ "index_type" = "hnsw",
+ "metric_type" = "l2_distance",
+ "dim" = "3"
+ )
+ ) ENGINE=OLAP
+ UNIQUE KEY(id) COMMENT "OLAP"
+ DISTRIBUTED BY HASH(id) BUCKETS 1
+ PROPERTIES (
+ "replication_num" = "1",
+ "enable_unique_key_merge_on_write" = "true"
+ );
+ """
+ }
+
+ sql "insert into ann_index_on_mow values (1, [1.0, 2.0, 3.0], 11),(2,
[4.0, 5.0, 6.0], 22),(3, [7.0, 8.0, 9.0], 33)"
+
+ qt_sql_1 "select * from ann_index_on_mow order by id"
+
+ qt_sql_2 "select id, l2_distance_approximate(vec, [1.0, 2.0, 3.0]) as dist
from ann_index_on_mow order by dist limit 1;"
+
+ sql "insert into ann_index_on_mow values (1, [10.0, 20.0, 30.0], 111),(2,
[40.0, 50.0, 60.0], 222),(3, [70.0, 80.0, 90.0], 333)"
+
+ qt_sql_3 "select * from ann_index_on_mow order by id"
+
+ qt_sql_4 "select id, l2_distance_approximate(vec, [10.0, 20.0, 30.0]) as
dist from ann_index_on_mow order by dist limit 1;"
+
+ qt_sql_5 "select * from ann_index_on_mow order by id"
+
+ sql "insert into ann_index_on_mow (id, vec, __DORIS_DELETE_SIGN__) values
(1, [10.0, 20.0, 30.0], 1),(2, [40.0, 50.0, 60.0], 1),(3, [70.0, 80.0, 90.0],
1);"
+
+ qt_sql_6 "select * from ann_index_on_mow order by id"
+
+ sql "set show_hidden_columns=true;"
+ qt_sql_7 "select * from ann_index_on_mow order by id"
+ sql "set show_hidden_columns=false;"
+
+ qt_sql_8 "select id, l2_distance_approximate(vec, [10.0, 20.0, 30.0]) as
dist from ann_index_on_mow order by dist limit 1;"
+}
diff --git a/regression-test/suites/ann_index_p0/build_ann_index_test.groovy
b/regression-test/suites/ann_index_p0/build_ann_index_test.groovy
index e8de0d3d2d1..26101a60950 100644
--- a/regression-test/suites/ann_index_p0/build_ann_index_test.groovy
+++ b/regression-test/suites/ann_index_p0/build_ann_index_test.groovy
@@ -109,4 +109,49 @@ suite("build_ann_index_test") {
// BUILD INDEX
sql "BUILD INDEX idx_test_ann ON table_build_ann_index_test;"
wait_for_last_build_index_on_table_finish(tableName, timeout)
+
+
+ // case 2: create and build ann index on mow table
+ sql "drop table if exists table_build_ann_index_on_mow;"
+
+ sql """
+ CREATE TABLE `table_build_ann_index_on_mow` (
+ `id` int NOT NULL COMMENT "",
+ `embedding` array<float> NOT NULL COMMENT ""
+ ) ENGINE=OLAP
+ UNIQUE KEY(`id`) COMMENT "OLAP"
+ DISTRIBUTED BY HASH(`id`) BUCKETS 2
+ PROPERTIES (
+ "replication_num" = "1",
+ "enable_unique_key_merge_on_write" = "true"
+ );
+ """
+
+ sql """
+ INSERT INTO table_build_ann_index_on_mow (id, embedding) VALUES
+ (0, [39.906116, 10.495334, 54.08394, 88.67262, 55.243687, 10.162686,
36.335983, 38.684258]),
+ (1, [62.759315, 97.15586, 25.832521, 39.604908, 88.76715, 72.64085,
9.688437, 17.721428]),
+ (2, [15.447449, 59.7771, 65.54516, 12.973712, 99.685135, 72.080734,
85.71118, 99.35976]),
+ (3, [72.26747, 46.42257, 32.368374, 80.50209, 5.777631, 98.803314,
7.0915947, 68.62693]),
+ (4, [22.098177, 74.10027, 63.634556, 4.710955, 12.405106, 79.39356,
63.014366, 68.67834]),
+ (5, [27.53003, 72.1106, 50.891026, 38.459953, 68.30715, 20.610682,
94.806274, 45.181377]),
+ (6, [77.73215, 64.42907, 71.50025, 43.85641, 94.42648, 50.04773,
65.12575, 68.58207]),
+ (7, [2.1537063, 82.667885, 16.171143, 71.126656, 5.335274, 40.286068,
11.943586, 3.69409]),
+ (8, [54.435013, 56.800594, 59.335514, 55.829235, 85.46627, 33.388138,
11.076194, 20.480877]),
+ (9, [76.197945, 60.623528, 84.229805, 31.652937, 71.82595, 48.04684,
71.29212, 30.282396]);
+ """
+
+ // CREATE INDEX
+ sql """
+ CREATE INDEX idx_test_ann ON table_build_ann_index_on_mow(`embedding`)
USING ANN PROPERTIES(
+ "index_type"="hnsw",
+ "metric_type"="l2_distance",
+ "dim"="8"
+ );
+ """
+ wait_for_latest_op_on_table_finish("table_build_ann_index_on_mow", timeout)
+
+ // BUILD INDEX
+ sql "BUILD INDEX idx_test_ann ON table_build_ann_index_on_mow;"
+ wait_for_last_build_index_on_table_finish("table_build_ann_index_on_mow",
timeout)
}
diff --git a/regression-test/suites/ann_index_p0/create_ann_index_test.groovy
b/regression-test/suites/ann_index_p0/create_ann_index_test.groovy
index 17f542de168..3e32584a378 100644
--- a/regression-test/suites/ann_index_p0/create_ann_index_test.groovy
+++ b/regression-test/suites/ann_index_p0/create_ann_index_test.groovy
@@ -39,6 +39,31 @@ suite("create_ann_index_test") {
);
"""
+ sql "drop table if exists tbl_mor_without_ann"
+ sql """
+ CREATE TABLE `tbl_mor_without_ann` (
+ `id` int NOT NULL COMMENT "",
+ `embedding` array<float> NOT NULL COMMENT ""
+ ) ENGINE=OLAP
+ UNIQUE KEY(`id`) COMMENT "OLAP"
+ DISTRIBUTED BY HASH(`id`) BUCKETS 2
+ PROPERTIES (
+ "replication_num" = "1",
+ "enable_unique_key_merge_on_write" = "false"
+ );
+ """
+
+ test {
+ sql """
+ CREATE INDEX idx_test_ann_on_mor ON tbl_mor_without_ann(`embedding`)
USING ANN PROPERTIES(
+ "index_type"="hnsw",
+ "metric_type"="l2_distance",
+ "dim"="1"
+ );
+ """
+ exception "ANN index can only be built on table with DUP_KEYS or
UNIQUE_KEYS with merge-on-write enabled"
+ }
+
// Test cases for creating tables with ANN indexes
// 1. Case for nullable column
@@ -299,26 +324,24 @@ suite("create_ann_index_test") {
);
"""
- sql "drop table if exists tbl_ann_unique_key"
- test {
- sql """
- CREATE TABLE tbl_ann_unique_key (
- id INT NOT NULL COMMENT "",
- embedding ARRAY<FLOAT> NOT NULL COMMENT "",
- INDEX idx_test_ann (`embedding`) USING ANN PROPERTIES(
- "index_type"="hnsw",
- "metric_type"="inner_product",
- "dim"="128"
- )
- ) ENGINE=OLAP
- UNIQUE KEY(id) COMMENT "OLAP"
- DISTRIBUTED BY HASH(id) BUCKETS 2
- PROPERTIES (
- "replication_num" = "1"
- );
- """
- exception "ANN index can only be used in DUP_KEYS table"
- }
+ sql "drop table if exists tbl_ann_on_mow"
+ sql """
+ CREATE TABLE tbl_ann_on_mow (
+ id INT NOT NULL COMMENT "",
+ embedding ARRAY<FLOAT> NOT NULL COMMENT "",
+ INDEX idx_test_ann (`embedding`) USING ANN PROPERTIES(
+ "index_type"="hnsw",
+ "metric_type"="inner_product",
+ "dim"="128"
+ )
+ ) ENGINE=OLAP
+ UNIQUE KEY(id) COMMENT "OLAP"
+ DISTRIBUTED BY HASH(id) BUCKETS 2
+ PROPERTIES (
+ "replication_num" = "1",
+ "enable_unique_key_merge_on_write" = "true"
+ );
+ """
sql "drop table if exists tbl_efconstruction"
sql """
diff --git
a/regression-test/suites/ann_index_p0/create_tbl_with_ann_index_test.groovy
b/regression-test/suites/ann_index_p0/create_tbl_with_ann_index_test.groovy
index 35725cd4b46..03c897d2760 100644
--- a/regression-test/suites/ann_index_p0/create_tbl_with_ann_index_test.groovy
+++ b/regression-test/suites/ann_index_p0/create_tbl_with_ann_index_test.groovy
@@ -37,6 +37,28 @@ suite("create_tbl_with_ann_index_test") {
"""
}
+ sql "drop table if exists ann_tbl_mor"
+ test {
+ sql """
+ CREATE TABLE ann_tbl_mor (
+ id INT NOT NULL COMMENT "",
+ vec ARRAY<FLOAT> NOT NULL COMMENT "",
+ INDEX ann_idx_mor (vec) USING ANN PROPERTIES(
+ "index_type" = "hnsw",
+ "metric_type" = "l2_distance",
+ "dim" = "128"
+ )
+ ) ENGINE=OLAP
+ UNIQUE KEY(id) COMMENT "OLAP"
+ DISTRIBUTED BY HASH(id) BUCKETS 2
+ PROPERTIES (
+ "replication_num" = "1",
+ "enable_unique_key_merge_on_write" = "false"
+ );
+ """
+ exception "ANN index can only be used in DUP_KEYS table or UNIQUE_KEYS
table with merge-on-write enabled"
+ }
+
sql "drop table if exists ann_tbl2"
test {
sql """
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]