o
    Lj9                    @   s  d dl Z d dlZd dlmZmZmZmZmZmZ d dl	m
Z
 d dlmZmZ d dlmZmZmZmZ d dlmZmZ d dlmZ d dlmZmZmZmZmZ d	d
lmZm Z  ddl!m"Z"m#Z#m$Z$ ddl%m&Z& ddl'm(Z( ddl)m*Z* ddl+m,Z, ddl-m.Z. ddl/m0Z0 ddl1m2Z2 dZ3dZ4dZ5dZ6dZ7dZ8dZ9dZ:dZ;dZ<dZ=dZ>d Z?d!Z@d"ZAd#ZBd$ZCd%ZDd&ZEd'ZFd(ZGd)ZHd*ZId+ZJd,ZKd-ZLd.ZMd/ZNd0ZOd1ZPd2ZQd3ZRd4ZSd5ZTd6ZUd7ZVd8ZWd9ZXd:ZYd;ZZd<Z[d=Z\d>Z]G d?d@ d@Z^G dAdB dBe^Z_dS )C    N)AnyDictListOptionalSetUnion)pairs_to_dict)NEVER_DECODEPipeline)CombineResultsMethodHybridCursorQueryHybridPostProcessingConfigHybridQuery)HybridCursorResultHybridResult)KeyT)check_protocol_versiondecode_field_valuedeprecated_functionexperimental_methodstr_if_bytes   )get_legacy_responsesget_protocol_version   )AggregateRequestAggregateResultCursor)Document)Field)IndexDefinition)ProfileInformation)Query)Result)SuggestionParserNUMERICz	FT.CREATEzFT.ALTERz	FT.SEARCHzFT.ADDz
FT.ADDHASHzFT.DROPINDEXz
FT.EXPLAINzFT.EXPLAINCLIzFT.DELzFT.AGGREGATEz
FT.PROFILEz	FT.CURSORzFT.SPELLCHECKz
FT.DICTADDz
FT.DICTDELzFT.DICTDUMPzFT.MGETz	FT.CONFIGz
FT.TAGVALSzFT.ALIASADDzFT.ALIASUPDATEzFT.ALIASDELzFT.ALIASLISTzFT.INFOz	FT.SUGADDz	FT.SUGDELz	FT.SUGLENz	FT.SUGGETzFT.SYNUPDATEz
FT.SYNDUMPz	FT.HYBRID	NOOFFSETSNOFIELDSNOHLNOFREQSMAXTEXTFIELDS	TEMPORARY	STOPWORDSSKIPINITIALSCAN
WITHSCORESFUZZYWITHPAYLOADSc                   @   s>  e Zd ZdZeeeeee	hZ
dd Zdd ZedddZed	d
 Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd  Zeh d!Zed"d# Zd$d% Zd&d' Zd(d) Zd*d+ Z d,d- Z!d.d/ Z"d0d1 Z#d2d3 Z$d4d5 Z%d6d7 Z&ed8d9 Z'ed:d; Z(d<d= Z)d>d? Z*d@dA Z+dBdC Z,dDdE Z-eddGdHZ.edIdJ Z/dKdL Z0edMdN Z1dOdP Z2dQdR Z3dSdT Z4dUdV Z5ddXdYZ6	F	F			F		F	F	FddZe7e8 d[e9d\e9d]e:e7e;  d^e:e< d_e9d`e9dae9fdbdcZ=dZe>e8e7e8 f fdddeZ?ddfe9fdgdhZ@		F	i		F	F		FddjdkZA	FddldmZBeCdndodp	F	i		F	F		Fddqe;dre9dseDdte:e9 due9dve9dwe:e; dxe9dZe7e; fdydzZEeCdndodpdd{d|ZFeCdnd}dpdd~dZGdde:eHe;eIf  fddZJeCdnd}dpdd ZKdd ZLde:eHe;e>e;eMeDeNf f  fddZOde:eHe;e>e;eMeDeNf f  fddZP	dde>e;eQf de>eHe;e>e;eMeDeNf f df fddZReS 					ddeTde:eU de:eV de:eHe;e>e;eMeDeNf f  de:eM de:eW de>eXeYeZf fddZ[	dde>e;eQf de:eHe;e>e;eMeDeNf f  fddZ\de>e;eQf fddZ]	dde>e^e_f de:eHe;e>e;eMeDeNf f  fddZ`de7de>e^e_f de9fddZa	F	dde>eQe^f de9de:eHe;e>e;eMeDeNf f  de>ebe>ecedf eef eef fddZfdddZgde;de7e; fddZhde;de7e; fddZide;fddZjeCdddpde;de;de9fddZkeCdddpde;de;fddZlde;fddZmdenfddZodenfddZpdenfddZqdere;eNB  fddZsddĄ Ztde;deMfddǄZude;de;deMfddʄZv	F		F	Fdde;de;de9deMde9de9de7ew fdd҄Zxdde;de9de7e; fddքZydd؄ ZzdS )SearchCommandszSearch commands.c                 C   s0  t | jt| jt| jt| jt| j	t
| jt| jt| jt| ji	| _t| jt| jt| ji| _t | jt| jt| jt| jt| jt
| jt| jt| jt| ji	| _t| jt| j t| ji| _!t| ji| _"t | j#t| jt| j$t| jt| j%t
| jt| j&t| j't| ji	| _(t)| j| _*| j+| j*t < | j,| j*t< | j-| j*t< | j.| j*t< dS )a  Build the per-protocol module callback maps.

        Called from ``Search.__init__``, ``Pipeline.__init__`` and
        ``AsyncPipeline.__init__`` so the mapping lives in a single place
        rather than being duplicated across all three classes.
        N)/INFO_CMD_parse_info
SEARCH_CMD_parse_search
HYBRID_CMD_parse_hybrid_searchAGGREGATE_CMD_parse_aggregatePROFILE_CMD_parse_profileSPELLCHECK_CMD_parse_spellcheck
CONFIG_CMD_parse_config_getSYNDUMP_CMD_parse_syndumpALIAS_LIST_CMD_parse_aliaslist_RESP2_MODULE_CALLBACKS_parse_search_resp3_native!_parse_hybrid_search_resp3_native_RESP3_MODULE_CALLBACKS_parse_info_resp3_to_legacy_parse_search_resp3_parse_hybrid_search_resp3_parse_aggregate_resp3_parse_profile_resp3_parse_spellcheck_resp3!_parse_config_get_resp3_to_legacy_parse_syndump_resp3'_RESP3_TO_RESP2_LEGACY_MODULE_CALLBACKS&_pipeline_parse_search_resp3_to_legacy-_pipeline_parse_hybrid_search_resp3_to_legacy)_RESP3_TO_RESP2_LEGACY_PIPELINE_CALLBACKS _RESP2_LEGACY_PIPELINE_CALLBACKS_parse_info_unified_parse_hybrid_search_unified_parse_profile_unified_parse_config_get_unified_parse_syndump_unified_RESP2_UNIFIED_MODULE_CALLBACKSdict_RESP3_UNIFIED_MODULE_CALLBACKS_parse_info_resp3_parse_config_get_resp3_parse_profile_resp3_unified"_parse_hybrid_search_resp3_unifiedself rc   \/home/djax/ivt_ai_plugin/venv/lib/python3.10/site-packages/redis/commands/search/commands.py_init_module_callbacks`   sb   


z%SearchCommands._init_module_callbacksc                 K   s   || j v rd|vr|S t| j}t| j}|r=|dv r*|tkr#t|S | j|}n%t|dr6| j	|}n| j
|}nt|drI| j|}n| j|}|d u rU|S ||fi |S )Nquery)   3rg   )_QUERY_REQUIRED_CMDSr   clientr   r:   r!   rG   getr   rP   rD   r\   rZ   )rb   cmdreskwargsprotocollegacycbrc   rc   rd   _parse_results   s$   



zSearchCommands._parse_resultsNc                 C   s&   t | ts|S | || | |S N)
isinstancer[   rk   encode)mappingkeydefaultrc   rc   rd   
_resp3_get   s   
zSearchCommands._resp3_getc                 C   s@   t | ts| S g }|  D ]\}}|t| || q|S rs   )rt   r[   itemsappendr   )rv   flatrw   valuerc   rc   rd   _flatten_resp3_mapping   s   
z%SearchCommands._flatten_resp3_mappingc              
   K   s   | d}|du st|ts|S | |ddg}| |dg D ]3}|| |d |jr6|| |d |jrB|| |d |jsR|| | |d	i  q|S )
zDConvert RESP3 FT.SEARCH pipeline output to raw RESP2 pipeline shape.rf   Ntotal_resultsr   resultsidscorepayloadextra_attributes)	rk   rt   r[   ry   r{   _with_scores_with_payloads_no_contentr~   )rb   rm   rn   rf   outputitemrc   rc   rd   rQ      s$   
z5SearchCommands._pipeline_parse_search_resp3_to_legacyc                    s   t |ts|S dd | D }d|v r d|dd|dgS  fdd|dg D }d	|d	d
d|d|dg d|dd
gS )zDConvert RESP3 FT.HYBRID pipeline output to raw RESP2 pipeline shape.c                 S      i | ]	\}}t ||qS rc   r   .0rw   r}   rc   rc   rd   
<dictcomp>       zPSearchCommands._pipeline_parse_hybrid_search_resp3_to_legacy.<locals>.<dictcomp>cursorSEARCHVSIMc                    $   g | ]}t |tr |n|qS rc   )rt   r[   r~   r   r   ra   rc   rd   
<listcomp>      zPSearchCommands._pipeline_parse_hybrid_search_resp3_to_legacy.<locals>.<listcomp>r   r   r   warningsexecution_time)rt   r[   rz   rk   )rb   rm   rn   r   rc   ra   rd   rR      s"   





z<SearchCommands._pipeline_parse_hybrid_search_resp3_to_legacyc                 K   s   t t|}tt||S rs   )mapr   r[   zip)rb   rm   rn   itrc   rc   rd   r3     s   
zSearchCommands._parse_infoc                 K   s   |rt |S t  S rs   )setrb   rm   rn   rc   rc   rd   rC     s   zSearchCommands._parse_aliaslistc                 K   s2   t ||d j |d |d j|d j|d jdS )Nrf   duration)r   has_payloadwith_scoresfield_encodings)r#   r   r   r   _return_fields_decode_asr   rc   rc   rd   r5      s   
zSearchCommands._parse_searchc                 K   s|   t |dd}d|v rtt|d t|d dS g }|d D ]}t |dd}|| qtt|d ||d	 t|d
 dS )NTdecode_keysr   r   r   search_cursor_idvsim_cursor_idr   r   r   r   r   r   r   r   )r   r   intr{   r   floatrb   rm   rn   res_dictr   res_item	item_dictrc   rc   rd   r7   *  s    



z#SearchCommands._parse_hybrid_searchc                 K   s   |  ||d |d S )Nrf   
has_cursor)_get_aggregate_resultr   rc   rc   rd   r9   @     zSearchCommands._parse_aggregatec                 K   sZ   |d }t |tr| |d ||j}nt|d |j |d |j|jd}|t|d fS )Nrf   r   r   r   r   r   r   )	rt   r   r   _cursorr#   r   r   r   r!   )rb   rm   rn   rf   resultrc   rc   rd   r;   C  s   
zSearchCommands._parse_profilec                 K   sr   i }|dkr|S |D ],}t |tr|dkrq
t|dkrq
|d s"q
|d d s)q
dd |d D ||d < q
|S )Nr   rg   r   c                 S   s   g | ]}|d  |d dqS )r   r   r   
suggestionrc   )r   _itemrc   rc   rd   r   q  s    z4SearchCommands._parse_spellcheck.<locals>.<listcomp>r   )rt   r   len)rb   rm   rn   corrections_correctionrc   rc   rd   r=   R  s    z SearchCommands._parse_spellcheckc                 K   s   |r	dd |D S i S )Nc                 S   s   i | ]	}|d  |d qS r   r   rc   r   kvsrc   rc   rd   r   x  r   z4SearchCommands._parse_config_get.<locals>.<dictcomp>rc   r   rc   rc   rd   r?   w  r   z SearchCommands._parse_config_getc                    s    fddt dt dD S )Nc                    s   i | ]} |  |d   qS )r   rc   r   irm   rc   rd   r   {      z1SearchCommands._parse_syndump.<locals>.<dictcomp>r   r   ranger   r   rc   r   rd   rA   z  s   zSearchCommands._parse_syndump>   typeWEIGHTPHONETIC	SEPARATOR	attribute
identifierc                 C   s   i }g }t j}d}|t| k r?t| | }||v r0|d t| k r0t| |d  ||< |d7 }n	|| |d7 }|t| k s||d< |S )aC  Convert a RESP2 flat attribute list into a RESP3-style dict.

        RESP2 format: ``[identifier, name, attribute, alias, type, TEXT,
        WEIGHT, 1, SORTABLE, NOSTEM]``.
        RESP3 format: ``{"identifier": name, "attribute": alias, "type":
        "TEXT", "WEIGHT": "1", "flags": ["SORTABLE", "NOSTEM"]}``.
        r   r   r   flags)r1   _INFO_ATTR_PAIR_KEYSr   r   r{   )	attr_listr   r   	pair_keysr   rw   rc   rc   rd   _normalize_info_attribute  s   	

z(SearchCommands._normalize_info_attributec                    sL   t t|}tt||}d|v r$t|d tr$ fdd|d D |d< |S )zParse FT.INFO into the unified shape with ``attributes`` as a
        list of dicts so RESP2 output matches RESP3 output.
        
attributesc                    r   rc   )rt   listr   r   attrra   rc   rd   r     r   z6SearchCommands._parse_info_unified.<locals>.<listcomp>)r   r   r[   r   rt   r   )rb   rm   rn   r   inforc   ra   rd   rU     s   


z"SearchCommands._parse_info_unifiedc                    s   t |dd}d|v rtt|d t|d dS jdi | g }|d D ]}t |dd}| fdd	| D  q&tt|d
 ||d t|d dS )NTr   r   r   r   r   r   c                        i | ]\}}| || qS rc   _decode_hybrid_field_valuer   r   rb   rc   rd   r     s    z?SearchCommands._parse_hybrid_search_unified.<locals>.<dictcomp>r   r   r   r   rc   )r   r   r   _hybrid_field_encodingsr{   rz   r   r   r   rc   r   rd   rV     s*   



z+SearchCommands._parse_hybrid_search_unifiedc                 K   s   |d }t |tr| |d ||j}nt|d |j |d |j|jd}|d }t |tr?|r?t |d t	t
fr?t|dd}|t|fS )zParse FT.PROFILE into ``(result, ProfileInformation)`` with
        the profile_data normalised to a dict on >= 7.9.0 servers.
        rf   r   r   r   r   Tr   )rt   r   r   r   r#   r   r   r   r   strbytesr   r!   )rb   rm   rn   rf   r   profile_datarc   rc   rd   rW     s&   
z%SearchCommands._parse_profile_unifiedc                 K   s   |si S dd |D S )Nc                 S   s"   i | ]}t |d  t |d qS r   r   r   rc   rc   rd   r     s   " z<SearchCommands._parse_config_get_unified.<locals>.<dictcomp>rc   r   rc   rc   rd   rX     s   z(SearchCommands._parse_config_get_unifiedc                    s&    si S  fddt dt dD S )Nc                    sN   i | ]#}t  | t |d   trdd  |d   D nt  |d   qS )r   c                 S      g | ]}t |qS rc   r   r   src   rc   rd   r         zDSearchCommands._parse_syndump_unified.<locals>.<dictcomp>.<listcomp>)r   rt   r   r   r   rc   rd   r     s    
z9SearchCommands._parse_syndump_unified.<locals>.<dictcomp>r   r   r   r   rc   r   rd   rY     s
   
z%SearchCommands._parse_syndump_unifiedc                 K   s4   | d}tj|| ddt|ddt|dddS )	z4Parse RESP3 FT.SEARCH response into a Result object.rf   r   r   r   Fr   N)r   r   r   )rk   r#   
from_resp3getattr)rb   rm   rn   rf   rc   rc   rd   rI     s   



z"SearchCommands._parse_search_resp3c                 K   s  t |ts|S |d}t|dd}dd | D }d|v rvg }|d D ]L}t |ts2|| q%dd | D }d|v rGt|d |d< |d	}t |trli }| D ]\}	}
t|	}	t|
|	|||	< qW||d	< || q%||d< d
|v rdd |d
 D |d
< |S )a  Normalise the RESP3 FT.SEARCH map while preserving its native shape.

        ``protocol=3`` + ``legacy_responses=True`` keeps the RESP3 dict
        surface.  FT.SEARCH uses ``NEVER_DECODE`` (its results may contain
        binary field values such as vector embeddings), so the wire hands
        back raw bytes.  Decode the structural keys and the field values here
        instead of at the wire: text values are decoded leniently while
        binary fields flagged via ``return_field(..., decode_field=False)``
        are preserved as raw bytes.  This keeps the previously decoded native
        surface intact and no longer crashes on non-UTF-8 field data.
        rf   r   Nc                 S   r   rc   r   r   kvrc   rc   rd   r     r   z=SearchCommands._parse_search_resp3_native.<locals>.<dictcomp>r   c                 S   r   rc   r   r   rc   rc   rd   r     r   r   r   warningc                 S   r   rc   r   r   wrc   rc   rd   r   "  r   z=SearchCommands._parse_search_resp3_native.<locals>.<listcomp>)rt   r[   rk   r   rz   r{   r   r   )rb   rm   rn   rf   r   r   r   extradecodedr   r   rc   rc   rd   rE     s4   





z)SearchCommands._parse_search_resp3_nativec                 K   s.  | d}| dd}d}|r%t|tr%|d }t|dkr"|d nd}n|}|du r-i }dd | D }d	d
 | dg D }| dd}g }	| dg D ]+}
dd |
 D }
|
 di }g }| D ]\}}|| || qf|	| qOd}|rt|tr||_|}nt|}t|	|d||dS )zAParse RESP3 FT.AGGREGATE response into an AggregateResult object.rf   r   Fr   r   Nc                 S   r   rc   r   r   rc   rc   rd   r   7  r   z9SearchCommands._parse_aggregate_resp3.<locals>.<dictcomp>c                 S   r   rc   r   r   rc   rc   rd   r   9  r   z9SearchCommands._parse_aggregate_resp3.<locals>.<listcomp>r   r   r   c                 S   r   rc   r   r   rc   rc   rd   r   >  r   r   )totalr   )	rk   rt   r   r   rz   r{   r   cidr   )rb   rm   rn   rf   r   	cursor_iddatar   r   rowsresult_itemextra_attrsr|   r   r   r   rc   rc   rd   rK   %  s8   


z%SearchCommands._parse_aggregate_resp3c              	   K   s   dd |  D }d|v rtt|d t|d dS g }|dg D ]}t|tr7|dd |  D  q#|t|d	d
 q#tt|dd||dg t	|dddS )a4  Parse RESP3 FT.HYBRID response into HybridResult/HybridCursorResult.

        Top-level keys are normalised to strings.  Values are preserved
        as delivered by the wire (bytes when ``NEVER_DECODE`` is set,
        strings otherwise) so byte/str semantics match the RESP2 legacy
        parser.
        c                 S   r   rc   r   r   rc   rc   rd   r   \  r   z=SearchCommands._parse_hybrid_search_resp3.<locals>.<dictcomp>r   r   r   r   r   c                 S   r   rc   r   r   rc   rc   rd   r   f  r   Tr   r   r   r   r   r   )
rz   r   r   rk   rt   r[   r{   r   r   r   )rb   rm   rn   r   r   rc   rc   rd   rJ   T  s"   



z)SearchCommands._parse_hybrid_search_resp3c              	      s   dd |  D }d|v rtt|d t|d dS jdi | g }|dg D ],}t|trB| fdd|  D  q+t|d	d
}| fdd|  D  q+t	t|dd||dg t
|dddS )z=Parse RESP3 FT.HYBRID into the approved unified HybridResult.c                 S   r   rc   r   r   rc   rc   rd   r   s  r   zESearchCommands._parse_hybrid_search_resp3_unified.<locals>.<dictcomp>r   r   r   r   r   c                    s(   i | ]\}}t ||t | qS rc   )r   r   r   r   rc   rd   r     s    

Tr   c                    r   rc   r   r   r   rc   rd   r     s    r   r   r   r   r   Nrc   )rz   r   r   r   rk   rt   r[   r{   r   r   r   )rb   rm   rn   r   r   r   rc   r   rd   r`   q  s6   


		
z1SearchCommands._parse_hybrid_search_resp3_unifiedc                  K   s:   i }dD ]}|  |}t|dd }|r|| q|pd S )N)rf   post_processingr   )rk   r   update)rn   	encodingssource_namesourcesource_encodingsrc   rc   rd   r     s   

z&SearchCommands._hybrid_field_encodingsc                 C   s   |r||vr| S t | ||S rs   )r   )r}   rw   r   rc   rc   rd   r     s   z)SearchCommands._decode_hybrid_field_valuec                 K   s^   dd |  D }d|v r|S d|v rdd |d D |d< d|v r-dd |d D |d< |S )	a[  Normalise RESP3 FT.HYBRID map keys while preserving native shape.

        ``protocol=3`` + ``legacy_responses=True`` keeps the RESP3 dict
        surface, but HYBRID uses ``NEVER_DECODE`` so result values mirror
        legacy RESP2 bytes. Decode only structural keys so callers can use
        the same native RESP3 key names as before.
        c                 S   r   rc   r   r   rc   rc   rd   r     r   zDSearchCommands._parse_hybrid_search_resp3_native.<locals>.<dictcomp>r   r   c                 S   s4   g | ]}t |trd d | D nt|ddqS )c                 S   r   rc   r   r   rc   rc   rd   r     r   zOSearchCommands._parse_hybrid_search_resp3_native.<locals>.<listcomp>.<dictcomp>Tr   )rt   r[   rz   r   r   rc   rc   rd   r     s    
zDSearchCommands._parse_hybrid_search_resp3_native.<locals>.<listcomp>r   c                 S   r   rc   r   r   rc   rc   rd   r     r   rz   r   rc   rc   rd   rF     s   
z0SearchCommands._parse_hybrid_search_resp3_nativec                 K   s   t |ts| j|fi |S dd | D }i }|di }| D ]4\}}|s*q#g }|D ]"}| D ]\}	}
t|
}|drG|dd }|||	d q4q.|rW|||< q#|S )a#  Parse RESP3 FT.SPELLCHECK response into unified format.

        RESP3 format:
            {"results": {"term": [{"suggestion": score}, ...], ...}}
        Unified format (matches RESP2 parsed output):
            {"term": [{"score": score_str, "suggestion": suggestion}, ...], ...}
        c                 S   r   rc   r   r   rc   rc   rd   r     r   z:SearchCommands._parse_spellcheck_resp3.<locals>.<dictcomp>r   z.0Nr   )rt   r[   r=   rz   rk   r   endswithr{   )rb   rm   rn   r   r   termsuggestionsterm_correctionssuggestion_dictr   r   	score_strrc   rc   rd   rM     s,   

z&SearchCommands._parse_spellcheck_resp3c                 K   s  |d }| d}|du r| d}|du r| d}|du r$| d}|du r-| d}| d}|du r;| d	}|du rD| d
}|du rM| d}|du rV| d}t|trbt||d}t|trs| j||t|jd}ntj	|| ddt
|ddd}| |}||fS )zExtract ``(result, profile_data_dict)`` from a RESP3 FT.PROFILE
        response.  ``profile_data_dict`` has its keys/values normalised
        to strings but is otherwise left as the native RESP3 dict.
        rf   ResultsNs   Resultsr   s   resultsr   Profiles   Profileprofiles   profiler   )r   r   rf   r   r   r   F)r   r   )rk   rt   r   r   r   rK   boolr   r#   r   r   _to_string_recursive)rb   rm   rn   rf   results_datar   r   rc   rc   rd   _extract_resp3_profile_parts  sD   














z+SearchCommands._extract_resp3_profile_partsc                 K   sf   t |tr| j|fi |S | j|fi |\}}t |tr-d|v p%d|v }| j||d}|t|fS )a  Parse RESP3 FT.PROFILE response into ``(result, ProfileInformation)``.

        RESP3 format (aligned, RediSearch >= MOD-6816):
            {"Results": {search/aggregate result dict},
             "Profile": {profile information dict}}

        Older RediSearch versions may return a list (same as RESP2) even
        when the connection uses RESP3.  In that case we delegate to the
        RESP2 ``_parse_profile`` parser.
        ShardsCoordinator)flat_top)rt   r   r;   r  r[   _resp3_profile_dict_to_listr!   )rb   rm   rn   r   r   r	  rc   rc   rd   rL     s   

z#SearchCommands._parse_profile_resp3c                 K   sb   t |tr| j|fi |S | j|fi |\}}t |tr+d|v s+d|v s+| |}|t|fS )a  Parse RESP3 FT.PROFILE for the unified shape.

        Redis < 7.9.0 returns RESP2 profile data as nested list-of-pairs,
        while RESP3 returns the same data as a dict.  Convert that pre-7.9
        RESP3 dict back to the RESP2 list shape so the unified surface is
        protocol-independent.  Redis >= 7.9.0 coordinator profiles contain
        ``Shards``/``Coordinator`` keys and stay as dicts, matching the
        RESP2 unified parser.
        r  r  )rt   r   rW   r  r[   r
  r!   )rb   rm   rn   r   r   rc   rc   rd   r_   7  s   



z+SearchCommands._parse_profile_resp3_unifiedFc                    s&   dd d	 fdd	  | ddS )
a*  Convert a RESP3 profile dict into the RESP2 wire list shape.

        Pre-7.9.0 servers serialise the RESP2 profile as a list of
        ``[key, value]`` pairs at the top level, with nested structures
        as flat alternating key-value lists.  Post-7.9.0 servers serialise
        the top level itself as a flat ``[key, value, key, value]`` list
        (with ``"Shards"``/``"Coordinator"`` keys).  ``flat_top`` selects
        between these two top-level shapes; nested dicts always use the
        flat alternating form.

        Key structural difference: when a dict value is a list of dicts
        (e.g. ``"Child iterators": [{...}, {...}]``), RESP2 expands each
        dict as a separate sibling element in the parent flat list rather
        than keeping them nested inside a single value.
        c                 S   s   t | to| ot | d tS )Nr   )rt   r   r[   objrc   rc   rd   _is_list_of_dicts]  s   zESearchCommands._resp3_profile_dict_to_list.<locals>._is_list_of_dictsFc                    s   t | tr`|r6s6g }|  D ]$\}}|g}|r'|D ]	}| | qn| | || q|S g }|  D ]!\}}|| |rV|D ]	}| | qKq<| | q<|S t | trn fdd| D S | S )Nc                    s   g | ]} |qS rc   rc   r   )_convertrc   rd   r   x  r   zPSearchCommands._resp3_profile_dict_to_list.<locals>._convert.<locals>.<listcomp>)rt   r[   rz   r{   r   )r  	top_levelr   r   r   entryr   r  r  r	  rc   rd   r  `  s0   


z<SearchCommands._resp3_profile_dict_to_list.<locals>._convertT)r  NFrc   )r   r	  rc   r  rd   r
  K  s   z*SearchCommands._resp3_profile_dict_to_listc                 C   sJ   t | tr	t| S t | trdd |  D S t | tr#dd | D S | S )zRecursively convert bytes keys/values to strings in nested
        structures.  Non-bytes scalars (int, float, None, bool) pass
        through unchanged.
        c                 S   s.   i | ]\}}t |trt|n|t|qS rc   )rt   r   r   r1   r  r   rc   rc   rd   r     s
    z7SearchCommands._to_string_recursive.<locals>.<dictcomp>c                 S   s   g | ]}t |qS rc   )r1   r  r   rc   rc   rd   r     s    z7SearchCommands._to_string_recursive.<locals>.<listcomp>)rt   r   r   r[   rz   r   r  rc   rc   rd   r    s   


z#SearchCommands._to_string_recursivec                 K   s
   |  |S )z;Parse RESP3 FT.INFO response, normalising bytes to strings.r  r   rc   rc   rd   r]     s   
z SearchCommands._parse_info_resp3c                 C   sP   g }|  dpg }|  D ]\}}|dkrq|| || q|| |S )zConvert a RESP3-style attribute dict back into the RESP2 flat
        list (key-value pairs followed by bare flag tokens).
        r   )rk   rz   r{   extend)	attr_dictr|   r   r   r   rc   rc   rd   _flatten_info_attribute  s   

z&SearchCommands._flatten_info_attributec                    s8     |}|d}t|tr fdd|D |d< |S )zParse RESP3 FT.INFO into the legacy RESP2 flat shape so
        ``legacy_responses=True`` on a RESP3 wire matches RESP2 output.
        r   c                    r   rc   )rt   r[   r  r   ra   rc   rd   r     r   z>SearchCommands._parse_info_resp3_to_legacy.<locals>.<listcomp>)r  rk   rt   r   )rb   rm   rn   r   attrsrc   ra   rd   rH     s   




z*SearchCommands._parse_info_resp3_to_legacyc                 K   s   |si S dd |  D S )zAParse RESP3 FT.CONFIG GET response, normalising bytes to strings.c                 S   s   i | ]\}}t |t |qS rc   r   r   rc   rc   rd   r     r   z:SearchCommands._parse_config_get_resp3.<locals>.<dictcomp>r   r   rc   rc   rd   r^     s   z&SearchCommands._parse_config_get_resp3c                 K   s   |rt |S i S )z>Parse RESP3 FT.CONFIG GET back to the RESP2 legacy dict shape.)r[   r   rc   rc   rd   rN     s   z0SearchCommands._parse_config_get_resp3_to_legacyc                 K   s   |si S |  |S )z>Parse RESP3 FT.SYNDUMP response, normalising bytes to strings.r  r   rc   rc   rd   rO     s   
z#SearchCommands._parse_syndump_resp3d   c                 C   s   | j | |dS )zT
        Create a new batch indexer from the client with a given chunk size
        )
chunk_size)BatchIndexer)rb   r  rc   rc   rd   batch_indexer  s   zSearchCommands.batch_indexerfieldsno_term_offsetsno_field_flags	stopwords
definitionno_highlightno_term_frequenciesskip_initial_scanc                 C   s*  t | jg}|dur||j7 }|r|t |dur(t|tr(|t || |r/|t |r6|t	 |r=|t
 |	rD|t |
rK|t |durkt|tttfrk|tt|g7 }t|dkrk|t|7 }|d z|ttjdd |D  7 }W n ty   || 7 }Y nw | j| S )a  
        Creates the search index. The index must not already exist.

        For more information, see https://redis.io/commands/ft.create/

        Args:
            fields: A list of Field objects.
            no_term_offsets: If `true`, term offsets will not be saved in the index.
            no_field_flags: If true, field flags that allow searching in specific fields
                            will not be saved.
            stopwords: If provided, the index will be created with this custom stopword
                       list. The list can be empty.
            definition: If provided, the index will be created with this custom index
                        definition.
            max_text_fields: If true, indexes will be encoded as if there were more than
                             32 text fields, allowing for additional fields beyond 32.
            temporary: Creates a lightweight temporary index which will expire after the
                       specified period of inactivity. The internal idle timer is reset
                       whenever the index is searched or added to.
            no_highlight: If true, disables highlighting support. Also implied by
                          `no_term_offsets`.
            no_term_frequencies: If true, term frequencies will not be saved in the
                                 index.
            skip_initial_scan: If true, the initial scan and indexing will be skipped.

        Nr   SCHEMAc                 s       | ]}|  V  qd S rs   
redis_argsr   frc   rc   rd   	<genexpr>      z.SearchCommands.create_index.<locals>.<genexpr>)
CREATE_CMD
index_nameargsr{   r*   rt   r   r+   r&   r(   r'   r)   r-   r   tupler   r,   r   	itertoolschain	TypeErrorr'  execute_command)rb   r  r  r  r  r   max_text_fields	temporaryr!  r"  r#  r.  rc   rc   rd   create_index  s:   
'









 
zSearchCommands.create_indexc                 C   sX   t | jddg}z|ttjdd |D  7 }W n ty&   || 7 }Y nw | j| S )a  
        Alter the existing search index by adding new fields. The index
        must already exist.

        ### Parameters:

        - **fields**: a list of Field objects to add for the index

        For more information see `FT.ALTER <https://redis.io/commands/ft.alter>`_.
        r$  ADDc                 s   r%  rs   r&  r(  rc   rc   rd   r*    r+  z2SearchCommands.alter_schema_add.<locals>.<genexpr>)	ALTER_CMDr-  r   r0  r1  r2  r'  r3  )rb   r  r.  rc   rc   rd   alter_schema_add  s    
zSearchCommands.alter_schema_adddelete_documentsc                 C   s<   t | jg}t|tr|du rdnd}|r|| | j| S )ai  
        Drop the index if it exists.
        Replaced `drop_index` in RediSearch 2.0.
        Default behavior was changed to not delete the indexed documents.

        ### Parameters:

        - **delete_documents**: If `True`, all documents will be deleted.

        For more information see `FT.DROPINDEX <https://redis.io/commands/ft.dropindex>`_.
        TDD )DROPINDEX_CMDr-  rt   r  r{   r3  )rb   r:  r.  
delete_strrc   rc   rd   	dropindex"  s   


zSearchCommands.dropindex      ?c
                 K   s   |s|	rd}t | j||g}|r|d |dur"|d || |r7|d |r0|d |	r7|d |r?|d|g7 }|d	 |ttj|
  7 }|durX|j| S | j| S )
zS
        Internal add_document used for both batch and single doc indexing
        TNOSAVENPAYLOADREPLACEPARTIALNOCREATELANGUAGEFIELDS)ADD_CMDr-  r{   r   r0  r1  rz   r3  )rb   doc_idconnnosaver   r   replacepartiallanguage	no_creater  r.  rc   rc   rd   _add_document;  s*   








zSearchCommands._add_documentc                 C   sH   t | j||g}|r|d |r|d|g7 }|dur|j| S | j| S )zX
        Internal add_document_hash used for both batch and single doc indexing
        rC  rF  N)ADDHASH_CMDr-  r{   r3  )rb   rI  rJ  r   rN  rL  r.  rc   rc   rd   _add_document_hashe  s   


z!SearchCommands._add_document_hashz2.0.0z2deprecated since redisearch 2.0, call hset insteadversionreasonrI  rK  r   r   rL  rM  rN  rO  c	           
      K   s$   | j |fd|||||||d|	S )a  
        Add a single document to the index.

        Args:

            doc_id: the id of the saved document.
            nosave: if set to true, we just index the document, and don't
                      save a copy of it. This means that searches will just
                      return ids.
            score: the document ranking, between 0.0 and 1.0
            payload: optional inner-index payload we can save for fast
                     access in scoring functions
            replace: if True, and the document already is in the index,
                     we perform an update and reindex the document
            partial: if True, the fields specified will be added to the
                       existing document.
                       This has the added benefit that any fields specified
                       with `no_index`
                       will not be reindexed again. Implies `replace`
            language: Specify the language used for document tokenization.
            no_create: if True, the document is only updated and reindexed
                         if it already exists.
                         If the document does not exist, an error will be
                         returned. Implies `replace`
            fields: kwargs dictionary of the document fields to be saved
                    and/or indexed.
                    NOTE: Geo points should be encoded as strings of "lon,lat"
        N)rJ  rK  r   r   rL  rM  rN  rO  )rP  )
rb   rI  rK  r   r   rL  rM  rN  rO  r  rc   rc   rd   add_documenty  s   +
zSearchCommands.add_documentc                 C   s   | j |d|||dS )a  
        Add a hash document to the index.

        ### Parameters

        - **doc_id**: the document's id. This has to be an existing HASH key
                      in Redis that will hold the fields the index needs.
        - **score**:  the document ranking, between 0.0 and 1.0
        - **replace**: if True, and the document already is in the index, we
                      perform an update and reindex the document
        - **language**: Specify the language used for document tokenization.
        N)rJ  r   rN  rL  )rR  )rb   rI  r   rN  rL  rc   rc   rd   add_document_hash  s   
z SearchCommands.add_document_hashzdeprecated since redisearch 2.0c                 C   s6   t | j|g}|r|d |dur|j| S | j| S )a  
        Delete a document from index
        Returns 1 if the document was deleted, 0 if not

        ### Parameters

        - **delete_actual_document**: if set to True, RediSearch also delete
                                      the actual document if it is in the index
        r;  N)DEL_CMDr-  r{   r3  )rb   rI  rJ  delete_actual_documentr.  rc   rc   rd   delete_document  s   


zSearchCommands.delete_documentr   c                    sR   | j |} fdd| D }z|d= W n	 ty   Y nw tdd|i|S )
        Load a single document by id

        - **field_encodings**: optional dict mapping field names to encodings.
          If a field's encoding is ``None`` the raw bytes value is preserved
          (useful for binary data such as vectors).
        c                    &   i | ]\}}t |t|t | qS rc   r   r   r   r   rc   rd   r         z0SearchCommands.load_document.<locals>.<dictcomp>r   Nrc   rj   hgetallrz   KeyErrorr   rb   r   r   r  rc   r^  rd   load_document  s   

zSearchCommands.load_documentc                 G   s   | j t| jg|R  S )z
        Returns the full contents of multiple documents.

        ### Parameters

        - **ids**: the ids of the saved documents.

        )r3  MGET_CMDr-  )rb   idsrc   rc   rd   rk     s   zSearchCommands.getc                 C      |  t| j}| t|S )
        Get info an stats about the the current index, including the number of
        documents, memory consumption, etc

        For more information see `FT.INFO <https://redis.io/commands/ft.info>`_.
        r3  r2   r-  rr   rb   rm   rc   rc   rd   r     s   zSearchCommands.infoquery_paramsc                 C   sb   |d u rg S g }t |dkr/|d |t |d  | D ]\}}|| || q |S )Nr   PARAMSr   )r   r{   rz   )rb   rk  r.  rw   r}   rc   rc   rd   get_params_args  s   

zSearchCommands.get_params_argsc                 C   sX   | j g}t|trt|}t|tstdt| || 7 }|| |7 }||fS )NzBad query type )r-  rt   r   r"   
ValueErrorr   get_argsrm  )rb   rf   rk  r.  rc   rc   rd   _mk_query_args  s   

zSearchCommands._mk_query_argsrf   c                 C   s   | j ||d\}}t }tdi}t| tr||d< d|d< | jtg|R i |}t|tr1|S | jt||t | d dS )  
        Search the index for a given query, and return a result of documents

        ### Parameters

        - **query**: the search query. Either a text for simple queries with
                     default parameters, or a Query object for complex queries.
                     See RediSearch's documentation on query format

        For more information see `FT.SEARCH <https://redis.io/commands/ft.search>`_.
        rk  Trf   r   r        @@rf   r   	rp  time	monotonicr	   rt   r
   r3  r4   rr   rb   rf   rk  r.  stoptionsrm   rc   rc   rd   search$  s   

zSearchCommands.searchcombine_methodr   params_substitutiontimeoutr   returnc                 C   s   | j }i }t|g}	|	|  |r|	|  |r&|	|  ||d< |r0|	| | |r9|	d|f |rFd|d< |	|  d|t< ||d< | j|	i |}
t|
t	r]|
S | j
t|
fi |S )M  
        Execute a hybrid search using both text and vector queries

        Args:
            - **query**: HybridQuery object
                        Contains the text and vector queries
            - **combine_method**: CombineResultsMethod object
                        Contains the combine method and parameters
            - **post_processing**: HybridPostProcessingConfig object
                        Contains the post processing configuration
            - **params_substitution**: Dict[str, Union[str, int, float, bytes]]
                        Contains the parameters substitution
            - **timeout**: int - contains the timeout in milliseconds
            - **cursor**: HybridCursorQuery object - contains the cursor configuration


        For more information see `FT.SEARCH <https://redis.io/commands/ft.hybrid>`.
        r   TIMEOUTTr   rf   r-  r6   r  ro  
build_argsrm  r	   r3  rt   r
   rr   rb   rf   r|  r   r}  r~  r   indexrz  piecesrm   rc   rc   rd   hybrid_searchH  s,   
zSearchCommands.hybrid_searchc                 C   s$   | j ||d\}}| jtg|R  S )zReturns the execution plan for a complex query.

        For more information see `FT.EXPLAIN <https://redis.io/commands/ft.explain>`_.
        rr  )rp  r3  EXPLAIN_CMD)rb   rf   rk  r.  
query_textrc   rc   rd   explain  s   	zSearchCommands.explainc                 C   s   t d)Nz#EXPLAINCLI will not be implemented.)NotImplementedError)rb   rf   rc   rc   rd   explain_cli  s   zSearchCommands.explain_clic                 C   s   t |trt|j}t| jg|  }nt |tr&d}td| jg|  }nt	d||| 
|7 }| j| }| jt|||dS )  
        Issue an aggregation query.

        ### Parameters

        **query**: This can be either an `AggregateRequest`, or a `Cursor`

        An `AggregateResult` object is returned. You can access the rows from
        its `rows` property, which will always yield the rows of the result.

        For more information see `FT.AGGREGATE <https://redis.io/commands/ft.aggregate>`_.
        TREAD	Bad queryr  rt   r   r  r   r8   r-  r  r   
CURSOR_CMDrn  rm  r3  rr   rb   rf   rk  r   rl   rawrc   rc   rd   	aggregate  s   




zSearchCommands.aggregater  r   c                 C   sz   |rt |tr|d |_|}nt|d }|d }nd }t |tr/|jr/|d }|dd  }nd }|dd  }t|||S )Nr   r   r   )rt   r   r   r   _with_schemar   )rb   r  rf   r   r   schemar   rc   rc   rd   r     s   


z$SearchCommands._get_aggregate_resultlimitedc                 C   s   t  }t| jdg}|r|d |d t|tr&d|d< || 7 }nt|tr=d|d< ||	 7 }|| 
|7 }ntd| j| }| jt||t  | d d	S )
a  
        Performs a search or aggregate command and collects performance
        information.

        ### Parameters

        **query**: This can be either an `AggregateRequest` or `Query`.
        **limited**: If set to True, removes details of reader iterator.
        **query_params**: Define one or more value parameters.
        Each parameter has a name and a value.

        r<  LIMITEDQUERY	AGGREGATEr   r   z5Must provide AggregateRequest object or Query object.rs  rt  )rv  rw  r:   r-  r{   rt   r   r  r"   ro  rm  rn  r3  rr   )rb   rf   r  rk  ry  rl   rm   rc   rc   rd   r    s"   




zSearchCommands.profilec                 C   s\   t | j|g}|r|d|g |r|dd|g |r#|dd|g | j| }| t |S )a  
        Issue a spellcheck query

        Args:

            query: search query.
            distance: the maximal Levenshtein distance for spelling
                       suggestions (default: 1, max: 4).
            include: specifies an inclusion custom dictionary.
            exclude: specifies an exclusion custom dictionary.

        For more information see `FT.SPELLCHECK <https://redis.io/commands/ft.spellcheck>`_.
        DISTANCETERMSINCLUDEEXCLUDEr<   r-  r  r3  rr   rb   rf   distanceincludeexcluderl   rm   rc   rc   rd   
spellcheck  s   
zSearchCommands.spellchecknametermsc                 G      t |g}|| | j| S )zAdds terms to a dictionary.

        ### Parameters

        - **name**: Dictionary name.
        - **terms**: List of items for adding to the dictionary.

        For more information see `FT.DICTADD <https://redis.io/commands/ft.dictadd>`_.
        )DICT_ADD_CMDr  r3  rb   r  r  rl   rc   rc   rd   dict_add     


zSearchCommands.dict_addc                 G   r  )a  Deletes terms from a dictionary.

        ### Parameters

        - **name**: Dictionary name.
        - **terms**: List of items for removing from the dictionary.

        For more information see `FT.DICTDEL <https://redis.io/commands/ft.dictdel>`_.
        )DICT_DEL_CMDr  r3  r  rc   rc   rd   dict_del  r  zSearchCommands.dict_delc                 C   s   t |g}| j| S )zDumps all terms in the given dictionary.

        ### Parameters

        - **name**: Dictionary name.

        For more information see `FT.DICTDUMP <https://redis.io/commands/ft.dictdump>`_.
        )DICT_DUMP_CMDr3  )rb   r  rl   rc   rc   rd   	dict_dump*  s   	
zSearchCommands.dict_dump8.0.0Ddeprecated since Redis 8.0, call config_set from core module insteadoptionr}   c                 C   s   t d||g}| j| }|dkS )  Set runtime configuration option.

        ### Parameters

        - **option**: the name of the configuration option.
        - **value**: a value for the configuration option.

        For more information see `FT.CONFIG SET <https://redis.io/commands/ft.config-set>`_.
        SETOKr>   r3  rb   r  r}   rl   r  rc   rc   rd   
config_set6  s   
zSearchCommands.config_setDdeprecated since Redis 8.0, call config_get from core module insteadc                 C   s    t d|g}| j| }| t |S )Get runtime configuration option value.

        ### Parameters

        - **option**: the name of the configuration option.

        For more information see `FT.CONFIG GET <https://redis.io/commands/ft.config-get>`_.
        GETr>   r3  rr   rb   r  rl   rm   rc   rc   rd   
config_getH  s   

zSearchCommands.config_gettagfieldc                 C   s   |  t| j|S )z
        Return a list of all possible tag values

        ### Parameters

        - **tagfield**: Tag field name

        For more information see `FT.TAGVALS <https://redis.io/commands/ft.tagvals>`_.
        )r3  TAGVALS_CMDr-  )rb   r  rc   rc   rd   tagvalsY     zSearchCommands.tagvalsaliasc                 C      |  t|| jS )z
        Alias a search index - will fail if alias already exists

        ### Parameters

        - **alias**: Name of the alias to create

        For more information see `FT.ALIASADD <https://redis.io/commands/ft.aliasadd>`_.
        )r3  ALIAS_ADD_CMDr-  rb   r  rc   rc   rd   aliasaddf  r  zSearchCommands.aliasaddc                 C   r  )z
        Updates an alias - will fail if alias does not already exist

        ### Parameters

        - **alias**: Name of the alias to create

        For more information see `FT.ALIASUPDATE <https://redis.io/commands/ft.aliasupdate>`_.
        )r3  ALIAS_UPDATE_CMDr-  r  rc   rc   rd   aliasupdates  r  zSearchCommands.aliasupdatec                 C      |  t|S )z
        Removes an alias to a search index

        ### Parameters

        - **alias**: Name of the alias to delete

        For more information see `FT.ALIASDEL <https://redis.io/commands/ft.aliasdel>`_.
        )r3  ALIAS_DEL_CMDr  rc   rc   rd   aliasdel  s   
zSearchCommands.aliasdelc                 C   s(   |  t| j}t|tr|S | t|S )  
        List all aliases associated with the current index as an unordered set.

        The index must be the name of an index created with ``FT.CREATE``; an
        alias name is not accepted as a substitute. Returns an empty set when
        the index exists but has no aliases. Alias names honor
        ``decode_responses`` (``str`` when decoding is enabled, ``bytes``
        otherwise).

        For more information see `FT.ALIASLIST <https://redis.io/commands/ft.aliaslist>`_.
        r3  rB   r-  rt   r
   rr   rj  rc   rc   rd   	aliaslist  s   
zSearchCommands.aliaslistc                 O   sl   | j dd}|D ]'}t||j|jg}|dr|d |jr*|d ||j |j|  q| d S )aJ  
        Add suggestion terms to the AutoCompleter engine. Each suggestion has
        a score and string.
        If kwargs["increment"] is true and the terms are already in the
        server's dictionary, we increment their scores.

        For more information see `FT.SUGADD <https://redis.io/commands/ft.sugadd/>`_.
        Ftransaction	incrementINCRrB  	pipelineSUGADD_COMMANDstringr   rk   r{   r   r3  executerb   rw   r   rn   pipesugr.  rc   rc   rd   sugadd  s   



zSearchCommands.sugaddrw   c                 C   r  )z
        Return the number of entries in the AutoCompleter index.

        For more information see `FT.SUGLEN <https://redis.io/commands/ft.suglen>`_.
        )r3  SUGLEN_COMMAND)rb   rw   rc   rc   rd   suglen  s   zSearchCommands.suglenr  c                 C   s   |  t||S )z
        Delete a string from the AutoCompleter index.
        Returns 1 if the string was found and deleted, 0 otherwise.

        For more information see `FT.SUGDEL <https://redis.io/commands/ft.sugdel>`_.
        )r3  SUGDEL_COMMAND)rb   rw   r  rc   rc   rd   sugdel  s   zSearchCommands.sugdel
   prefixfuzzynumr   with_payloadsc                 C   sh   t ||d|g}|r|t |r|t |r|t | j| }g }	|s'|	S t|||}
dd |
D S )t  
        Get a list of suggestions from the AutoCompleter, for a given prefix.

        Parameters:

        prefix : str
            The prefix we are searching. **Must be valid ascii or utf-8**
        fuzzy : bool
            If set to true, the prefix search is done in fuzzy mode.
            **NOTE**: Running fuzzy searches on short (<3 letters) prefixes
            can be very
            slow, and even scan the entire index.
        with_scores : bool
            If set to true, we also return the (refactored) score of
            each suggestion.
            This is normally not needed, and is NOT the original score
            inserted into the index.
        with_payloads : bool
            Return suggestion payloads
        num : int
            The maximum number of results we return. Note that we might
            return less. The algorithm trims irrelevant suggestions.

        Returns:

        list:
             A list of Suggestion objects. If with_scores was False, the
             score of all suggestions is 1.

        For more information see `FT.SUGGET <https://redis.io/commands/ft.sugget>`_.
        MAXc                 S      g | ]}|qS rc   rc   r   rc   rc   rd   r         z)SearchCommands.sugget.<locals>.<listcomp>SUGGET_COMMANDr{   r/   r.   r0   r3  r$   )rb   rw   r  r  r  r   r  r.  rm   r   parserrc   rc   rd   sugget  s   (



zSearchCommands.suggetgroupidskipinitialc                 G   s0   t | j|g}|r|dg || | j| S )a  
        Updates a synonym group.
        The command is used to create or update a synonym group with
        additional terms.
        Only documents which were indexed after the update will be affected.

        Parameters:

        groupid :
            Synonym group id.
        skipinitial : bool
            If set to true, we do not scan and index.
        terms :
            The terms.

        For more information see `FT.SYNUPDATE <https://redis.io/commands/ft.synupdate>`_.
        r-   )SYNUPDATE_CMDr-  r  r3  )rb   r  r  r  rl   rc   rc   rd   	synupdate  s
   

zSearchCommands.synupdatec                 C   rg  )a  
        Dumps the contents of a synonym group.

        The command is used to dump the synonyms data structure.
        Returns a list of synonym terms and their synonym group ids.

        For more information see `FT.SYNDUMP <https://redis.io/commands/ft.syndump>`_.
        )r3  r@   r-  rr   rj  rc   rc   rd   syndump  s   	zSearchCommands.syndumprs   r  )r  )	FFNNFNFFF)NFr@  NFFNF)Nr@  NF)Fr@  NFFNF)r@  NF)NFNNNNN)FNNNNFr  FF){__name__
__module____qualname____doc__	frozensetr4   r8   r  r6   r:   ri   re   rr   staticmethodry   r~   rQ   rR   r3   rC   r5   r7   r9   r;   r=   r?   rA   r   r   rU   rV   rW   rX   rY   rI   rE   rK   rJ   r`   r   r   rF   rM   r  rL   r_   r
  r  r]   r  rH   r^   rN   rO   r  r   r   r  r   r   r    r6  r   r9  r?  rP  rR  r   r   rV  rW  rZ  r   r   rd  rk   r   r   r   rm  rp  r"   r{  r   r   r   r   r   r   r   r
   r  r  r  r   r   r  r   r/  r#   r   r!   r  r  r  r  r  r  r  r  r   r  r  r  r   r  r  r  r  r$   r  r  r  rc   rc   rc   rd   r1   T   s   ]
	

%

'/(
	
(13


	
	

F
+
	
5






$<



 




+
8r1   c                   @   s  e Zd Zdd ZdeeeB  fddZ	d/deee	f de
eeeeeeef f  fd	d
Ze 					d0dede
e de
e de
eeeeeeef f  de
e de
e deeeef fddZ	d/deeef de
eeeeeeef f  fddZd1ddZeddddededefddZeddddedefddZd/d e
eee f  fd!d"Z!d#d$ Z"	%	&	%	%d2d'ed(ed)ed*ed+ed,ede#e$ fd-d.Z%dS )3AsyncSearchCommandsc                    s"   |  t| jI dH }| t|S )rh  Nri  rj  rc   rc   rd   r   #  s   zAsyncSearchCommands.infor  c                    s0   |  t| jI dH }t|tr|S | t|S )r  Nr  rj  rc   rc   rd   r  .  s
   
zAsyncSearchCommands.aliaslistNrf   rk  c                    s   | j ||d\}}t }tdi}t| tr||d< d|d< | jtg|R i |I dH }t|tr5|S | jt||t | d dS )	rq  rr  Trf   r   r   Nrs  rt  ru  rx  rc   rc   rd   r{  ?  s   

zAsyncSearchCommands.searchr|  r   r}  r~  r   c                    s   | j }i }t|g}	|	|  |r|	|  |r'|	|  ||d< |r1|	| | |r:|	d|f |rGd|d< |	|  d|t< ||d< | j|	i |I dH }
t|
t	ra|
S | j
t|
fi |S )r  r   r  Tr   rf   Nr  r  rc   rc   rd   r  c  s.   
z!AsyncSearchCommands.hybrid_searchc                    s   t |trt|j}t| jg|  }nt |tr'd}td| jg|  }nt	d||| 
|7 }| j| I dH }| jt|||dS )r  Tr  r  Nr  r  r  rc   rc   rd   r    s   



zAsyncSearchCommands.aggregatec                    sd   t | j|g}|r|d|g |r|dd|g |r$|dd|g | j| I dH }| t |S )a  
        Issue a spellcheck query

        ### Parameters

        **query**: search query.
        **distance***: the maximal Levenshtein distance for spelling
                       suggestions (default: 1, max: 4).
        **include**: specifies an inclusion custom dictionary.
        **exclude**: specifies an exclusion custom dictionary.

        For more information see `FT.SPELLCHECK <https://redis.io/commands/ft.spellcheck>`_.
        r  r  r  r  Nr  r  rc   rc   rd   r    s   zAsyncSearchCommands.spellcheckr  r  rS  r  r}   c                    s&   t d||g}| j| I dH }|dkS )r  r  Nr  r  r  rc   rc   rd   r    s   zAsyncSearchCommands.config_setr  c                    s,   t d|g}i }| j| I dH }| t |S )r  r  Nr  r  rc   rc   rd   r    s
   
zAsyncSearchCommands.config_getr   c                    sZ   | j |I dH } fdd| D }z|d= W n	 ty#   Y nw tdd|i|S )r[  Nc                    r\  rc   r]  r   r^  rc   rd   r     r_  z5AsyncSearchCommands.load_document.<locals>.<dictcomp>r   rc   r`  rc  rc   r^  rd   rd    s   

z!AsyncSearchCommands.load_documentc                    st   | j dd}|D ]'}t||j|jg}|dr|d |jr+|d ||j |j|  q	| I dH d S )aI  
        Add suggestion terms to the AutoCompleter engine. Each suggestion has
        a score and string.
        If kwargs["increment"] is true and the terms are already in the
        server's dictionary, we increment their scores.

        For more information see `FT.SUGADD <https://redis.io/commands/ft.sugadd>`_.
        Fr  r  r  rB  Nr  r  r  rc   rc   rd   r    s   



zAsyncSearchCommands.sugaddFr  rw   r  r  r  r   r  c                    sp   t ||d|g}|r|t |r|t |r|t | j| I dH }g }	|s+|	S t|||}
dd |
D S )r  r  Nc                 S   r  rc   rc   r   rc   rc   rd   r   _  r  z.AsyncSearchCommands.sugget.<locals>.<listcomp>r  )rb   rw   r  r  r  r   r  r.  retr   r  rc   rc   rd   r  )  s   (


zAsyncSearchCommands.suggetrs   r  r  r  )&r  r  r  r   r   r   r   r  r   r"   r   r   r   r   r{  r   r   r   r   r   r   r   r
   r  r   r   r  r  r   r  r  r  r   rd  r  r   r$   r  rc   rc   rc   rd   r  "  s    

$<


 r  )`r0  rv  typingr   r   r   r   r   r   redis._parsers.helpersr   redis.clientr	   r
   "redis.commands.search.hybrid_queryr   r   r   r   #redis.commands.search.hybrid_resultr   r   redis.typingr   redis.utilsr   r   r   r   r   helpersr   r   aggregationr   r   r   documentr   fieldr   index_definitionr    profile_informationr!   rf   r"   r   r#   r   r$   r%   r,  r8  r4   rH  rQ  r=  r  EXPLAINCLI_CMDrX  r8   r:   r  r<   r  r  r  re  r>   r  r  r  r  rB   r2   r  r  r  r  r  r@   r6   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r  rc   rc   rc   rd   <module>   s                  [