mirror of
https://github.com/milvus-io/milvus.git
synced 2026-07-21 10:15:43 +00:00
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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
Reference in New Issue
Block a user