@@ -1282,6 +1282,75 @@ async def aggregate(self, *args, **kwargs) -> "AggregateResult":
12821282 except Exception as e :
12831283 raise RedisSearchError (f"Error while aggregating: { str (e )} " ) from e
12841284
1285+ async def batch_search (
1286+ self , queries : List [str ], batch_size : int = 100 , ** query_params
1287+ ) -> List [List [Dict [str , Any ]]]:
1288+ """Perform a search against the index for multiple queries.
1289+
1290+ This method takes a list of queries and returns a list of search results.
1291+ The results are returned in the same order as the queries.
1292+
1293+ Args:
1294+ queries (List[str]): The queries to search for.
1295+ batch_size (int, optional): The number of queries to search for at a time.
1296+ Defaults to 100.
1297+ query_params (dict, optional): The query parameters to pass to the search
1298+ for each query.
1299+
1300+ Returns:
1301+ List[List[Dict[str, Any]]]: The search results.
1302+ """
1303+ all_parsed = []
1304+ client = await self ._get_client ()
1305+ search = client .ft (self .schema .index .name )
1306+ options = {}
1307+ if get_protocol_version (client ) not in ["3" , 3 ]:
1308+ options [NEVER_DECODE ] = True
1309+
1310+ for i in range (0 , len (queries ), batch_size ):
1311+ batch_queries = queries [i : i + batch_size ]
1312+
1313+ # redis-py doesn't support calling `search` in a pipeline,
1314+ # so we need to manually execute each command in a pipeline
1315+ # and parse the results
1316+ async with client .pipeline (transaction = False ) as pipe :
1317+ batch_built_queries = []
1318+ for query in batch_queries :
1319+ query_args , q = search ._mk_query_args ( # type: ignore
1320+ query , query_params = query_params
1321+ )
1322+ batch_built_queries .append (q )
1323+ pipe .execute_command (
1324+ "FT.SEARCH" ,
1325+ * query_args ,
1326+ ** options ,
1327+ )
1328+
1329+ st = time .time ()
1330+ results = await pipe .execute ()
1331+
1332+ # We don't know how long each query took, so we'll use the total time
1333+ # for all queries in the batch as the duration for each query
1334+ duration = (time .time () - st ) * 1000.0
1335+
1336+ for i , query_results in enumerate (results ):
1337+ _built_query = batch_built_queries [i ]
1338+ parsed_raw = search ._parse_search ( # type: ignore
1339+ query_results ,
1340+ query = _built_query ,
1341+ duration = duration ,
1342+ )
1343+ parsed = process_results (
1344+ parsed_raw ,
1345+ query = _built_query ,
1346+ storage_type = self .schema .index .storage_type ,
1347+ )
1348+ # Create separate lists of parsed results for each query
1349+ # passed in to the batch_search method, so that callers can
1350+ # access the results for each query individually
1351+ all_parsed .append (parsed )
1352+ return all_parsed
1353+
12851354 async def search (self , * args , ** kwargs ) -> "Result" :
12861355 """Perform a search on this index.
12871356
0 commit comments