matrixorigin / matrixorigin/matrixone
[Bug]: HNSW rewrite returns different values for the same l2_distance expression
- Dominant language
- Go
- Stars
- 1.9k
- Forks
- 311
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 768
Description
## 问题
HNSW rewrite 只把直接投影和排序中的 `l2_distance` 替换成索引返回的 score;同一个函数嵌套在 `ROUND()`、比较表达式或 WHERE 中时仍按标量函数计算。两个计算路径的精度不同,因此同一行、同一 SQL 表达式会在一条结果中同时得到互相矛盾的值。
这不是 ANN 召回差异:HNSW 与精确路径选择了相同的行,错误发生在返回值和表达式一致性上。
## 环境
- MatrixOne `main`
- commit: `01d60e1c4ded1b0f3fc1a4ecd75ce54e95e23b90`
- 本地 2 CN / 1 TN / 1 LogService
- 同步 HNSW,`vector_l2_ops`
## 最小复现
```sql
set experimental_hnsw_index=1;
create table t(id bigint primary key,v vecf32(4));
insert into t values
(29,'[2.189,-1.676,2.08,-1.578]'),
(99,'[10,10,10,10]');
create index hx using hnsw on t(v)
op_type 'vector_l2_ops'
m=16 ef_construction=128 ef_search=128 max_index_capacity=75;
alter table t alter reindex hx hnsw force_sync;
select id,
l2_distance(v,'[1.125,-0.625,0.375,1.75]') direct_d,
round(l2_distance(v,'[1.125,-0.625,0.375,1.75]'),14) wrapped_d,
l2_distance(v,'[1.125,-0.625,0.375,1.75]') = 4.02731990814209 eq,
l2_distance(v,'[1.125,-0.625,0.375,1.75]') <= 4.02731990814209 le
from t
where l2_distance(v,'[1.125,-0.625,0.375,1.75]') <= 4.02731990814209
order by l2_distance(v,'[1.125,-0.625,0.375,1.75]')
limit 10;
```
## 实际结果
```text
id direct_d wrapped_d eq le
29 4.027320069947357 4.02731990814209 1 1
```
`direct_d` 大于比较边界,但同一个 `l2_distance` 的 `=` 和 `<=` 同时为真。给查询加 `BY RANK WITH OPTION 'mode=force'` 后,直接投影也返回标量值 `4.02731990814209`。
## 预期结果
同一行中语义相同的函数调用必须得到同一个 SQL 值。向量索引可以近似选择候选行,但访问路径不应让直接投影、函数嵌套和谓词分别观察到不同的 `l2_distance` 值。
## 稳定性与控制
- 3 个独立数据库和 HNSW 索引;
- 两个 CN;
- 每个索引、每个 CN 连续执行 3 次;
- 18/18 次均返回上述矛盾结果;
- `mode=force` 18/18 次返回一致的标量值;
- HNSW 与精确路径的行集都只有 `id=29`,排除了召回率或 Top-K tie;
- `EXPLAIN` 确认错误查询使用 `hnsw_search`,WHERE 在回表 scan 上按标量函数执行。
## 定位
`pkg/sql/plan/apply_indices_hnsw.go` 用 HNSW table function 的 `score` 列替换 Top-K 的排序表达式,并通过 `vectorRemapForChildProject` 重映射直接投影;原表上的 residual filter 保留标量 `l2_distance`。`vecf32` 标量实现返回 float32 舍入后的距离,而 HNSW score 路径返回另一精度的值,于是相同函数根据表达式位置产生不同结果。
这与 #29040 不同:#29040 是 IVFFLAT 推送距离范围后改变行成员关系;本问题是 HNSW 选中相同行后,直接投影与同查询内的标量表达式值不一致,修复路径也位于 HNSW rewrite/remap。
Contributor guide
Research direction
Start in pkg/sql/plan/apply_indices_hnsw.go, focusing on the HNSW score replacement and vectorRemapForChildProject. Reproduce the SQL example with experimental_hnsw_index enabled and compare direct projection, ROUND(), predicates, and the force-ranked path. Done means equivalent l2_distance expressions observe one consistent SQL value while HNSW and exact paths retain the same selected rows.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100