10
10
import com .azure .search .documents .indexes .models .VectorSearchProfile ;
11
11
import com .azure .search .documents .models .IndexDocumentsResult ;
12
12
import com .azure .search .documents .models .IndexingResult ;
13
+ import com .azure .search .documents .models .ScoringParameter ;
13
14
import com .azure .search .documents .models .SearchOptions ;
14
15
import com .azure .search .documents .models .VectorQuery ;
15
16
import com .azure .search .documents .models .VectorizableTextQuery ;
39
40
import java .util .HashSet ;
40
41
import java .util .Iterator ;
41
42
import java .util .List ;
43
+ import java .util .Vector ;
42
44
import java .util .stream .Collectors ;
43
45
import javax .annotation .Nonnull ;
44
46
import reactor .core .publisher .Flux ;
@@ -288,37 +290,40 @@ public Mono<Void> deleteBatchAsync(List<String> keys, DeleteRecordOptions option
288
290
}).collect (Collectors .toList ())).then ();
289
291
}
290
292
291
- private Mono <VectorSearchResults <Record >> searchAndMapAsync (
292
- List <VectorQuery > vectorQueries , VectorSearchOptions options ,
293
- GetRecordOptions getRecordOptions ) {
294
-
293
+ private SearchOptions configureVectorSearchOptions (
294
+ List <VectorQuery > vectorQueries , VectorSearchOptions options ) {
295
295
String filter = AzureAISearchVectorStoreCollectionSearchMapping .getInstance ()
296
296
.getFilter (options .getVectorSearchFilter (), recordDefinition );
297
297
298
298
SearchOptions searchOptions = new SearchOptions ()
299
299
.setFilter (filter )
300
300
.setTop (options .getTop ())
301
301
.setSkip (options .getSkip ())
302
- .setScoringParameters ()
303
302
.setVectorSearchOptions (new com .azure .search .documents .models .VectorSearchOptions ()
304
303
.setQueries (vectorQueries ));
305
304
306
305
if (!options .isIncludeVectors ()) {
307
306
searchOptions .setSelect (nonVectorFields .toArray (new String [0 ]));
308
307
}
309
308
309
+ return searchOptions ;
310
+ }
311
+
312
+ private Mono <VectorSearchResults <Record >> searchAndMapAsync (String query ,
313
+ SearchOptions searchOptions ,
314
+ boolean includeVectors ) {
310
315
VectorStoreRecordMapper <Record , SearchDocument > mapper = this .options
311
316
.getVectorStoreRecordMapper ();
312
317
313
- return this .searchAsyncClient .search (null , searchOptions )
318
+ return this .searchAsyncClient .search (query , searchOptions )
314
319
.flatMap (response -> {
315
320
Record record ;
316
321
317
322
// Use custom mapper if available
318
323
if (mapper != null && mapper .getStorageModelToRecordMapper () != null ) {
319
324
record = mapper
320
325
.mapStorageModelToRecord (response .getDocument (SearchDocument .class ),
321
- getRecordOptions );
326
+ new GetRecordOptions ( includeVectors ) );
322
327
} else {
323
328
record = response .getDocument (this .options .getRecordClass ());
324
329
}
@@ -329,7 +334,9 @@ record = response.getDocument(this.options.getRecordClass());
329
334
}
330
335
331
336
/**
332
- * Vectorizable text search. This method searches for records that are similar to the given text.
337
+ * Vectorizable text search. This method searches for records that are similar to the given text after vectorization.
338
+ * <p>
339
+ * Vectorizer configuration must be set up in the Azure AI Search index.
333
340
*
334
341
* @param searchText The text to search with.
335
342
* @param options The options to use for the search.
@@ -353,8 +360,9 @@ public Mono<VectorSearchResults<Record>> searchAsync(String searchText,
353
360
: firstVectorFieldName ).getEffectiveStorageName ())
354
361
.setKNearestNeighborsCount (options .getTop ()));
355
362
356
- return searchAndMapAsync (vectorQueries , options ,
357
- new GetRecordOptions (options .isIncludeVectors ()));
363
+ return searchAndMapAsync (null ,
364
+ configureVectorSearchOptions (vectorQueries , options ),
365
+ options .isIncludeVectors ());
358
366
}
359
367
360
368
/**
@@ -367,22 +375,97 @@ public Mono<VectorSearchResults<Record>> searchAsync(String searchText,
367
375
@ Override
368
376
public Mono <VectorSearchResults <Record >> searchAsync (List <Float > vector ,
369
377
VectorSearchOptions options ) {
370
- if (firstVectorFieldName == null ) {
371
- throw new SKException ("No vector fields defined. Cannot perform vector search" );
372
- }
378
+ return hybridSearchAsync (null , vector , options , null );
379
+ }
373
380
374
- if (options == null ) {
375
- options = VectorSearchOptions .createDefault (firstVectorFieldName );
381
+ /**
382
+ * Hybrid search. This method searches for records that are similar to the given text and vector.
383
+ *
384
+ * @param searchText The text to search with.
385
+ * If null, only vector search is performed.
386
+ * @param vector The vector to search with.
387
+ * If null, only full text search is performed.
388
+ * @param options The vector search options used for the search.
389
+ * @param additionalSearchOptions AzureAI search additional options.
390
+ * If Filter, Top, Skip, Select or VectorSearchOptions are not null, they will be used instead of the default options.
391
+ * <p>
392
+ * If null, default search options are used.
393
+ */
394
+ public Mono <VectorSearchResults <Record >> hybridSearchAsync (String searchText ,
395
+ List <Float > vector , VectorSearchOptions options , SearchOptions additionalSearchOptions ) {
396
+ SearchOptions searchOptions = new SearchOptions ();
397
+
398
+ if (vector != null ) {
399
+ if (firstVectorFieldName == null ) {
400
+ throw new SKException ("No vector fields defined. Cannot perform vector search" );
401
+ }
402
+
403
+ if (options == null ) {
404
+ options = VectorSearchOptions .createDefault (firstVectorFieldName );
405
+ }
406
+
407
+ List <VectorQuery > vectorQueries = new ArrayList <>();
408
+ vectorQueries .add (new VectorizedQuery (vector )
409
+ .setFields (recordDefinition .getField (options .getVectorFieldName () != null
410
+ ? options .getVectorFieldName ()
411
+ : firstVectorFieldName ).getEffectiveStorageName ())
412
+ .setKNearestNeighborsCount (options .getTop ()));
413
+
414
+ // Configure default vector search options
415
+ searchOptions = configureVectorSearchOptions (vectorQueries , options );
376
416
}
377
417
378
- List <VectorQuery > vectorQueries = new ArrayList <>();
379
- vectorQueries .add (new VectorizedQuery (vector )
380
- .setFields (recordDefinition .getField (options .getVectorFieldName () != null
381
- ? options .getVectorFieldName ()
382
- : firstVectorFieldName ).getEffectiveStorageName ())
383
- .setKNearestNeighborsCount (options .getTop ()));
418
+ // Configure additional search options
419
+ if (additionalSearchOptions != null ) {
420
+ searchOptions
421
+ .setQueryType (additionalSearchOptions .getQueryType ())
422
+ .setSemanticSearchOptions (additionalSearchOptions .getSemanticSearchOptions ())
423
+ .setFacets (additionalSearchOptions .getFacets () != null
424
+ ? additionalSearchOptions .getFacets ().toArray (new String [0 ])
425
+ : null )
426
+ .setHighlightFields (additionalSearchOptions .getHighlightFields () != null
427
+ ? additionalSearchOptions .getHighlightFields ().toArray (new String [0 ])
428
+ : null )
429
+ .setHighlightPreTag (additionalSearchOptions .getHighlightPreTag ())
430
+ .setHighlightPostTag (additionalSearchOptions .getHighlightPostTag ())
431
+ .setMinimumCoverage (additionalSearchOptions .getMinimumCoverage ())
432
+ .setOrderBy (additionalSearchOptions .getOrderBy () != null
433
+ ? additionalSearchOptions .getOrderBy ().toArray (new String [0 ])
434
+ : null )
435
+ .setScoringParameters (additionalSearchOptions .getScoringParameters () != null
436
+ ? additionalSearchOptions .getScoringParameters ().stream ()
437
+ .map (s -> new ScoringParameter (s .getName (), s .getValues ()))
438
+ .toArray (ScoringParameter []::new )
439
+ : null )
440
+ .setScoringProfile (additionalSearchOptions .getScoringProfile ())
441
+ .setSearchFields (additionalSearchOptions .getSearchFields () != null
442
+ ? additionalSearchOptions .getSearchFields ().toArray (new String [0 ])
443
+ : null )
444
+ .setIncludeTotalCount (additionalSearchOptions .isTotalCountIncluded ())
445
+ .setSearchMode (additionalSearchOptions .getSearchMode ())
446
+ .setScoringStatistics (additionalSearchOptions .getScoringStatistics ())
447
+ .setSessionId (additionalSearchOptions .getSessionId ());
448
+
449
+ // Override default vector options if provided
450
+ if (additionalSearchOptions .getFilter () != null ) {
451
+ searchOptions .setFilter (additionalSearchOptions .getFilter ());
452
+ }
453
+ if (additionalSearchOptions .getTop () != null ) {
454
+ searchOptions .setTop (additionalSearchOptions .getTop ());
455
+ }
456
+ if (additionalSearchOptions .getSkip () != null ) {
457
+ searchOptions .setSkip (additionalSearchOptions .getSkip ());
458
+ }
459
+ if (additionalSearchOptions .getVectorSearchOptions () != null ) {
460
+ searchOptions
461
+ .setVectorSearchOptions (additionalSearchOptions .getVectorSearchOptions ());
462
+ }
463
+ if (additionalSearchOptions .getSelect () != null ) {
464
+ searchOptions .setSelect (additionalSearchOptions .getSelect ().toArray (new String [0 ]));
465
+ }
466
+ }
384
467
385
- return searchAndMapAsync (vectorQueries , options ,
386
- new GetRecordOptions ( options .isIncludeVectors () ));
468
+ return searchAndMapAsync (searchText , searchOptions ,
469
+ options != null && options .isIncludeVectors ());
387
470
}
388
471
}
0 commit comments