mirror of
https://gitee.com/milvus-io/milvus.git
synced 2024-12-05 05:18:52 +08:00
e5948bd039
Use variadic parameter function for options make client options easier to use. Related to #31293 Signed-off-by: Congqi Xia <congqi.xia@zilliz.com>
1078 lines
58 KiB
Go
1078 lines
58 KiB
Go
package testcases
|
||
|
||
import (
|
||
"fmt"
|
||
"math/rand"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/stretchr/testify/require"
|
||
"go.uber.org/zap"
|
||
|
||
"github.com/milvus-io/milvus/client/v2"
|
||
"github.com/milvus-io/milvus/client/v2/column"
|
||
"github.com/milvus-io/milvus/client/v2/entity"
|
||
"github.com/milvus-io/milvus/client/v2/index"
|
||
"github.com/milvus-io/milvus/pkg/log"
|
||
"github.com/milvus-io/milvus/tests/go_client/common"
|
||
hp "github.com/milvus-io/milvus/tests/go_client/testcases/helper"
|
||
)
|
||
|
||
func TestSearchDefault(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
// create -> insert -> flush -> index -> load
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
resSearch, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong))
|
||
common.CheckErr(t, err, true)
|
||
common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultLimit)
|
||
}
|
||
|
||
func TestSearchDefaultGrowing(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
// create -> index -> load -> insert
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.VarcharBinary), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
|
||
// search
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeBinaryVector)
|
||
resSearch, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong))
|
||
common.CheckErr(t, err, true)
|
||
common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultLimit)
|
||
}
|
||
|
||
// test search collection and partition name not exist
|
||
func TestSearchInvalidCollectionPartitionName(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
// search with not exist collection
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
_, err := mc.Search(ctx, client.NewSearchOption("aaa", common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong))
|
||
common.CheckErr(t, err, false, "can't find collection")
|
||
|
||
// search with empty collections name
|
||
_, err = mc.Search(ctx, client.NewSearchOption("", common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong))
|
||
common.CheckErr(t, err, false, "collection name should not be empty")
|
||
|
||
// search with not exist partition
|
||
_, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.VarcharBinary), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
_, err1 := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithPartitions("aaa"))
|
||
common.CheckErr(t, err1, false, "partition name aaa not found")
|
||
|
||
// search with empty partition name []string{""} -> error
|
||
_, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).
|
||
WithConsistencyLevel(entity.ClStrong).WithANNSField(common.DefaultFloatVecFieldName).WithPartitions(""))
|
||
common.CheckErr(t, errSearch, false, "Partition name should not be empty")
|
||
}
|
||
|
||
// test search empty collection -> return empty
|
||
func TestSearchEmptyCollection(t *testing.T) {
|
||
t.Skip("https://github.com/milvus-io/milvus/issues/33952")
|
||
t.Parallel()
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, enableDynamicField := range []bool{true, false} {
|
||
// create -> index -> load
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.AllFields), hp.TNewFieldsOption(),
|
||
hp.TNewSchemaOption().TWithEnableDynamicField(enableDynamicField))
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
type mNameVec struct {
|
||
fieldName string
|
||
queryVec []entity.Vector
|
||
}
|
||
for _, _mNameVec := range []mNameVec{
|
||
{fieldName: common.DefaultFloatVecFieldName, queryVec: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)},
|
||
{fieldName: common.DefaultFloat16VecFieldName, queryVec: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloat16Vector)},
|
||
{fieldName: common.DefaultBFloat16VecFieldName, queryVec: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeBFloat16Vector)},
|
||
{fieldName: common.DefaultBinaryVecFieldName, queryVec: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeBinaryVector)},
|
||
} {
|
||
resSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, _mNameVec.queryVec).
|
||
WithConsistencyLevel(entity.ClStrong).WithANNSField(_mNameVec.fieldName))
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckSearchResult(t, resSearch, common.DefaultNq, 0)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestSearchEmptySparseCollection(t *testing.T) {
|
||
t.Skip("https://github.com/milvus-io/milvus/issues/33952")
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(),
|
||
hp.TNewSchemaOption().TWithEnableDynamicField(true))
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeSparseVector)
|
||
resSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).
|
||
WithConsistencyLevel(entity.ClStrong).WithANNSField(common.DefaultSparseVecFieldName))
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckSearchResult(t, resSearch, common.DefaultNq, 0)
|
||
}
|
||
|
||
// test search with partition names []string{}, specify partitions
|
||
func TestSearchPartitions(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
parName := common.GenRandomString("p", 4)
|
||
// create collection and partition
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption().TWithAutoID(true),
|
||
hp.TNewSchemaOption().TWithEnableDynamicField(true))
|
||
err := mc.CreatePartition(ctx, client.NewCreatePartitionOption(schema.CollectionName, parName))
|
||
common.CheckErr(t, err, true)
|
||
|
||
// insert autoID data into parName and _default partitions
|
||
_defVec := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, *hp.TNewDataOption())
|
||
_defDynamic := hp.GenDynamicColumnData(0, common.DefaultNb)
|
||
insertRes1, err1 := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName).WithColumns(_defVec).WithColumns(_defDynamic...))
|
||
common.CheckErr(t, err1, true)
|
||
|
||
_parVec := hp.GenColumnData(common.DefaultNb, entity.FieldTypeFloatVector, *hp.TNewDataOption())
|
||
insertRes2, err2 := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName).WithColumns(_parVec))
|
||
common.CheckErr(t, err2, true)
|
||
|
||
// flush -> FLAT index -> load
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{common.DefaultFloatVecFieldName: index.NewFlatIndex(entity.COSINE)}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search with empty partition name []string{""} -> error
|
||
vectors := make([]entity.Vector, 0, 2)
|
||
// query first ID of _default and parName partition
|
||
_defId0, _ := insertRes1.IDs.GetAsInt64(0)
|
||
_parId0, _ := insertRes2.IDs.GetAsInt64(0)
|
||
queryRes, _ := mc.Query(ctx, client.NewQueryOption(schema.CollectionName).WithFilter(fmt.Sprintf("int64 in [%d, %d]", _defId0, _parId0)).WithOutputFields("*"))
|
||
require.ElementsMatch(t, []int64{_defId0, _parId0}, queryRes.GetColumn(common.DefaultInt64FieldName).(*column.ColumnInt64).Data())
|
||
for _, vec := range queryRes.GetColumn(common.DefaultFloatVecFieldName).(*column.ColumnFloatVector).Data() {
|
||
vectors = append(vectors, entity.FloatVector(vec))
|
||
}
|
||
|
||
for _, partitions := range [][]string{{}, {common.DefaultPartition, parName}} {
|
||
// search with empty partition names slice []string{} -> all partitions
|
||
searchResult, errSearch1 := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, 5, vectors).
|
||
WithConsistencyLevel(entity.ClStrong).WithANNSField(common.DefaultFloatVecFieldName).WithPartitions(partitions...).WithOutputFields("*"))
|
||
|
||
// check search result contains search vector, which from all partitions
|
||
common.CheckErr(t, errSearch1, true)
|
||
common.CheckSearchResult(t, searchResult, len(vectors), 5)
|
||
require.Contains(t, searchResult[0].IDs.(*column.ColumnInt64).Data(), _defId0)
|
||
require.Contains(t, searchResult[1].IDs.(*column.ColumnInt64).Data(), _parId0)
|
||
require.EqualValues(t, entity.FloatVector(searchResult[0].GetColumn(common.DefaultFloatVecFieldName).(*column.ColumnFloatVector).Data()[0]), vectors[0])
|
||
require.EqualValues(t, entity.FloatVector(searchResult[1].GetColumn(common.DefaultFloatVecFieldName).(*column.ColumnFloatVector).Data()[0]), vectors[1])
|
||
}
|
||
}
|
||
|
||
// test query empty output fields: []string{} -> []string{}
|
||
// test query empty output fields: []string{""} -> error
|
||
func TestSearchEmptyOutputFields(t *testing.T) {
|
||
t.Parallel()
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, dynamic := range []bool{true, false} {
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(dynamic))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(100))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
resSearch, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).WithOutputFields())
|
||
common.CheckErr(t, err, true)
|
||
common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultLimit)
|
||
common.CheckOutputFields(t, []string{}, resSearch[0].Fields)
|
||
|
||
_, err = mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).WithOutputFields(""))
|
||
if dynamic {
|
||
common.CheckErr(t, err, false, "parse output field name failed")
|
||
} else {
|
||
common.CheckErr(t, err, false, "field not exist")
|
||
}
|
||
}
|
||
}
|
||
|
||
// test query with not existed field ["aa"]: error or as dynamic field
|
||
// test query with part not existed field ["aa", "$meat"]: error or as dynamic field
|
||
// test query with repeated field: ["*", "$meat"], ["floatVec", floatVec"] unique field
|
||
func TestSearchNotExistOutputFields(t *testing.T) {
|
||
t.Parallel()
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, enableDynamic := range []bool{false, true} {
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(enableDynamic))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search vector output fields not exist, part exist
|
||
type dynamicOutputFields struct {
|
||
outputFields []string
|
||
expOutputFields []string
|
||
}
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
dof := []dynamicOutputFields{
|
||
{outputFields: []string{"aaa"}, expOutputFields: []string{"aaa"}},
|
||
{outputFields: []string{"aaa", common.DefaultDynamicFieldName}, expOutputFields: []string{"aaa", common.DefaultDynamicFieldName}},
|
||
{outputFields: []string{"*", common.DefaultDynamicFieldName}, expOutputFields: []string{common.DefaultInt64FieldName, common.DefaultFloatVecFieldName, common.DefaultDynamicFieldName}},
|
||
}
|
||
|
||
for _, _dof := range dof {
|
||
resSearch, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).WithOutputFields(_dof.outputFields...))
|
||
if enableDynamic {
|
||
common.CheckErr(t, err, true)
|
||
common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultLimit)
|
||
common.CheckOutputFields(t, _dof.expOutputFields, resSearch[0].Fields)
|
||
} else {
|
||
common.CheckErr(t, err, false, "not exist")
|
||
}
|
||
}
|
||
existedRepeatedFields := []string{common.DefaultInt64FieldName, common.DefaultFloatVecFieldName, common.DefaultInt64FieldName, common.DefaultFloatVecFieldName}
|
||
resSearch2, err2 := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).WithOutputFields(existedRepeatedFields...))
|
||
common.CheckErr(t, err2, true)
|
||
common.CheckSearchResult(t, resSearch2, common.DefaultNq, common.DefaultLimit)
|
||
common.CheckOutputFields(t, []string{common.DefaultInt64FieldName, common.DefaultFloatVecFieldName}, resSearch2[0].Fields)
|
||
}
|
||
}
|
||
|
||
// test search output all * fields when enable dynamic and insert dynamic column data
|
||
func TestSearchOutputAllFields(t *testing.T) {
|
||
t.Parallel()
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.AllFields), hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
//
|
||
allFieldsName := []string{common.DefaultDynamicFieldName}
|
||
for _, field := range schema.Fields {
|
||
allFieldsName = append(allFieldsName, field.Name)
|
||
}
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
|
||
searchRes, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithANNSField(common.DefaultFloatVecFieldName).WithOutputFields("*"))
|
||
common.CheckErr(t, err, true)
|
||
common.CheckSearchResult(t, searchRes, common.DefaultNq, common.DefaultLimit)
|
||
for _, res := range searchRes {
|
||
common.CheckOutputFields(t, allFieldsName, res.Fields)
|
||
}
|
||
}
|
||
|
||
// test search output all * fields when enable dynamic and insert dynamic column data
|
||
func TestSearchOutputBinaryPk(t *testing.T) {
|
||
t.Parallel()
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.VarcharBinary), hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
//
|
||
allFieldsName := []string{common.DefaultDynamicFieldName}
|
||
for _, field := range schema.Fields {
|
||
allFieldsName = append(allFieldsName, field.Name)
|
||
}
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeBinaryVector)
|
||
searchRes, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).WithOutputFields("*"))
|
||
common.CheckErr(t, err, true)
|
||
common.CheckSearchResult(t, searchRes, common.DefaultNq, common.DefaultLimit)
|
||
for _, res := range searchRes {
|
||
common.CheckOutputFields(t, allFieldsName, res.Fields)
|
||
}
|
||
}
|
||
|
||
// test search output all * fields when enable dynamic and insert dynamic column data
|
||
func TestSearchOutputSparse(t *testing.T) {
|
||
t.Parallel()
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
//
|
||
allFieldsName := []string{common.DefaultDynamicFieldName}
|
||
for _, field := range schema.Fields {
|
||
allFieldsName = append(allFieldsName, field.Name)
|
||
}
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeSparseVector)
|
||
searchRes, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithANNSField(common.DefaultSparseVecFieldName).WithOutputFields("*"))
|
||
common.CheckErr(t, err, true)
|
||
common.CheckSearchResult(t, searchRes, common.DefaultNq, common.DefaultLimit)
|
||
for _, res := range searchRes {
|
||
common.CheckOutputFields(t, allFieldsName, res.Fields)
|
||
}
|
||
}
|
||
|
||
// test search with invalid vector field name: not exist; non-vector field, empty fiend name, json and dynamic field -> error
|
||
func TestSearchInvalidVectorField(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(500))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
type invalidVectorFieldStruct struct {
|
||
vectorField string
|
||
errNil bool
|
||
errMsg string
|
||
}
|
||
|
||
invalidVectorFields := []invalidVectorFieldStruct{
|
||
// not exist field
|
||
{vectorField: common.DefaultBinaryVecFieldName, errNil: false, errMsg: fmt.Sprintf("failed to get field schema by name: fieldName(%s) not found", common.DefaultBinaryVecFieldName)},
|
||
|
||
// non-vector field
|
||
{vectorField: common.DefaultInt64FieldName, errNil: false, errMsg: fmt.Sprintf("failed to create query plan: field (%s) to search is not of vector data type", common.DefaultInt64FieldName)},
|
||
|
||
// json field
|
||
{vectorField: common.DefaultJSONFieldName, errNil: false, errMsg: fmt.Sprintf("failed to get field schema by name: fieldName(%s) not found", common.DefaultJSONFieldName)},
|
||
|
||
// dynamic field
|
||
{vectorField: common.DefaultDynamicFieldName, errNil: false, errMsg: fmt.Sprintf("failed to get field schema by name: fieldName(%s) not found", common.DefaultDynamicFieldName)},
|
||
|
||
// allows empty vector field name
|
||
{vectorField: "", errNil: true, errMsg: ""},
|
||
}
|
||
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeSparseVector)
|
||
for _, invalidVectorField := range invalidVectorFields {
|
||
_, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithANNSField(invalidVectorField.vectorField))
|
||
common.CheckErr(t, err, invalidVectorField.errNil, invalidVectorField.errMsg)
|
||
}
|
||
}
|
||
|
||
// test search with invalid vectors
|
||
func TestSearchInvalidVectors(t *testing.T) {
|
||
t.Parallel()
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64MultiVec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(500))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
type invalidVectorsStruct struct {
|
||
fieldName string
|
||
vectors []entity.Vector
|
||
errMsg string
|
||
}
|
||
|
||
invalidVectors := []invalidVectorsStruct{
|
||
// dim not match
|
||
{fieldName: common.DefaultFloatVecFieldName, vectors: hp.GenSearchVectors(common.DefaultNq, 64, entity.FieldTypeFloatVector), errMsg: "vector dimension mismatch"},
|
||
{fieldName: common.DefaultFloat16VecFieldName, vectors: hp.GenSearchVectors(common.DefaultNq, 64, entity.FieldTypeFloat16Vector), errMsg: "vector dimension mismatch"},
|
||
|
||
// vector type not match
|
||
{fieldName: common.DefaultFloatVecFieldName, vectors: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeBinaryVector), errMsg: "vector type must be the same"},
|
||
{fieldName: common.DefaultBFloat16VecFieldName, vectors: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloat16Vector), errMsg: "vector type must be the same"},
|
||
|
||
// empty vectors
|
||
{fieldName: common.DefaultBinaryVecFieldName, vectors: []entity.Vector{}, errMsg: "nq [0] is invalid"},
|
||
{fieldName: common.DefaultFloatVecFieldName, vectors: []entity.Vector{entity.FloatVector{}}, errMsg: "vector dimension mismatch"},
|
||
{vectors: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector), errMsg: "multiple anns_fields exist, please specify a anns_field in search_params"},
|
||
{fieldName: "", vectors: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector), errMsg: "multiple anns_fields exist, please specify a anns_field in search_params"},
|
||
}
|
||
|
||
for _, invalidVector := range invalidVectors {
|
||
_, errSearchEmpty := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, invalidVector.vectors).WithANNSField(invalidVector.fieldName))
|
||
common.CheckErr(t, errSearchEmpty, false, invalidVector.errMsg)
|
||
}
|
||
}
|
||
|
||
// test search with invalid vectors
|
||
func TestSearchEmptyInvalidVectors(t *testing.T) {
|
||
t.Log("https://github.com/milvus-io/milvus/issues/33639")
|
||
t.Log("https://github.com/milvus-io/milvus/issues/33637")
|
||
t.Parallel()
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
type invalidVectorsStruct struct {
|
||
vectors []entity.Vector
|
||
errNil bool
|
||
errMsg string
|
||
}
|
||
|
||
invalidVectors := []invalidVectorsStruct{
|
||
// dim not match
|
||
{vectors: hp.GenSearchVectors(common.DefaultNq, 64, entity.FieldTypeFloatVector), errNil: true, errMsg: "vector dimension mismatch"},
|
||
|
||
// vector type not match
|
||
{vectors: hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeBinaryVector), errNil: true, errMsg: "vector type must be the same"},
|
||
|
||
// empty vectors
|
||
{vectors: []entity.Vector{}, errNil: false, errMsg: "nq [0] is invalid"},
|
||
{vectors: []entity.Vector{entity.FloatVector{}}, errNil: true, errMsg: "vector dimension mismatch"},
|
||
}
|
||
|
||
for _, invalidVector := range invalidVectors {
|
||
_, errSearchEmpty := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, invalidVector.vectors).WithANNSField(common.DefaultFloatVecFieldName))
|
||
common.CheckErr(t, errSearchEmpty, invalidVector.errNil, invalidVector.errMsg)
|
||
}
|
||
}
|
||
|
||
// test search metric type isn't the same with index metric type
|
||
func TestSearchNotMatchMetricType(t *testing.T) {
|
||
t.Skip("Waiting for support for specifying search parameters")
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(500))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).
|
||
TWithFieldIndex(map[string]index.Index{common.DefaultFloatVecFieldName: index.NewHNSWIndex(entity.COSINE, 8, 200)}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
vectors := hp.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
_, errSearchEmpty := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors))
|
||
common.CheckErr(t, errSearchEmpty, false, "metric type not match: invalid parameter")
|
||
}
|
||
|
||
// test search with invalid topK -> error
|
||
func TestSearchInvalidTopK(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(500))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
vectors := hp.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
for _, invalidTopK := range []int{-1, 0, 16385} {
|
||
_, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, invalidTopK, vectors))
|
||
common.CheckErr(t, errSearch, false, "should be in range [1, 16384]")
|
||
}
|
||
}
|
||
|
||
// test search with invalid topK -> error
|
||
func TestSearchInvalidOffset(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(500))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
vectors := hp.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
for _, invalidOffset := range []int{-1, common.MaxTopK + 1} {
|
||
_, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithOffset(invalidOffset))
|
||
common.CheckErr(t, errSearch, false, "should be in range [1, 16384]")
|
||
}
|
||
}
|
||
|
||
// test search with invalid search params
|
||
func TestSearchInvalidSearchParams(t *testing.T) {
|
||
t.Skip("Waiting for support for specifying search parameters")
|
||
}
|
||
|
||
// search with index hnsw search param ef < topK -> error
|
||
func TestSearchEfHnsw(t *testing.T) {
|
||
t.Skip("Waiting for support for specifying search parameters")
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(500))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).
|
||
TWithFieldIndex(map[string]index.Index{common.DefaultFloatVecFieldName: index.NewHNSWIndex(entity.COSINE, 8, 200)}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
vectors := hp.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
_, err := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors))
|
||
common.CheckErr(t, err, false, "ef(7) should be larger than k(10)")
|
||
}
|
||
|
||
// test search params mismatch index type, hnsw index and ivf sq8 search param -> search with default hnsw params, ef=topK
|
||
func TestSearchSearchParamsMismatchIndex(t *testing.T) {
|
||
t.Skip("Waiting for support for specifying search parameters")
|
||
}
|
||
|
||
// search with index scann search param ef < topK -> error
|
||
func TestSearchInvalidScannReorderK(t *testing.T) {
|
||
t.Skip("Waiting for support for specifying search parameters")
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VecJSON), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(500))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{
|
||
common.DefaultFloatVecFieldName: index.NewSCANNIndex(entity.COSINE, 16, true),
|
||
}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search with invalid reorder_k < topK
|
||
|
||
// valid scann index search reorder_k
|
||
}
|
||
|
||
// test search with scann index params: with_raw_data and metrics_type [L2, IP, COSINE]
|
||
func TestSearchScannAllMetricsWithRawData(t *testing.T) {
|
||
t.Skip("Waiting for support scann index params withRawData")
|
||
t.Parallel()
|
||
/*for _, withRawData := range []bool{true, false} {
|
||
for _, metricType := range []entity.MetricType{entity.L2, entity.IP, entity.COSINE} {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VecJSON), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema, 500), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{
|
||
common.DefaultFloatVecFieldName: index.NewSCANNIndex(entity.COSINE, 16),
|
||
}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search and output all fields
|
||
vectors := hp.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
resSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).WithOutputFields([]string{"*"}))
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckOutputFields(t, []string{common.DefaultInt64FieldName, common.DefaultFloatFieldName,
|
||
common.DefaultJSONFieldName, common.DefaultFloatVecFieldName, common.DefaultDynamicFieldName}, resSearch[0].Fields)
|
||
common.CheckSearchResult(t, resSearch, 1, common.DefaultLimit)
|
||
}
|
||
}*/
|
||
}
|
||
|
||
// test search with valid expression
|
||
func TestSearchExpr(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption())
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
type mExprExpected struct {
|
||
expr string
|
||
ids []int64
|
||
}
|
||
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
for _, _mExpr := range []mExprExpected{
|
||
{expr: fmt.Sprintf("%s < 10", common.DefaultInt64FieldName), ids: []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}},
|
||
{expr: fmt.Sprintf("%s in [10, 100]", common.DefaultInt64FieldName), ids: []int64{10, 100}},
|
||
} {
|
||
resSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(_mExpr.expr))
|
||
common.CheckErr(t, errSearch, true)
|
||
for _, res := range resSearch {
|
||
require.ElementsMatch(t, _mExpr.ids, res.IDs.(*column.ColumnInt64).Data())
|
||
}
|
||
}
|
||
}
|
||
|
||
// test search with invalid expression
|
||
func TestSearchInvalidExpr(t *testing.T) {
|
||
t.Parallel()
|
||
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VecJSON), hp.TNewFieldsOption(), hp.TNewSchemaOption().TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search with invalid expr
|
||
vectors := hp.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
for _, exprStruct := range common.InvalidExpressions {
|
||
log.Debug("TestSearchInvalidExpr", zap.String("expr", exprStruct.Expr))
|
||
_, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(exprStruct.Expr).WithANNSField(common.DefaultFloatVecFieldName))
|
||
common.CheckErr(t, errSearch, exprStruct.ErrNil, exprStruct.ErrMsg)
|
||
}
|
||
}
|
||
|
||
func TestSearchJsonFieldExpr(t *testing.T) {
|
||
t.Parallel()
|
||
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
exprs := []string{
|
||
"",
|
||
fmt.Sprintf("exists %s['number'] ", common.DefaultJSONFieldName), // exists
|
||
"json[\"number\"] > 1 and json[\"number\"] < 1000", // > and
|
||
fmt.Sprintf("%s[\"number\"] > 10", common.DefaultJSONFieldName), // number >
|
||
fmt.Sprintf("%s != 10 ", common.DefaultJSONFieldName), // json != 10
|
||
fmt.Sprintf("%s[\"number\"] < 2000", common.DefaultJSONFieldName), // number <
|
||
fmt.Sprintf("%s[\"bool\"] != true", common.DefaultJSONFieldName), // bool !=
|
||
fmt.Sprintf("%s[\"bool\"] == False", common.DefaultJSONFieldName), // bool ==
|
||
fmt.Sprintf("%s[\"bool\"] in [true]", common.DefaultJSONFieldName), // bool in
|
||
fmt.Sprintf("%s[\"string\"] >= '1' ", common.DefaultJSONFieldName), // string >=
|
||
fmt.Sprintf("%s['list'][0] > 200", common.DefaultJSONFieldName), // list filter
|
||
fmt.Sprintf("%s['list'] != [2, 3]", common.DefaultJSONFieldName), // json[list] !=
|
||
fmt.Sprintf("%s > 2000", common.DefaultJSONFieldName), // json > 2000
|
||
fmt.Sprintf("%s like '2%%' ", common.DefaultJSONFieldName), // json like '2%'
|
||
fmt.Sprintf("%s[0] > 2000 ", common.DefaultJSONFieldName), // json[0] > 2000
|
||
fmt.Sprintf("%s > 2000.5 ", common.DefaultJSONFieldName), // json > 2000.5
|
||
}
|
||
|
||
for _, dynamicField := range []bool{false, true} {
|
||
// create collection
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VecJSON), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(dynamicField))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search with jsonField expr key datatype and json data type mismatch
|
||
for _, expr := range exprs {
|
||
log.Debug("TestSearchJsonFieldExpr", zap.String("expr", expr))
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
searchRes, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(expr).WithANNSField(common.DefaultFloatVecFieldName).WithOutputFields(common.DefaultInt64FieldName, common.DefaultJSONFieldName))
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckOutputFields(t, []string{common.DefaultInt64FieldName, common.DefaultJSONFieldName}, searchRes[0].Fields)
|
||
common.CheckSearchResult(t, searchRes, common.DefaultNq, common.DefaultLimit)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestSearchDynamicFieldExpr(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
// create collection
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VecJSON), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
exprs := []string{
|
||
"",
|
||
"exists dynamicNumber", // exist without dynamic fieldName
|
||
fmt.Sprintf("exists %s[\"dynamicNumber\"]", common.DefaultDynamicFieldName), // exist with fieldName
|
||
fmt.Sprintf("%s[\"dynamicNumber\"] > 10", common.DefaultDynamicFieldName), // int expr with fieldName
|
||
fmt.Sprintf("%s[\"dynamicBool\"] == true", common.DefaultDynamicFieldName), // bool with fieldName
|
||
"dynamicBool == False", // bool without fieldName
|
||
fmt.Sprintf("%s['dynamicString'] == '1'", common.DefaultDynamicFieldName), // string with fieldName
|
||
"dynamicString != \"2\" ", // string without fieldName
|
||
}
|
||
|
||
// search with jsonField expr key datatype and json data type mismatch
|
||
for _, expr := range exprs {
|
||
log.Debug("TestSearchDynamicFieldExpr", zap.String("expr", expr))
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
searchRes, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(expr).WithANNSField(common.DefaultFloatVecFieldName).WithOutputFields(common.DefaultInt64FieldName, "dynamicNumber", "number"))
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckOutputFields(t, []string{common.DefaultInt64FieldName, "dynamicNumber", "number"}, searchRes[0].Fields)
|
||
if expr == "$meta['dynamicString'] == '1'" {
|
||
common.CheckSearchResult(t, searchRes, common.DefaultNq, 1)
|
||
} else {
|
||
common.CheckSearchResult(t, searchRes, common.DefaultNq, common.DefaultLimit)
|
||
}
|
||
}
|
||
|
||
// search with expr filter number and, &&, or, ||
|
||
exprs2 := []string{
|
||
"dynamicNumber > 1 and dynamicNumber <= 999", // int expr without fieldName
|
||
fmt.Sprintf("%s['dynamicNumber'] > 1 && %s['dynamicNumber'] < 1000", common.DefaultDynamicFieldName, common.DefaultDynamicFieldName),
|
||
"dynamicNumber < 888 || dynamicNumber < 1000",
|
||
fmt.Sprintf("%s['dynamicNumber'] < 888 or %s['dynamicNumber'] < 1000", common.DefaultDynamicFieldName, common.DefaultDynamicFieldName),
|
||
fmt.Sprintf("%s[\"dynamicNumber\"] < 1000", common.DefaultDynamicFieldName), // int expr with fieldName
|
||
}
|
||
|
||
for _, expr := range exprs2 {
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
searchRes, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(expr).WithANNSField(common.DefaultFloatVecFieldName).
|
||
WithOutputFields(common.DefaultInt64FieldName, common.DefaultJSONFieldName, common.DefaultDynamicFieldName, "dynamicNumber", "number"))
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckOutputFields(t, []string{common.DefaultInt64FieldName, common.DefaultJSONFieldName, common.DefaultDynamicFieldName, "dynamicNumber", "number"}, searchRes[0].Fields)
|
||
for _, res := range searchRes {
|
||
for _, id := range res.IDs.(*column.ColumnInt64).Data() {
|
||
require.Less(t, id, int64(1000))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestSearchArrayFieldExpr(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
// create collection
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VecArray), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
var capacity int64 = common.TestCapacity
|
||
exprs := []string{
|
||
fmt.Sprintf("%s[0] == false", common.DefaultBoolArrayField), // array[0] ==
|
||
fmt.Sprintf("%s[0] > 0", common.DefaultInt64ArrayField), // array[0] >
|
||
fmt.Sprintf("json_contains (%s, %d)", common.DefaultInt16ArrayField, capacity), // json_contains
|
||
fmt.Sprintf("array_contains (%s, %d)", common.DefaultInt16ArrayField, capacity), // array_contains
|
||
fmt.Sprintf("json_contains_all (%s, [90, 91])", common.DefaultInt64ArrayField), // json_contains_all
|
||
fmt.Sprintf("array_contains_all (%s, [90, 91])", common.DefaultInt64ArrayField), // array_contains_all
|
||
fmt.Sprintf("array_contains_any (%s, [0, 100, 10000])", common.DefaultFloatArrayField), // array_contains_any
|
||
fmt.Sprintf("json_contains_any (%s, [0, 100, 10])", common.DefaultFloatArrayField), // json_contains_any
|
||
fmt.Sprintf("array_length(%s) == %d", common.DefaultDoubleArrayField, capacity), // array_length
|
||
}
|
||
|
||
// search with jsonField expr key datatype and json data type mismatch
|
||
allArrayFields := make([]string, 0, len(schema.Fields))
|
||
for _, field := range schema.Fields {
|
||
if field.DataType == entity.FieldTypeArray {
|
||
allArrayFields = append(allArrayFields, field.Name)
|
||
}
|
||
}
|
||
vectors := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
for _, expr := range exprs {
|
||
searchRes, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(expr).WithOutputFields(allArrayFields...))
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckOutputFields(t, allArrayFields, searchRes[0].Fields)
|
||
common.CheckSearchResult(t, searchRes, common.DefaultNq, common.DefaultLimit)
|
||
}
|
||
|
||
// search hits empty
|
||
searchRes, errSearchEmpty := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(fmt.Sprintf("array_contains (%s, 1000000)", common.DefaultInt32ArrayField)).WithOutputFields(allArrayFields...))
|
||
common.CheckErr(t, errSearchEmpty, true)
|
||
common.CheckSearchResult(t, searchRes, common.DefaultNq, 0)
|
||
}
|
||
|
||
// test search with field not existed expr: if dynamic
|
||
func TestSearchNotExistedExpr(t *testing.T) {
|
||
t.Parallel()
|
||
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, isDynamic := range [2]bool{true, false} {
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64Vec), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(isDynamic))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption())
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search with invalid expr
|
||
vectors := hp.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector)
|
||
expr := "id in [0]"
|
||
res, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, vectors).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(expr).WithANNSField(common.DefaultFloatVecFieldName))
|
||
if isDynamic {
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckSearchResult(t, res, 1, 0)
|
||
} else {
|
||
common.CheckErr(t, errSearch, false, "not exist")
|
||
}
|
||
}
|
||
}
|
||
|
||
// test search with fp16/ bf16 /binary vector
|
||
func TestSearchMultiVectors(t *testing.T) {
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64MultiVec), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithNb(common.DefaultNb*2))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
flatIndex := index.NewFlatIndex(entity.L2)
|
||
binIndex := index.NewGenericIndex(common.DefaultBinaryVecFieldName, map[string]string{"nlist": "64", index.MetricTypeKey: "JACCARD", index.IndexTypeKey: "BIN_IVF_FLAT"})
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{
|
||
common.DefaultFloatVecFieldName: flatIndex,
|
||
common.DefaultFloat16VecFieldName: flatIndex,
|
||
common.DefaultBFloat16VecFieldName: flatIndex,
|
||
common.DefaultBinaryVecFieldName: binIndex,
|
||
}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search with all kinds of vectors
|
||
type mFieldNameType struct {
|
||
fieldName string
|
||
fieldType entity.FieldType
|
||
metricType entity.MetricType
|
||
}
|
||
fnts := []mFieldNameType{
|
||
{fieldName: common.DefaultFloatVecFieldName, fieldType: entity.FieldTypeFloatVector, metricType: entity.L2},
|
||
{fieldName: common.DefaultBinaryVecFieldName, fieldType: entity.FieldTypeBinaryVector, metricType: entity.JACCARD},
|
||
{fieldName: common.DefaultFloat16VecFieldName, fieldType: entity.FieldTypeFloat16Vector, metricType: entity.L2},
|
||
{fieldName: common.DefaultBFloat16VecFieldName, fieldType: entity.FieldTypeBFloat16Vector, metricType: entity.L2},
|
||
}
|
||
|
||
for _, fnt := range fnts {
|
||
queryVec := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, fnt.fieldType)
|
||
expr := fmt.Sprintf("%s > 10", common.DefaultInt64FieldName)
|
||
|
||
resSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit*2, queryVec).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(expr).WithANNSField(fnt.fieldName).WithOutputFields("*"))
|
||
common.CheckErr(t, errSearch, true)
|
||
common.CheckSearchResult(t, resSearch, common.DefaultNq, common.DefaultLimit*2)
|
||
common.CheckOutputFields(t, []string{
|
||
common.DefaultInt64FieldName, common.DefaultFloatVecFieldName,
|
||
common.DefaultBinaryVecFieldName, common.DefaultFloat16VecFieldName, common.DefaultBFloat16VecFieldName, common.DefaultDynamicFieldName,
|
||
}, resSearch[0].Fields)
|
||
|
||
// pagination search
|
||
resPage, errPage := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, queryVec).WithConsistencyLevel(entity.ClStrong).
|
||
WithFilter(expr).WithANNSField(fnt.fieldName).WithOutputFields("*").WithOffset(10))
|
||
|
||
common.CheckErr(t, errPage, true)
|
||
common.CheckSearchResult(t, resPage, common.DefaultNq, common.DefaultLimit)
|
||
for i := 0; i < common.DefaultNq; i++ {
|
||
require.Equal(t, resSearch[i].IDs.(*column.ColumnInt64).Data()[10:], resPage[i].IDs.(*column.ColumnInt64).Data())
|
||
}
|
||
common.CheckOutputFields(t, []string{
|
||
common.DefaultInt64FieldName, common.DefaultFloatVecFieldName,
|
||
common.DefaultBinaryVecFieldName, common.DefaultFloat16VecFieldName, common.DefaultBFloat16VecFieldName, common.DefaultDynamicFieldName,
|
||
}, resPage[0].Fields)
|
||
|
||
// TODO range search
|
||
// TODO iterator search
|
||
}
|
||
}
|
||
|
||
func TestSearchSparseVector(t *testing.T) {
|
||
t.Parallel()
|
||
idxInverted := index.NewGenericIndex(common.DefaultSparseVecFieldName, map[string]string{"drop_ratio_build": "0.2", index.MetricTypeKey: "IP", index.IndexTypeKey: "SPARSE_INVERTED_INDEX"})
|
||
idxWand := index.NewGenericIndex(common.DefaultSparseVecFieldName, map[string]string{"drop_ratio_build": "0.3", index.MetricTypeKey: "IP", index.IndexTypeKey: "SPARSE_WAND"})
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, idx := range []index.Index{idxInverted, idxWand} {
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithSparseMaxLen(128).TWithNb(common.DefaultNb*2))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{common.DefaultSparseVecFieldName: idx}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search
|
||
queryVec := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeSparseVector)
|
||
resSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, queryVec).WithConsistencyLevel(entity.ClStrong).
|
||
WithOutputFields("*"))
|
||
|
||
common.CheckErr(t, errSearch, true)
|
||
require.Len(t, resSearch, common.DefaultNq)
|
||
outputFields := []string{common.DefaultInt64FieldName, common.DefaultVarcharFieldName, common.DefaultSparseVecFieldName, common.DefaultDynamicFieldName}
|
||
for _, res := range resSearch {
|
||
require.LessOrEqual(t, res.ResultCount, common.DefaultLimit)
|
||
if res.ResultCount == common.DefaultLimit {
|
||
common.CheckOutputFields(t, outputFields, resSearch[0].Fields)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// test search with invalid sparse vector
|
||
func TestSearchInvalidSparseVector(t *testing.T) {
|
||
t.Parallel()
|
||
|
||
idxInverted := index.NewGenericIndex(common.DefaultSparseVecFieldName, map[string]string{"drop_ratio_build": "0.2", index.MetricTypeKey: "IP", index.IndexTypeKey: "SPARSE_INVERTED_INDEX"})
|
||
idxWand := index.NewGenericIndex(common.DefaultSparseVecFieldName, map[string]string{"drop_ratio_build": "0.3", index.MetricTypeKey: "IP", index.IndexTypeKey: "SPARSE_WAND"})
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, idx := range []index.Index{idxInverted, idxWand} {
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithSparseMaxLen(128))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{common.DefaultSparseVecFieldName: idx}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
_, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, []entity.Vector{}).WithConsistencyLevel(entity.ClStrong))
|
||
common.CheckErr(t, errSearch, false, "nq (number of search vector per search request) should be in range [1, 16384]")
|
||
|
||
positions := make([]uint32, 100)
|
||
values := make([]float32, 100)
|
||
for i := 0; i < 100; i++ {
|
||
positions[i] = uint32(1)
|
||
values[i] = rand.Float32()
|
||
}
|
||
vector, _ := entity.NewSliceSparseEmbedding(positions, values)
|
||
_, errSearch2 := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, []entity.Vector{vector}).WithConsistencyLevel(entity.ClStrong))
|
||
common.CheckErr(t, errSearch2, false, "Invalid sparse row: id should be strict ascending")
|
||
}
|
||
}
|
||
|
||
// test search with empty sparse vector
|
||
func TestSearchWithEmptySparseVector(t *testing.T) {
|
||
t.Parallel()
|
||
idxInverted := index.NewSparseInvertedIndex(entity.IP, 0.1)
|
||
idxWand := index.NewSparseWANDIndex(entity.IP, 0.1)
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, idx := range []index.Index{idxInverted, idxWand} {
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithSparseMaxLen(128))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{common.DefaultSparseVecFieldName: idx}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// An empty sparse vector is considered to be uncorrelated with any other vector.
|
||
vector1, err := entity.NewSliceSparseEmbedding([]uint32{}, []float32{})
|
||
common.CheckErr(t, err, true)
|
||
searchRes, errSearch1 := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, []entity.Vector{vector1}).WithConsistencyLevel(entity.ClStrong))
|
||
common.CheckErr(t, errSearch1, true)
|
||
common.CheckSearchResult(t, searchRes, 1, 0)
|
||
}
|
||
}
|
||
|
||
// test search from empty sparse vectors collection
|
||
func TestSearchFromEmptySparseVector(t *testing.T) {
|
||
t.Skip("https://github.com/milvus-io/milvus/issues/33952")
|
||
t.Skip("https://github.com/zilliztech/knowhere/issues/774")
|
||
idxInverted := index.NewSparseInvertedIndex(entity.IP, 0.1)
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, idx := range []index.Index{idxInverted} {
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithSparseMaxLen(128).TWithStart(common.DefaultNb))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{common.DefaultSparseVecFieldName: idx}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// insert sparse vector: empty position and values
|
||
columnOpt := hp.TNewDataOption()
|
||
data := []column.Column{
|
||
hp.GenColumnData(common.DefaultNb, entity.FieldTypeInt64, *columnOpt),
|
||
hp.GenColumnData(common.DefaultNb, entity.FieldTypeVarChar, *columnOpt),
|
||
}
|
||
sparseVecs := make([]entity.SparseEmbedding, 0, common.DefaultNb)
|
||
for i := 0; i < common.DefaultNb; i++ {
|
||
vec, _ := entity.NewSliceSparseEmbedding([]uint32{}, []float32{})
|
||
sparseVecs = append(sparseVecs, vec)
|
||
}
|
||
|
||
data = append(data, column.NewColumnSparseVectors(common.DefaultSparseVecFieldName, sparseVecs))
|
||
insertRes, err := mc.Insert(ctx, client.NewColumnBasedInsertOption(schema.CollectionName, data...))
|
||
common.CheckErr(t, err, true)
|
||
require.EqualValues(t, common.DefaultNb, insertRes.InsertCount)
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
|
||
// search vector is or not empty sparse vector
|
||
vector1, _ := entity.NewSliceSparseEmbedding([]uint32{}, []float32{})
|
||
vector2, _ := entity.NewSliceSparseEmbedding([]uint32{0, 2, 5, 10, 100}, []float32{rand.Float32(), rand.Float32(), rand.Float32(), rand.Float32(), rand.Float32()})
|
||
|
||
// search from sparse collection: part normal sparse vectors, part empty sparse
|
||
// excepted: The empty vector is not related to any other vector, so it will not be returned,and alsopty obtained as the search vector.
|
||
for limit, vector := range map[int]entity.Vector{0: vector1, common.DefaultLimit: vector2} {
|
||
searchRes, errSearch1 := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, []entity.Vector{vector}).WithConsistencyLevel(entity.ClStrong))
|
||
common.CheckErr(t, errSearch1, true)
|
||
common.CheckSearchResult(t, searchRes, 1, limit)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestSearchSparseVectorPagination(t *testing.T) {
|
||
t.Parallel()
|
||
idxInverted := index.NewGenericIndex(common.DefaultSparseVecFieldName, map[string]string{"drop_ratio_build": "0.2", index.MetricTypeKey: "IP", index.IndexTypeKey: "SPARSE_INVERTED_INDEX"})
|
||
idxWand := index.NewGenericIndex(common.DefaultSparseVecFieldName, map[string]string{"drop_ratio_build": "0.3", index.MetricTypeKey: "IP", index.IndexTypeKey: "SPARSE_WAND"})
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
for _, idx := range []index.Index{idxInverted, idxWand} {
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithSparseMaxLen(128))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema).TWithFieldIndex(map[string]index.Index{common.DefaultSparseVecFieldName: idx}))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
|
||
// search
|
||
queryVec := hp.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeSparseVector)
|
||
resSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, queryVec).WithConsistencyLevel(entity.ClStrong).
|
||
WithOutputFields("*"))
|
||
common.CheckErr(t, errSearch, true)
|
||
require.Len(t, resSearch, common.DefaultNq)
|
||
|
||
pageSearch, errSearch := mc.Search(ctx, client.NewSearchOption(schema.CollectionName, common.DefaultLimit, queryVec).WithConsistencyLevel(entity.ClStrong).
|
||
WithOutputFields("*").WithOffset(5))
|
||
common.CheckErr(t, errSearch, true)
|
||
require.Len(t, pageSearch, common.DefaultNq)
|
||
for i := 0; i < len(resSearch); i++ {
|
||
if resSearch[i].ResultCount == common.DefaultLimit && pageSearch[i].ResultCount == 5 {
|
||
require.Equal(t, resSearch[i].IDs.(*column.ColumnInt64).Data()[5:], pageSearch[i].IDs.(*column.ColumnInt64).Data())
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// test sparse vector unsupported search: TODO iterator search
|
||
func TestSearchSparseVectorNotSupported(t *testing.T) {
|
||
t.Skip("Go-sdk support iterator search in progress")
|
||
}
|
||
|
||
func TestRangeSearchSparseVector(t *testing.T) {
|
||
t.Skip("Waiting for support range search")
|
||
ctx := hp.CreateContext(t, time.Second*common.DefaultTimeout*2)
|
||
mc := createDefaultMilvusClient(ctx, t)
|
||
|
||
prepare, schema := hp.CollPrepare.CreateCollection(ctx, t, mc, hp.NewCreateCollectionParams(hp.Int64VarcharSparseVec), hp.TNewFieldsOption(), hp.TNewSchemaOption().
|
||
TWithEnableDynamicField(true))
|
||
prepare.CreateIndex(ctx, t, mc, hp.TNewIndexParams(schema))
|
||
prepare.Load(ctx, t, mc, hp.NewLoadParams(schema.CollectionName))
|
||
prepare.InsertData(ctx, t, mc, hp.NewInsertParams(schema), hp.TNewDataOption().TWithSparseMaxLen(128))
|
||
prepare.FlushData(ctx, t, mc, schema.CollectionName)
|
||
|
||
// TODO range search
|
||
}
|