2626 DeleteQueryParameters ,
2727 DocumentSchema ,
2828 DocumentWriteParameters ,
29- SearchParameters ,
29+ MultiSearchParameters ,
3030)
31+ from typesense .types .multi_search import MultiSearchRequestSchema
3132
3233from langchain_typesense ._codec import (
3334 build_vector_query ,
@@ -239,7 +240,7 @@ def _build_search_parameters(
239240 ef : int | None ,
240241 flat_search_cutoff : int | None ,
241242 include_vectors : bool ,
242- ) -> SearchParameters :
243+ ) -> MultiSearchParameters :
243244 """Build a vector search request while protecting managed parameters."""
244245 self ._validate_k (k )
245246 vector_query = self ._build_vector_query (
@@ -281,7 +282,8 @@ def _build_search_parameters(
281282 filter_by = self ._to_filter_by (filter )
282283 if filter_by :
283284 parameters ["filter_by" ] = filter_by
284- return cast (SearchParameters , parameters )
285+ parameters ["collection" ] = self ._collection_name
286+ return cast (MultiSearchParameters , parameters )
285287
286288 def _build_vector_query (
287289 self ,
@@ -352,7 +354,7 @@ def _build_hybrid_search_parameters(
352354 distance_threshold : float | None ,
353355 ef : int | None ,
354356 flat_search_cutoff : int | None ,
355- ) -> SearchParameters :
357+ ) -> MultiSearchParameters :
356358 """Build a Typesense request that fuses keyword and vector ranking."""
357359 parameters : dict [str , Any ] = {
358360 "q" : query ,
@@ -376,7 +378,8 @@ def _build_hybrid_search_parameters(
376378 filter_by = self ._to_filter_by (filter )
377379 if filter_by :
378380 parameters ["filter_by" ] = filter_by
379- return cast (SearchParameters , parameters )
381+ parameters ["collection" ] = self ._collection_name
382+ return cast (MultiSearchParameters , parameters )
380383
381384 def _parse_search_response (
382385 self ,
@@ -405,6 +408,49 @@ def _parse_hybrid_search_response(
405408 metadata_key = self ._metadata_key ,
406409 )
407410
411+ def _build_multi_search_request (
412+ self , parameters : MultiSearchParameters
413+ ) -> MultiSearchRequestSchema :
414+ """Wrap one collection search for the Typesense multi-search endpoint."""
415+ return {"searches" : [parameters ]}
416+
417+ @staticmethod
418+ def _extract_multi_search_result (response : object ) -> Mapping [str , Any ]:
419+ """Extract the single result returned for a one-query multi-search."""
420+ if not isinstance (response , Mapping ):
421+ raise TypesenseVectorStoreError (
422+ "Typesense multi-search response must be an object with one result."
423+ )
424+ results = response .get ("results" )
425+ if (
426+ not isinstance (results , list )
427+ or len (results ) != 1
428+ or not isinstance (results [0 ], Mapping )
429+ ):
430+ raise TypesenseVectorStoreError (
431+ "Typesense multi-search response must contain exactly one result."
432+ )
433+ result = results [0 ]
434+ if result .get ("code" ) == 404 :
435+ raise ObjectNotFound (str (result .get ("error" , "Collection not found." )))
436+ return result
437+
438+ def _perform_search (self , parameters : MultiSearchParameters ) -> Mapping [str , Any ]:
439+ """Run one search through Typesense's POST-based multi-search endpoint."""
440+ response = self ._require_sync_client ().multi_search .perform (
441+ self ._build_multi_search_request (parameters )
442+ )
443+ return self ._extract_multi_search_result (response )
444+
445+ async def _aperform_search (self , parameters : MultiSearchParameters ) -> Mapping [str , Any ]:
446+ """Run one search through the native async multi-search endpoint."""
447+ if self ._async_client is None : # pragma: no cover - guarded by callers
448+ raise RuntimeError ("An asynchronous Typesense client is required." )
449+ response = await self ._async_client .multi_search .perform (
450+ self ._build_multi_search_request (parameters )
451+ )
452+ return self ._extract_multi_search_result (response )
453+
408454 # ------------------------------------------------------------------
409455 # Collection management
410456 # ------------------------------------------------------------------
@@ -817,11 +863,7 @@ def _search_by_vector(
817863 flat_search_cutoff = flat_search_cutoff ,
818864 include_vectors = include_vectors ,
819865 )
820- response = (
821- self ._require_sync_client ()
822- .collections [self ._collection_name ]
823- .documents .search (parameters )
824- )
866+ response = self ._perform_search (parameters )
825867 return self ._parse_search_response (response , include_vectors = include_vectors )
826868
827869 async def _asearch_by_vector (
@@ -863,9 +905,7 @@ async def _asearch_by_vector(
863905 flat_search_cutoff = flat_search_cutoff ,
864906 include_vectors = include_vectors ,
865907 )
866- response = await self ._async_client .collections [self ._collection_name ].documents .search (
867- parameters
868- )
908+ response = await self ._aperform_search (parameters )
869909 return self ._parse_search_response (response , include_vectors = include_vectors )
870910
871911 def similarity_search (
@@ -1257,11 +1297,7 @@ def hybrid_search_with_score(
12571297 ef = ef ,
12581298 flat_search_cutoff = flat_search_cutoff ,
12591299 )
1260- response = (
1261- self ._require_sync_client ()
1262- .collections [self ._collection_name ]
1263- .documents .search (parameters )
1264- )
1300+ response = self ._perform_search (parameters )
12651301 return self ._parse_hybrid_search_response (response )
12661302
12671303 async def ahybrid_search (
@@ -1352,9 +1388,7 @@ async def ahybrid_search_with_score(
13521388 ef = ef ,
13531389 flat_search_cutoff = flat_search_cutoff ,
13541390 )
1355- response = await self ._async_client .collections [self ._collection_name ].documents .search (
1356- parameters
1357- )
1391+ response = await self ._aperform_search (parameters )
13581392 return self ._parse_hybrid_search_response (response )
13591393
13601394 # ------------------------------------------------------------------
0 commit comments