github.com/milvus-io/milvus-sdk-go/v2@v2.4.1/test/testcases/groupby_search_test.go (about) 1 //go:build L0 2 3 package testcases 4 5 import ( 6 "context" 7 "fmt" 8 "log" 9 "testing" 10 "time" 11 12 "github.com/milvus-io/milvus-sdk-go/v2/client" 13 "github.com/milvus-io/milvus-sdk-go/v2/test/base" 14 15 "github.com/milvus-io/milvus-sdk-go/v2/entity" 16 "github.com/milvus-io/milvus-sdk-go/v2/test/common" 17 18 "github.com/stretchr/testify/require" 19 ) 20 21 // Generate groupBy-supported vector indexes 22 func genGroupByVectorIndex(metricType entity.MetricType) []entity.Index { 23 nlist := 128 24 idxFlat, _ := entity.NewIndexFlat(metricType) 25 idxIvfFlat, _ := entity.NewIndexIvfFlat(metricType, nlist) 26 idxHnsw, _ := entity.NewIndexHNSW(metricType, 8, 96) 27 28 allFloatIndex := []entity.Index{ 29 idxFlat, 30 idxIvfFlat, 31 idxHnsw, 32 } 33 return allFloatIndex 34 } 35 36 // Generate groupBy-supported vector indexes 37 func genGroupByBinaryIndex(metricType entity.MetricType) []entity.Index { 38 nlist := 128 39 idxBinFlat, _ := entity.NewIndexBinFlat(metricType, nlist) 40 idxBinIvfFlat, _ := entity.NewIndexBinIvfFlat(metricType, nlist) 41 42 allFloatIndex := []entity.Index{ 43 idxBinFlat, 44 idxBinIvfFlat, 45 } 46 return allFloatIndex 47 } 48 49 func genUnsupportedFloatGroupByIndex() []entity.Index { 50 idxIvfSq8, _ := entity.NewIndexIvfSQ8(entity.L2, 128) 51 idxIvfPq, _ := entity.NewIndexIvfPQ(entity.L2, 128, 16, 8) 52 idxScann, _ := entity.NewIndexSCANN(entity.L2, 16, false) 53 idxDiskAnn, _ := entity.NewIndexDISKANN(entity.L2) 54 return []entity.Index{ 55 idxIvfSq8, 56 idxIvfPq, 57 idxScann, 58 idxDiskAnn, 59 } 60 } 61 62 func prepareDataForGroupBySearch(t *testing.T, loopInsert int, insertNi int, idx entity.Index, withGrowing bool) (*base.MilvusClient, context.Context, string) { 63 ctx := createContext(t, time.Second*common.DefaultTimeout*5) 64 mc := createMilvusClient(ctx, t) 65 66 // create collection with all datatype 67 cp := CollectionParams{CollectionFieldsType: AllFields, AutoID: false, EnableDynamicField: true, 68 ShardsNum: common.DefaultShards, Dim: common.DefaultDim} 69 collName := createCollection(ctx, t, mc, cp) 70 71 // insert 72 dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: AllFields, 73 start: 0, nb: insertNi, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} 74 for i := 0; i < loopInsert; i++ { 75 _, _ = insertData(ctx, t, mc, dp) 76 } 77 78 if !withGrowing { 79 mc.Flush(ctx, collName, false) 80 } 81 82 //create scalar index 83 supportedGroupByFields := []string{common.DefaultIntFieldName, common.DefaultInt8FieldName, common.DefaultInt16FieldName, 84 common.DefaultInt32FieldName, common.DefaultVarcharFieldName, common.DefaultBoolFieldName} 85 for _, groupByField := range supportedGroupByFields { 86 err := mc.CreateIndex(ctx, collName, groupByField, entity.NewScalarIndex(), false) 87 common.CheckErr(t, err, true) 88 } 89 90 // create vector index 91 idxHnsw, _ := entity.NewIndexHNSW(entity.COSINE, 8, 96) 92 indexBinary, _ := entity.NewIndexBinIvfFlat(entity.JACCARD, 64) 93 for _, fieldName := range common.AllVectorsFieldsName { 94 if fieldName == common.DefaultFloatVecFieldName { 95 err := mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idx, false) 96 common.CheckErr(t, err, true) 97 } else if fieldName == common.DefaultBinaryVecFieldName { 98 err := mc.CreateIndex(ctx, collName, fieldName, indexBinary, false) 99 common.CheckErr(t, err, true) 100 } else { 101 err := mc.CreateIndex(ctx, collName, fieldName, idxHnsw, false) 102 common.CheckErr(t, err, true) 103 } 104 } 105 106 // load collection 107 err := mc.LoadCollection(ctx, collName, false) 108 common.CheckErr(t, err, true) 109 110 return mc, ctx, collName 111 } 112 113 // create coll with all datatype -> build all supported index 114 // -> search with WithGroupByField (int* + varchar + bool 115 // -> verify every top passage is the top of whole group 116 // output_fields: pk + groupBy 117 func TestSearchGroupByFloatDefault(t *testing.T) { 118 t.Parallel() 119 for _, metricType := range common.SupportFloatMetricType { 120 for _, idx := range genGroupByVectorIndex(metricType) { 121 // prepare data 122 mc, ctx, collName := prepareDataForGroupBySearch(t, 100, 200, idx, false) 123 124 // search params 125 queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) 126 sp, _ := entity.NewIndexIvfFlatSearchParam(32) 127 128 // search with groupBy field 129 supportedGroupByFields := []string{common.DefaultIntFieldName, "int8", "int16", "int32", "varchar", "bool"} 130 for _, groupByField := range supportedGroupByFields { 131 resGroupBy, _ := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, groupByField}, queryVec, 132 common.DefaultFloatVecFieldName, metricType, common.DefaultTopK, sp, client.WithGroupByField(groupByField)) 133 134 // verify each topK entity is the top1 of the whole group 135 hitsNum := 0 136 total := 0 137 for i := 0; i < common.DefaultNq; i++ { 138 for j := 0; j < resGroupBy[i].ResultCount; j++ { 139 groupByValue, _ := resGroupBy[i].GroupByValue.Get(j) 140 pkValue, _ := resGroupBy[i].IDs.GetAsInt64(j) 141 var expr string 142 if groupByField == "varchar" { 143 expr = fmt.Sprintf("%s == '%v' ", groupByField, groupByValue) 144 } else { 145 expr = fmt.Sprintf("%s == %v", groupByField, groupByValue) 146 } 147 // search filter with groupByValue is the top1 148 resFilter, _ := mc.Search(ctx, collName, []string{}, expr, []string{common.DefaultIntFieldName, 149 groupByField}, []entity.Vector{queryVec[i]}, common.DefaultFloatVecFieldName, metricType, 1, sp) 150 filterTop1Pk, _ := resFilter[0].IDs.GetAsInt64(0) 151 //log.Printf("Search top1 with %s: groupByValue: %v, pkValue: %d. The returned pk by filter search is: %d", 152 // groupByField, groupByValue, pkValue, filterTop1Pk) 153 if filterTop1Pk == pkValue { 154 hitsNum += 1 155 } 156 total += 1 157 } 158 } 159 160 // verify hits rate 161 hitsRate := float32(hitsNum) / float32(total) 162 _str := fmt.Sprintf("GroupBy search with field %s, nq=%d and limit=%d , then hitsNum= %d, hitsRate=%v\n", 163 groupByField, common.DefaultNq, common.DefaultTopK, hitsNum, hitsRate) 164 log.Println(_str) 165 if groupByField != "bool" { 166 require.GreaterOrEqualf(t, hitsRate, float32(0.8), _str) 167 } 168 } 169 } 170 } 171 } 172 173 // test groupBy search sparse vector 174 func TestGroupBySearchSparseVector(t *testing.T) { 175 t.Parallel() 176 idxInverted, _ := entity.NewIndexSparseInverted(entity.IP, 0.3) 177 idxWand, _ := entity.NewIndexSparseWAND(entity.IP, 0.2) 178 for _, idx := range []entity.Index{idxInverted, idxWand} { 179 ctx := createContext(t, time.Second*common.DefaultTimeout*2) 180 // connect 181 mc := createMilvusClient(ctx, t) 182 183 // create -> insert [0, 3000) -> flush -> index -> load 184 cp := CollectionParams{CollectionFieldsType: Int64VarcharSparseVec, AutoID: false, EnableDynamicField: true, 185 ShardsNum: common.DefaultShards, Dim: common.DefaultDim, MaxLength: common.TestMaxLen} 186 collName := createCollection(ctx, t, mc, cp, client.WithConsistencyLevel(entity.ClStrong)) 187 188 // insert data 189 dp := DataParams{DoInsert: true, CollectionName: collName, CollectionFieldsType: Int64VarcharSparseVec, start: 0, 190 nb: 200, dim: common.DefaultDim, EnableDynamicField: true} 191 for i := 0; i < 100; i++ { 192 _, _ = insertData(ctx, t, mc, dp) 193 } 194 mc.Flush(ctx, collName, false) 195 196 // index and load 197 idxHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) 198 mc.CreateIndex(ctx, collName, common.DefaultFloatVecFieldName, idxHnsw, false) 199 mc.CreateIndex(ctx, collName, common.DefaultSparseVecFieldName, idx, false) 200 mc.LoadCollection(ctx, collName, false) 201 202 // groupBy search 203 queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeSparseVector) 204 sp, _ := entity.NewIndexSparseInvertedSearchParam(0.2) 205 resGroupBy, _ := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName}, queryVec, 206 common.DefaultSparseVecFieldName, entity.IP, common.DefaultTopK, sp, client.WithGroupByField(common.DefaultVarcharFieldName)) 207 208 // verify each topK entity is the top1 of the whole group 209 hitsNum := 0 210 total := 0 211 for i := 0; i < common.DefaultNq; i++ { 212 if resGroupBy[i].ResultCount > 0 { 213 for j := 0; j < resGroupBy[i].ResultCount; j++ { 214 groupByValue, _ := resGroupBy[i].GroupByValue.Get(j) 215 pkValue, _ := resGroupBy[i].IDs.GetAsInt64(j) 216 expr := fmt.Sprintf("%s == '%v' ", common.DefaultVarcharFieldName, groupByValue) 217 // search filter with groupByValue is the top1 218 resFilter, _ := mc.Search(ctx, collName, []string{}, expr, []string{common.DefaultIntFieldName, 219 common.DefaultVarcharFieldName}, []entity.Vector{queryVec[i]}, common.DefaultSparseVecFieldName, entity.IP, 1, sp) 220 filterTop1Pk, _ := resFilter[0].IDs.GetAsInt64(0) 221 log.Printf("Search top1 with %s: groupByValue: %v, pkValue: %d. The returned pk by filter search is: %d", 222 common.DefaultVarcharFieldName, groupByValue, pkValue, filterTop1Pk) 223 if filterTop1Pk == pkValue { 224 hitsNum += 1 225 } 226 total += 1 227 } 228 } 229 } 230 231 // verify hits rate 232 hitsRate := float32(hitsNum) / float32(total) 233 _str := fmt.Sprintf("GroupBy search with field %s, nq=%d and limit=%d , then hitsNum= %d, hitsRate=%v\n", 234 common.DefaultVarcharFieldName, common.DefaultNq, common.DefaultTopK, hitsNum, hitsRate) 235 log.Println(_str) 236 require.GreaterOrEqualf(t, hitsRate, float32(0.8), _str) 237 } 238 } 239 240 // binary vector -> not supported 241 func TestSearchGroupByBinaryDefault(t *testing.T) { 242 t.Parallel() 243 for _, metricType := range common.SupportBinIvfFlatMetricType { 244 for _, idx := range genGroupByBinaryIndex(metricType) { 245 ctx := createContext(t, time.Second*common.DefaultTimeout) 246 // connect 247 mc := createMilvusClient(ctx, t) 248 249 // create collection with all datatype 250 cp := CollectionParams{CollectionFieldsType: VarcharBinaryVec, AutoID: false, EnableDynamicField: true, 251 ShardsNum: common.DefaultShards, Dim: common.DefaultDim} 252 collName := createCollection(ctx, t, mc, cp) 253 254 // insert 255 dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: VarcharBinaryVec, 256 start: 0, nb: 1000, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} 257 for i := 0; i < 2; i++ { 258 _, _ = insertData(ctx, t, mc, dp) 259 } 260 mc.Flush(ctx, collName, false) 261 262 // create index and load 263 err := mc.CreateIndex(ctx, collName, common.DefaultBinaryVecFieldName, idx, false) 264 common.CheckErr(t, err, true) 265 err = mc.LoadCollection(ctx, collName, false) 266 common.CheckErr(t, err, true) 267 268 // search params 269 queryVec := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeBinaryVector) 270 sp, _ := entity.NewIndexBinIvfFlatSearchParam(32) 271 supportedGroupByFields := []string{common.DefaultVarcharFieldName, common.DefaultBinaryVecFieldName} 272 273 // search with groupBy field 274 for _, groupByField := range supportedGroupByFields { 275 _, err := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultVarcharFieldName, groupByField}, queryVec, 276 common.DefaultBinaryVecFieldName, metricType, common.DefaultTopK, sp, client.WithGroupByField(groupByField)) 277 common.CheckErr(t, err, false, "not support search_group_by operation based on binary vector column") 278 } 279 } 280 } 281 } 282 283 // binary vector -> growing segments, maybe brute force 284 // default Bounded ConsistencyLevel -> succ ?? 285 // strong ConsistencyLevel -> error 286 func TestSearchGroupByBinaryGrowing(t *testing.T) { 287 t.Parallel() 288 for _, metricType := range common.SupportBinIvfFlatMetricType { 289 idxBinIvfFlat, _ := entity.NewIndexBinIvfFlat(metricType, 128) 290 ctx := createContext(t, time.Second*common.DefaultTimeout) 291 // connect 292 mc := createMilvusClient(ctx, t) 293 294 // create collection with all datatype 295 cp := CollectionParams{CollectionFieldsType: VarcharBinaryVec, AutoID: false, EnableDynamicField: true, 296 ShardsNum: common.DefaultShards, Dim: common.DefaultDim} 297 collName := createCollection(ctx, t, mc, cp) 298 299 // create index and load 300 err := mc.CreateIndex(ctx, collName, common.DefaultBinaryVecFieldName, idxBinIvfFlat, false) 301 common.CheckErr(t, err, true) 302 err = mc.LoadCollection(ctx, collName, false) 303 common.CheckErr(t, err, true) 304 305 // insert 306 dp := DataParams{CollectionName: collName, PartitionName: "", CollectionFieldsType: VarcharBinaryVec, 307 start: 0, nb: 1000, dim: common.DefaultDim, EnableDynamicField: true, WithRows: false} 308 _, _ = insertData(ctx, t, mc, dp) 309 310 // search params 311 queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeBinaryVector) 312 sp, _ := entity.NewIndexBinIvfFlatSearchParam(64) 313 supportedGroupByFields := []string{common.DefaultVarcharFieldName} 314 315 // search with groupBy field 316 for _, groupByField := range supportedGroupByFields { 317 _, err := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultVarcharFieldName, 318 groupByField}, queryVec, common.DefaultBinaryVecFieldName, metricType, common.DefaultTopK, sp, 319 client.WithGroupByField(groupByField), client.WithSearchQueryConsistencyLevel(entity.ClStrong)) 320 common.CheckErr(t, err, false, "not support search_group_by operation based on binary vector column") 321 } 322 } 323 } 324 325 // groupBy in growing segments, maybe growing index or brute force 326 func TestSearchGroupByFloatGrowing(t *testing.T) { 327 for _, metricType := range common.SupportFloatMetricType { 328 idxHnsw, _ := entity.NewIndexHNSW(metricType, 8, 96) 329 mc, ctx, collName := prepareDataForGroupBySearch(t, 100, 200, idxHnsw, true) 330 331 // search params 332 queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) 333 sp, _ := entity.NewIndexIvfFlatSearchParam(32) 334 supportedGroupByFields := []string{common.DefaultIntFieldName, "int8", "int16", "int32", "varchar", "bool"} 335 336 // search with groupBy field 337 hitsNum := 0 338 total := 0 339 for _, groupByField := range supportedGroupByFields { 340 resGroupBy, _ := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, groupByField}, queryVec, 341 common.DefaultFloatVecFieldName, metricType, common.DefaultTopK, sp, client.WithGroupByField(groupByField), 342 client.WithSearchQueryConsistencyLevel(entity.ClStrong)) 343 344 // verify each topK entity is the top1 in the group 345 for i := 0; i < common.DefaultNq; i++ { 346 for j := 0; j < resGroupBy[i].ResultCount; j++ { 347 groupByValue, _ := resGroupBy[i].GroupByValue.Get(j) 348 pkValue, _ := resGroupBy[i].IDs.GetAsInt64(j) 349 var expr string 350 if groupByField == "varchar" { 351 expr = fmt.Sprintf("%s == '%v' ", groupByField, groupByValue) 352 } else { 353 expr = fmt.Sprintf("%s == %v", groupByField, groupByValue) 354 } 355 resFilter, _ := mc.Search(ctx, collName, []string{}, expr, []string{common.DefaultIntFieldName, 356 groupByField}, []entity.Vector{queryVec[i]}, common.DefaultFloatVecFieldName, metricType, 1, sp, client.WithSearchQueryConsistencyLevel(entity.ClStrong)) 357 358 // search filter with groupByValue is the top1 359 filterTop1Pk, _ := resFilter[0].IDs.GetAsInt64(0) 360 //log.Printf("Search top1 with %s: groupByValue: %v, pkValue: %d. The returned pk by filter search is: %d", 361 // groupByField, groupByValue, pkValue, filterTop1Pk) 362 if filterTop1Pk == pkValue { 363 hitsNum += 1 364 } 365 total += 1 366 } 367 } 368 // verify hits rate 369 hitsRate := float32(hitsNum) / float32(total) 370 _str := fmt.Sprintf("GroupBy search with field %s, nq=%d and limit=%d , then hitsNum= %d, hitsRate=%v\n", 371 groupByField, common.DefaultNq, common.DefaultTopK, hitsNum, hitsRate) 372 log.Println(_str) 373 if groupByField != "bool" { 374 require.GreaterOrEqualf(t, hitsRate, float32(0.8), _str) 375 } 376 } 377 } 378 } 379 380 // groupBy + pagination 381 func TestSearchGroupByPagination(t *testing.T) { 382 // create index and load 383 idx, _ := entity.NewIndexHNSW(entity.COSINE, 8, 96) 384 mc, ctx, collName := prepareDataForGroupBySearch(t, 10, 1000, idx, false) 385 386 // search params 387 queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) 388 sp, _ := entity.NewIndexIvfFlatSearchParam(32) 389 var offset = int64(10) 390 391 // search pagination & groupBy 392 resGroupByPagination, _ := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName}, 393 queryVec, common.DefaultFloatVecFieldName, entity.COSINE, common.DefaultTopK, sp, 394 client.WithGroupByField(common.DefaultVarcharFieldName), client.WithOffset(offset)) 395 396 common.CheckSearchResult(t, resGroupByPagination, common.DefaultNq, common.DefaultTopK) 397 398 // search limit=origin limit + offset 399 resGroupByDefault, _ := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName}, 400 queryVec, common.DefaultFloatVecFieldName, entity.COSINE, common.DefaultTopK+int(offset), sp, 401 client.WithGroupByField(common.DefaultVarcharFieldName)) 402 for i := 0; i < common.DefaultNq; i++ { 403 require.Equal(t, resGroupByDefault[i].IDs.(*entity.ColumnInt64).Data()[10:], resGroupByPagination[i].IDs.(*entity.ColumnInt64).Data()) 404 } 405 } 406 407 // only support: "FLAT", "IVF_FLAT", "HNSW" 408 func TestSearchGroupByUnsupportedIndex(t *testing.T) { 409 t.Parallel() 410 for _, idx := range genUnsupportedFloatGroupByIndex() { 411 mc, ctx, collName := prepareDataForGroupBySearch(t, 3, 1000, idx, false) 412 // groupBy search 413 queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) 414 sp, _ := entity.NewIndexIvfFlatSearchParam(32) 415 _, err := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName}, 416 queryVec, common.DefaultFloatVecFieldName, entity.MetricType(idx.Params()["metrics_type"]), 417 common.DefaultTopK, sp, client.WithGroupByField(common.DefaultVarcharFieldName)) 418 common.CheckErr(t, err, false, "doesn't support search_group_by") 419 } 420 } 421 422 // FLOAT, DOUBLE, JSON, ARRAY 423 func TestSearchGroupByUnsupportedDataType(t *testing.T) { 424 idxHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) 425 mc, ctx, collName := prepareDataForGroupBySearch(t, 1, 1000, idxHnsw, true) 426 427 // groupBy search with unsupported field type 428 queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) 429 sp, _ := entity.NewIndexIvfFlatSearchParam(32) 430 for _, unsupportedField := range []string{"float", "double", "json", "floatVec", "int8Array", "floatArray"} { 431 _, err := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName}, 432 queryVec, common.DefaultFloatVecFieldName, entity.L2, 433 common.DefaultTopK, sp, client.WithGroupByField(unsupportedField)) 434 common.CheckErr(t, err, false, "unsupported data type") 435 } 436 } 437 438 // groupBy + iterator -> not supported 439 func TestSearchGroupByIterator(t *testing.T) { 440 // TODO: sdk support 441 } 442 443 // groupBy + range search -> not supported 444 func TestSearchGroupByRangeSearch(t *testing.T) { 445 idxHnsw, _ := entity.NewIndexHNSW(entity.COSINE, 8, 96) 446 mc, ctx, collName := prepareDataForGroupBySearch(t, 1, 1000, idxHnsw, true) 447 448 // groupBy search with range 449 queryVec := common.GenSearchVectors(common.DefaultNq, common.DefaultDim, entity.FieldTypeFloatVector) 450 sp, _ := entity.NewIndexHNSWSearchParam(50) 451 sp.AddRadius(0) 452 sp.AddRangeFilter(0.8) 453 454 // range search 455 _, err := mc.Search(ctx, collName, []string{}, "", []string{common.DefaultIntFieldName, common.DefaultVarcharFieldName}, 456 queryVec, common.DefaultFloatVecFieldName, entity.COSINE, common.DefaultTopK, sp, 457 client.WithGroupByField(common.DefaultVarcharFieldName)) 458 459 common.CheckErr(t, err, false, "Not allowed to do range-search when doing search-group-by") 460 } 461 462 // groupBy + advanced search 463 func TestSearchGroupByHybridSearch(t *testing.T) { 464 // prepare data 465 indexHnsw, _ := entity.NewIndexHNSW(entity.L2, 8, 96) 466 mc, ctx, collName := prepareDataForGroupBySearch(t, 10, 1000, indexHnsw, false) 467 468 // hybrid search with groupBy field 469 sp, _ := entity.NewIndexHNSWSearchParam(20) 470 expr := fmt.Sprintf("%s > 4", common.DefaultIntFieldName) 471 queryVec1 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) 472 queryVec2 := common.GenSearchVectors(1, common.DefaultDim, entity.FieldTypeFloatVector) 473 sReqs := []*client.ANNSearchRequest{ 474 client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec1, sp, common.DefaultTopK, client.WithOffset(2)), 475 client.NewANNSearchRequest(common.DefaultFloatVecFieldName, entity.L2, expr, queryVec2, sp, common.DefaultTopK, client.WithGroupByField("varchar")), 476 } 477 _, errSearch := mc.HybridSearch(ctx, collName, []string{}, common.DefaultTopK, []string{}, client.NewRRFReranker(), sReqs) 478 common.CheckErr(t, errSearch, false, "not support search_group_by operation in the hybrid search") 479 }