mirror of
https://github.com/milvus-io/milvus.git
synced 2026-07-21 10:15:43 +00:00
fix: reject invalid ArrayOfVector metric types (#50363)
issue: https://github.com/milvus-io/milvus/issues/42148 Signed-off-by: SpadeA <tangchenjie1210@gmail.com>
This commit is contained in:
@@ -570,22 +570,8 @@ func (cit *createIndexTask) parseIndexParams(ctx context.Context) error {
|
||||
return merr.WrapErrParameterInvalid("valid index params", "invalid index params", "int vector index does not support metric type: "+metricType)
|
||||
}
|
||||
} else if typeutil.IsArrayOfVectorType(cit.fieldSchema.DataType) {
|
||||
if !funcutil.SliceContain(indexparamcheck.EmbListMetrics, metricType) {
|
||||
if typeutil.IsDenseFloatVectorType(cit.fieldSchema.ElementType) {
|
||||
if !funcutil.SliceContain(indexparamcheck.FloatVectorMetrics, metricType) {
|
||||
return merr.WrapErrParameterInvalid("valid index params", "invalid index params", "array of vector with float element type does not support metric type: "+metricType)
|
||||
}
|
||||
} else if typeutil.IsBinaryVectorType(cit.fieldSchema.ElementType) {
|
||||
if !funcutil.SliceContain(indexparamcheck.BinaryVectorMetrics, metricType) {
|
||||
return merr.WrapErrParameterInvalid("valid index params", "invalid index params", "array of vector with binary element type does not support metric type: "+metricType)
|
||||
}
|
||||
} else if typeutil.IsIntVectorType(cit.fieldSchema.ElementType) {
|
||||
if !funcutil.SliceContain(indexparamcheck.IntVectorMetrics, metricType) {
|
||||
return merr.WrapErrParameterInvalid("valid index params", "invalid index params", "array of vector with int element type does not support metric type: "+metricType)
|
||||
}
|
||||
} else {
|
||||
return merr.WrapErrParameterInvalid("valid index params", "invalid index params", "array of vector index does not support metric type: "+metricType)
|
||||
}
|
||||
if err := indexparamcheck.ValidateArrayOfVectorMetricType(cit.fieldSchema.ElementType, metricType); err != nil {
|
||||
return merr.WrapErrParameterInvalid("valid index params", "invalid index params", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1489,6 +1489,51 @@ func Test_arrayOfVector_nonEmbListMetric_indexCompat(t *testing.T) {
|
||||
err := cit.parseIndexParams(context.TODO())
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("ArrayOfVector with float element should reject MaxSimHamming", func(t *testing.T) {
|
||||
cit := &createIndexTask{
|
||||
req: &milvuspb.CreateIndexRequest{
|
||||
ExtraParams: []*commonpb.KeyValuePair{
|
||||
{Key: common.IndexTypeKey, Value: "HNSW_SQ"},
|
||||
{Key: common.MetricTypeKey, Value: metric.MaxSimHamming},
|
||||
},
|
||||
},
|
||||
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.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "array of vector with float element type does not support metric type")
|
||||
})
|
||||
|
||||
t.Run("ArrayOfVector with binary element should accept MaxSimHamming", func(t *testing.T) {
|
||||
cit := &createIndexTask{
|
||||
req: &milvuspb.CreateIndexRequest{
|
||||
ExtraParams: []*commonpb.KeyValuePair{
|
||||
{Key: common.IndexTypeKey, Value: "HNSW"},
|
||||
{Key: common.MetricTypeKey, Value: metric.MaxSimHamming},
|
||||
},
|
||||
},
|
||||
fieldSchema: &schemapb.FieldSchema{
|
||||
FieldID: 101,
|
||||
Name: "vec_field",
|
||||
DataType: schemapb.DataType_ArrayOfVector,
|
||||
ElementType: schemapb.DataType_BinaryVector,
|
||||
TypeParams: []*commonpb.KeyValuePair{
|
||||
{Key: common.DimKey, Value: "128"},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := cit.parseIndexParams(context.TODO())
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ngram_parseIndexParams(t *testing.T) {
|
||||
|
||||
@@ -59,6 +59,13 @@ var (
|
||||
BinaryVectorMetrics = []string{metric.HAMMING, metric.JACCARD, metric.SUBSTRUCTURE, metric.SUPERSTRUCTURE, metric.MHJACCARD}
|
||||
IntVectorMetrics = []string{metric.L2, metric.IP, metric.COSINE}
|
||||
EmbListMetrics = []string{metric.MaxSim, metric.MaxSimCosine, metric.MaxSimL2, metric.MaxSimIP, metric.MaxSimHamming, metric.MaxSimJaccard}
|
||||
|
||||
ArrayOfVectorFloatMetrics = []string{metric.L2, metric.IP, metric.COSINE, metric.MaxSim, metric.MaxSimCosine, metric.MaxSimL2, metric.MaxSimIP}
|
||||
ArrayOfVectorBinaryMetrics = []string{
|
||||
metric.HAMMING, metric.JACCARD, metric.SUBSTRUCTURE, metric.SUPERSTRUCTURE, metric.MHJACCARD,
|
||||
metric.MaxSimHamming, metric.MaxSimJaccard,
|
||||
}
|
||||
ArrayOfVectorIntMetrics = []string{metric.L2, metric.IP, metric.COSINE, metric.MaxSim, metric.MaxSimCosine, metric.MaxSimL2, metric.MaxSimIP}
|
||||
)
|
||||
|
||||
// BinIDMapMetrics is a set of all metric types supported for binary vector.
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/milvus-io/milvus/pkg/v3/common"
|
||||
"github.com/milvus-io/milvus/pkg/v3/util/funcutil"
|
||||
"github.com/milvus-io/milvus/pkg/v3/util/paramtable"
|
||||
"github.com/milvus-io/milvus/pkg/v3/util/typeutil"
|
||||
)
|
||||
|
||||
// CheckIntByRange check if the data corresponding to the key is in the range of [min, max].
|
||||
@@ -62,6 +63,30 @@ func CheckStrByValues(params map[string]string, key string, container []string)
|
||||
return funcutil.SliceContain(container, value)
|
||||
}
|
||||
|
||||
// ValidateArrayOfVectorMetricType validates both element-level and EmbList metrics
|
||||
// against the ArrayOfVector element type.
|
||||
func ValidateArrayOfVectorMetricType(elementType schemapb.DataType, metricType string) error {
|
||||
if typeutil.IsDenseFloatVectorType(elementType) {
|
||||
if !funcutil.SliceContain(ArrayOfVectorFloatMetrics, metricType) {
|
||||
return fmt.Errorf("array of vector with float element type does not support metric type: %s, supported: %v", metricType, ArrayOfVectorFloatMetrics)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if typeutil.IsBinaryVectorType(elementType) {
|
||||
if !funcutil.SliceContain(ArrayOfVectorBinaryMetrics, metricType) {
|
||||
return fmt.Errorf("array of vector with binary element type does not support metric type: %s, supported: %v", metricType, ArrayOfVectorBinaryMetrics)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if typeutil.IsIntVectorType(elementType) {
|
||||
if !funcutil.SliceContain(ArrayOfVectorIntMetrics, metricType) {
|
||||
return fmt.Errorf("array of vector with int element type does not support metric type: %s, supported: %v", metricType, ArrayOfVectorIntMetrics)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("array of vector index does not support element type: %s", elementType.String())
|
||||
}
|
||||
|
||||
func errOutOfRange(x interface{}, lb interface{}, ub interface{}) error {
|
||||
return fmt.Errorf("%v out of range: [%v, %v]", x, lb, ub)
|
||||
}
|
||||
|
||||
@@ -73,22 +73,8 @@ func (c vecIndexChecker) StaticCheck(dataType schemapb.DataType, elementType sch
|
||||
return fmt.Errorf("metric type %s not found or not supported, supported: %v", params[Metric], IntVectorMetrics)
|
||||
}
|
||||
} else if typeutil.IsArrayOfVectorType(dataType) {
|
||||
if !CheckStrByValues(params, Metric, EmbListMetrics) {
|
||||
if typeutil.IsDenseFloatVectorType(elementType) {
|
||||
if !CheckStrByValues(params, Metric, FloatVectorMetrics) {
|
||||
return fmt.Errorf("metric type %s not found or not supported for array of vector with float element type, supported: %v", params[Metric], FloatVectorMetrics)
|
||||
}
|
||||
} else if typeutil.IsBinaryVectorType(elementType) {
|
||||
if !CheckStrByValues(params, Metric, BinaryVectorMetrics) {
|
||||
return fmt.Errorf("metric type %s not found or not supported for array of vector with binary element type, supported: %v", params[Metric], BinaryVectorMetrics)
|
||||
}
|
||||
} else if typeutil.IsIntVectorType(elementType) {
|
||||
if !CheckStrByValues(params, Metric, IntVectorMetrics) {
|
||||
return fmt.Errorf("metric type %s not found or not supported for array of vector with int element type, supported: %v", params[Metric], IntVectorMetrics)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("metric type %s not found or not supported for array of vector, supported: %v", params[Metric], EmbListMetrics)
|
||||
}
|
||||
if err := ValidateArrayOfVectorMetricType(elementType, params[Metric]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/milvus-io/milvus-proto/go-api/v3/schemapb"
|
||||
"github.com/milvus-io/milvus/pkg/v3/common"
|
||||
"github.com/milvus-io/milvus/pkg/v3/util/metric"
|
||||
)
|
||||
|
||||
func TestVecIndexChecker_StaticCheck(t *testing.T) {
|
||||
@@ -14,6 +16,7 @@ func TestVecIndexChecker_StaticCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dataType schemapb.DataType
|
||||
elemType schemapb.DataType
|
||||
params map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
@@ -70,11 +73,55 @@ func TestVecIndexChecker_StaticCheck(t *testing.T) {
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "ArrayOfVector float accepts MaxSimCosine",
|
||||
dataType: schemapb.DataType_ArrayOfVector,
|
||||
elemType: schemapb.DataType_FloatVector,
|
||||
params: map[string]string{
|
||||
common.IndexTypeKey: "HNSW",
|
||||
common.MetricTypeKey: metric.MaxSimCosine,
|
||||
HNSWM: "16",
|
||||
EFConstruction: "200",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ArrayOfVector float rejects MaxSimHamming",
|
||||
dataType: schemapb.DataType_ArrayOfVector,
|
||||
elemType: schemapb.DataType_FloatVector,
|
||||
params: map[string]string{
|
||||
common.IndexTypeKey: "HNSW_SQ",
|
||||
common.MetricTypeKey: metric.MaxSimHamming,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "ArrayOfVector binary accepts MaxSimHamming",
|
||||
dataType: schemapb.DataType_ArrayOfVector,
|
||||
elemType: schemapb.DataType_BinaryVector,
|
||||
params: map[string]string{
|
||||
common.IndexTypeKey: "HNSW",
|
||||
common.MetricTypeKey: metric.MaxSimHamming,
|
||||
HNSWM: "16",
|
||||
EFConstruction: "200",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ArrayOfVector binary rejects MaxSimCosine",
|
||||
dataType: schemapb.DataType_ArrayOfVector,
|
||||
elemType: schemapb.DataType_BinaryVector,
|
||||
params: map[string]string{
|
||||
common.IndexTypeKey: "HNSW",
|
||||
common.MetricTypeKey: metric.MaxSimCosine,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := checker.StaticCheck(tt.dataType, schemapb.DataType_None, tt.params)
|
||||
err := checker.StaticCheck(tt.dataType, tt.elemType, tt.params)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user