@@ -857,3 +857,245 @@ def syndump(self):
857857 """ # noqa
858858 raw = self .execute_command (SYNDUMP_CMD , self .index_name )
859859 return {raw [i ]: raw [i + 1 ] for i in range (0 , len (raw ), 2 )}
860+
861+
862+ class AsyncSearchCommands (SearchCommands ):
863+ async def info (self ):
864+ """
865+ Get info an stats about the the current index, including the number of
866+ documents, memory consumption, etc
867+
868+ For more information https://oss.redis.com/redisearch/Commands/#ftinfo
869+ """
870+
871+ res = await self .execute_command (INFO_CMD , self .index_name )
872+ it = map (to_string , res )
873+ return dict (zip (it , it ))
874+
875+ async def search (
876+ self ,
877+ query : Union [str , Query ],
878+ query_params : Dict [str , Union [str , int , float ]] = None ,
879+ ):
880+ """
881+ Search the index for a given query, and return a result of documents
882+
883+ ### Parameters
884+
885+ - **query**: the search query. Either a text for simple queries with
886+ default parameters, or a Query object for complex queries.
887+ See RediSearch's documentation on query format
888+
889+ For more information: https://oss.redis.com/redisearch/Commands/#ftsearch
890+ """ # noqa
891+ args , query = self ._mk_query_args (query , query_params = query_params )
892+ st = time .time ()
893+ res = await self .execute_command (SEARCH_CMD , * args )
894+
895+ if isinstance (res , Pipeline ):
896+ return res
897+
898+ return Result (
899+ res ,
900+ not query ._no_content ,
901+ duration = (time .time () - st ) * 1000.0 ,
902+ has_payload = query ._with_payloads ,
903+ with_scores = query ._with_scores ,
904+ )
905+
906+ async def aggregate (
907+ self ,
908+ query : Union [str , Query ],
909+ query_params : Dict [str , Union [str , int , float ]] = None ,
910+ ):
911+ """
912+ Issue an aggregation query.
913+
914+ ### Parameters
915+
916+ **query**: This can be either an `AggregateRequest`, or a `Cursor`
917+
918+ An `AggregateResult` object is returned. You can access the rows from
919+ its `rows` property, which will always yield the rows of the result.
920+
921+ For more information: https://oss.redis.com/redisearch/Commands/#ftaggregate
922+ """ # noqa
923+ if isinstance (query , AggregateRequest ):
924+ has_cursor = bool (query ._cursor )
925+ cmd = [AGGREGATE_CMD , self .index_name ] + query .build_args ()
926+ elif isinstance (query , Cursor ):
927+ has_cursor = True
928+ cmd = [CURSOR_CMD , "READ" , self .index_name ] + query .build_args ()
929+ else :
930+ raise ValueError ("Bad query" , query )
931+ if query_params is not None :
932+ cmd += self .get_params_args (query_params )
933+
934+ raw = await self .execute_command (* cmd )
935+ return self ._get_aggregate_result (raw , query , has_cursor )
936+
937+ async def spellcheck (self , query , distance = None , include = None , exclude = None ):
938+ """
939+ Issue a spellcheck query
940+
941+ ### Parameters
942+
943+ **query**: search query.
944+ **distance***: the maximal Levenshtein distance for spelling
945+ suggestions (default: 1, max: 4).
946+ **include**: specifies an inclusion custom dictionary.
947+ **exclude**: specifies an exclusion custom dictionary.
948+
949+ For more information: https://oss.redis.com/redisearch/Commands/#ftspellcheck
950+ """ # noqa
951+ cmd = [SPELLCHECK_CMD , self .index_name , query ]
952+ if distance :
953+ cmd .extend (["DISTANCE" , distance ])
954+
955+ if include :
956+ cmd .extend (["TERMS" , "INCLUDE" , include ])
957+
958+ if exclude :
959+ cmd .extend (["TERMS" , "EXCLUDE" , exclude ])
960+
961+ raw = await self .execute_command (* cmd )
962+
963+ corrections = {}
964+ if raw == 0 :
965+ return corrections
966+
967+ for _correction in raw :
968+ if isinstance (_correction , int ) and _correction == 0 :
969+ continue
970+
971+ if len (_correction ) != 3 :
972+ continue
973+ if not _correction [2 ]:
974+ continue
975+ if not _correction [2 ][0 ]:
976+ continue
977+
978+ corrections [_correction [1 ]] = [
979+ {"score" : _item [0 ], "suggestion" : _item [1 ]} for _item in _correction [2 ]
980+ ]
981+
982+ return corrections
983+
984+ async def config_set (self , option , value ):
985+ """Set runtime configuration option.
986+
987+ ### Parameters
988+
989+ - **option**: the name of the configuration option.
990+ - **value**: a value for the configuration option.
991+
992+ For more information: https://oss.redis.com/redisearch/Commands/#ftconfig
993+ """ # noqa
994+ cmd = [CONFIG_CMD , "SET" , option , value ]
995+ raw = await self .execute_command (* cmd )
996+ return raw == "OK"
997+
998+ async def config_get (self , option ):
999+ """Get runtime configuration option value.
1000+
1001+ ### Parameters
1002+
1003+ - **option**: the name of the configuration option.
1004+
1005+ For more information: https://oss.redis.com/redisearch/Commands/#ftconfig
1006+ """ # noqa
1007+ cmd = [CONFIG_CMD , "GET" , option ]
1008+ res = {}
1009+ raw = await self .execute_command (* cmd )
1010+ if raw :
1011+ for kvs in raw :
1012+ res [kvs [0 ]] = kvs [1 ]
1013+ return res
1014+
1015+ async def load_document (self , id ):
1016+ """
1017+ Load a single document by id
1018+ """
1019+ fields = await self .client .hgetall (id )
1020+ f2 = {to_string (k ): to_string (v ) for k , v in fields .items ()}
1021+ fields = f2
1022+
1023+ try :
1024+ del fields ["id" ]
1025+ except KeyError :
1026+ pass
1027+
1028+ return Document (id = id , ** fields )
1029+
1030+ async def sugadd (self , key , * suggestions , ** kwargs ):
1031+ """
1032+ Add suggestion terms to the AutoCompleter engine. Each suggestion has
1033+ a score and string.
1034+ If kwargs["increment"] is true and the terms are already in the
1035+ server's dictionary, we increment their scores.
1036+
1037+ For more information: https://oss.redis.com/redisearch/master/Commands/#ftsugadd
1038+ """ # noqa
1039+ # If Transaction is not False it will MULTI/EXEC which will error
1040+ pipe = self .pipeline (transaction = False )
1041+ for sug in suggestions :
1042+ args = [SUGADD_COMMAND , key , sug .string , sug .score ]
1043+ if kwargs .get ("increment" ):
1044+ args .append ("INCR" )
1045+ if sug .payload :
1046+ args .append ("PAYLOAD" )
1047+ args .append (sug .payload )
1048+
1049+ pipe .execute_command (* args )
1050+
1051+ return (await pipe .execute ())[- 1 ]
1052+
1053+ async def sugget (
1054+ self , key , prefix , fuzzy = False , num = 10 , with_scores = False , with_payloads = False
1055+ ):
1056+ """
1057+ Get a list of suggestions from the AutoCompleter, for a given prefix.
1058+
1059+ Parameters:
1060+
1061+ prefix : str
1062+ The prefix we are searching. **Must be valid ascii or utf-8**
1063+ fuzzy : bool
1064+ If set to true, the prefix search is done in fuzzy mode.
1065+ **NOTE**: Running fuzzy searches on short (<3 letters) prefixes
1066+ can be very
1067+ slow, and even scan the entire index.
1068+ with_scores : bool
1069+ If set to true, we also return the (refactored) score of
1070+ each suggestion.
1071+ This is normally not needed, and is NOT the original score
1072+ inserted into the index.
1073+ with_payloads : bool
1074+ Return suggestion payloads
1075+ num : int
1076+ The maximum number of results we return. Note that we might
1077+ return less. The algorithm trims irrelevant suggestions.
1078+
1079+ Returns:
1080+
1081+ list:
1082+ A list of Suggestion objects. If with_scores was False, the
1083+ score of all suggestions is 1.
1084+
1085+ For more information: https://oss.redis.com/redisearch/master/Commands/#ftsugget
1086+ """ # noqa
1087+ args = [SUGGET_COMMAND , key , prefix , "MAX" , num ]
1088+ if fuzzy :
1089+ args .append (FUZZY )
1090+ if with_scores :
1091+ args .append (WITHSCORES )
1092+ if with_payloads :
1093+ args .append (WITHPAYLOADS )
1094+
1095+ ret = await self .execute_command (* args )
1096+ results = []
1097+ if not ret :
1098+ return results
1099+
1100+ parser = SuggestionParser (with_scores , with_payloads , ret )
1101+ return [s for s in parser ]
0 commit comments