fix: fix index type error for element-level index in struct array (#48183)

issue: https://github.com/milvus-io/milvus/issues/47622

---------

Signed-off-by: SpadeA <tangchenjie1210@gmail.com>
This commit is contained in:
Spade A
2026-03-12 10:19:24 +08:00
committed by GitHub
parent 77829deb3c
commit 076637db5e
2 changed files with 75 additions and 3 deletions
+23 -3
View File
@@ -23,6 +23,7 @@ import (
"github.com/cockroachdb/errors"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
@@ -661,8 +662,20 @@ func checkTrain(ctx context.Context, field *schemapb.FieldSchema, indexParams ma
return fmt.Errorf("invalid index type: %s", indexType)
}
// For ArrayOfVector with non-EmbList metrics (e.g., COSINE, L2, IP), each embedding
// in the array is indexed independently as a regular vector. The index only needs to
// support the element vector type, not the EmbeddingList capability.
// Resolve the effective data type used for index compatibility checks.
effectiveDataType := field.DataType
effectiveElementType := field.ElementType
if typeutil.IsArrayOfVectorType(field.DataType) &&
!funcutil.SliceContain(indexparamcheck.EmbListMetrics, indexParams[common.MetricTypeKey]) {
effectiveDataType = field.ElementType
effectiveElementType = schemapb.DataType_None
}
if typeutil.IsVectorType(field.DataType) && indexType != indexparamcheck.AutoIndex {
exist := CheckVecIndexWithDataTypeExist(indexType, field.DataType, field.ElementType)
exist := CheckVecIndexWithDataTypeExist(indexType, effectiveDataType, effectiveElementType)
if !exist {
return fmt.Errorf("data type %s can't build with this index %s", schemapb.DataType_name[int32(field.GetDataType())], indexType)
}
@@ -676,12 +689,19 @@ func checkTrain(ctx context.Context, field *schemapb.FieldSchema, indexParams ma
}
}
if err := checker.CheckValidDataType(indexType, field); err != nil {
effectiveField := field
if effectiveDataType != field.DataType {
effectiveField = proto.Clone(field).(*schemapb.FieldSchema)
effectiveField.DataType = effectiveDataType
effectiveField.ElementType = effectiveElementType
}
if err := checker.CheckValidDataType(indexType, effectiveField); err != nil {
log.Ctx(ctx).Info("create index with invalid data type", zap.Error(err), zap.String("data_type", field.GetDataType().String()))
return err
}
if err := checker.CheckTrain(field.DataType, field.ElementType, indexParams); err != nil {
if err := checker.CheckTrain(effectiveDataType, effectiveElementType, indexParams); err != nil {
log.Ctx(ctx).Info("create index with invalid parameters", zap.Error(err))
return err
}
+52
View File
@@ -1276,6 +1276,58 @@ func Test_checkEmbeddingListIndex(t *testing.T) {
})
}
func Test_arrayOfVector_nonEmbListMetric_indexCompat(t *testing.T) {
// When an ArrayOfVector field uses a non-EmbList metric (e.g., COSINE),
// each embedding is indexed independently. The index should only need to
// support the element vector type, not the EmbeddingList capability.
// This means indexes like IVF_PQ that don't have the EMB_LIST flag
// should still work with ArrayOfVector + COSINE.
t.Run("ArrayOfVector with COSINE should accept IVF_FLAT", func(t *testing.T) {
cit := &createIndexTask{
req: &milvuspb.CreateIndexRequest{
ExtraParams: []*commonpb.KeyValuePair{
{Key: common.IndexTypeKey, Value: "IVF_FLAT"},
{Key: common.MetricTypeKey, Value: metric.COSINE},
{Key: "nlist", Value: "128"},
},
},
fieldSchema: &schemapb.FieldSchema{
FieldID: 101,
Name: "vec_field",
DataType: schemapb.DataType_ArrayOfVector,
ElementType: schemapb.DataType_FloatVector,
TypeParams: []*commonpb.KeyValuePair{
{Key: common.DimKey, Value: "128"},
},
},
}
err := cit.parseIndexParams(context.TODO())
assert.NoError(t, err)
})
t.Run("ArrayOfVector with MaxSimCosine should accept HNSW", func(t *testing.T) {
cit := &createIndexTask{
req: &milvuspb.CreateIndexRequest{
ExtraParams: []*commonpb.KeyValuePair{
{Key: common.IndexTypeKey, Value: "HNSW"},
{Key: common.MetricTypeKey, Value: metric.MaxSimCosine},
},
},
fieldSchema: &schemapb.FieldSchema{
FieldID: 101,
Name: "vec_field",
DataType: schemapb.DataType_ArrayOfVector,
ElementType: schemapb.DataType_FloatVector,
TypeParams: []*commonpb.KeyValuePair{
{Key: common.DimKey, Value: "128"},
},
},
}
err := cit.parseIndexParams(context.TODO())
assert.NoError(t, err)
})
}
func Test_ngram_parseIndexParams(t *testing.T) {
t.Run("valid ngram index params", func(t *testing.T) {
cit := &createIndexTask{