
    Q
i             	          d dl mZ d dlmZmZmZmZ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mZmZ  ee          Z ed	          Z ed
          Ze	eeeef         ee	eeeef         f                  f         Z G d de          Z G d de          Z G d de          Z  G d d          Z! G d dee          Z" G d de!e          Z# G d de!e          Z$ G d de$          Z% G d de          Z&dS )    )Enum)AnyDictListOptionalSetTupleUnion)Query)FilterExpression)array_to_buffer)
get_logger)TokenEscaper)denorm_cosine_distancelazy_importnltkznltk.corpus.stopwordsc                   D    e Zd ZU dZi Zeeef         ed<    e	d          Z
eee	f         ed<   dZee         ed<   ddef fdZd	efd
Zd	efdZedee         d	eeeef                  fd            Z	 ddee         ded	d f fdZ	 ddeeee	f                  fdZed	eee	f         fd            Zedd            Zed	eeef         fd            Zed	efd            Zej        dee         fd            Zdddeeeee         f                  f fdZ xZ S ) 	BaseQuerya  
    Base query class used to subclass many query types.

    NOTE: In the base class, the `_query_string` field is set once on
    initialization, and afterward, redis-py expects to be able to read it. By
    contrast, our query subclasses allow users to call methods that alter the
    query string at runtime. To avoid having to rebuild `_query_string` every
    time one of these methods is called, we lazily build the query string when a
    user calls `query()` or accesses the property `_query_string`, when the
    underlying `_built_query_string` field is None. Any method that alters the query
    string should set `_built_query_string` to None so that the next time the query
    string is accessed, it is rebuilt.
    _params*_filter_expressionN_built_query_stringquery_stringc                 ~    t                                          |           d| _        t                      | _        dS )z
        Initialize the BaseQuery class.

        Args:
            query_string (str, optional): The query string to use. Defaults to '*'.
        N)super__init__r   set_skip_decode_fields)selfr   	__class__s     C:\Users\Dell Inspiron 16\Desktop\tws\AgrotaPowerBi\back-agrota-powerbi\mcp-client-agrota\venv\Lib\site-packages\redisvl/query/query.pyr   zBaseQuery.__init__,   s;     	&&&
 $(  .1UU       returnc                 d    d                     d |                                 D                       S )z.Return the string representation of the query. c                 ,    g | ]}t          |          S  )str).0xs     r!   
<listcomp>z%BaseQuery.__str__.<locals>.<listcomp>A   s    999AQ999r"   )joinget_argsr   s    r!   __str__zBaseQuery.__str__?   s+    xx99999:::r"   c                      t          d          )z"Build the full Redis query string.z!Must be implemented by subclasses)NotImplementedErrorr.   s    r!   _build_query_stringzBaseQuery._build_query_stringC   s    !"EFFFr"   	sort_specc                 B   | | g k    rg S g }t          | t                    r|                    | df           ndt          | t                    rt	          |           dk    rt          dt	          |                      | \  }}t          |t                    st          dt          |                     t          |t                    st          dt          |                     |                                }|dvrt          d| d	          |                    ||d
k    f           nit          | t                    r5| D ]1}t                              |          }|                    |           2nt          dt          |                      |S )ah  Parse sort specification into list of (field, ascending) tuples.

        Args:
            sort_spec: Sort specification in various formats:
                - str: single field name (defaults to ASC)
                - Tuple[str, str]: (field_name, "ASC"|"DESC")
                - List: list of strings or tuples

        Returns:
            List of (field_name, ascending) tuples where ascending is a boolean.

        Raises:
            TypeError: If sort_spec is not a valid type.
            ValueError: If direction is not "ASC" or "DESC".

        Examples:
            >>> BaseQuery._parse_sort_spec("price")
            [("price", True)]
            >>> BaseQuery._parse_sort_spec(("price", "DESC"))
            [("price", False)]
            >>> BaseQuery._parse_sort_spec(["price", ("rating", "DESC")])
            [("price", True), ("rating", False)]
        NT   z@Sort tuple must have exactly 2 elements (field, direction), got !Field name must be a string, got z Direction must be a string, got )ASCDESCz-Sort direction must be 'ASC' or 'DESC', got ''r7   z.sort_by must be a string, tuple, or list, got )
isinstancer(   appendtuplelen
ValueError	TypeErrortypeupperlistr   _parse_sort_specextend)r3   resultfield	directiondirection_upperitemparseds          r!   rC   zBaseQuery._parse_sort_specG   s   2 	RI)+ i%% !	MM9d+,,,, 	5)) 	9~~"" gWZ[dWeWegg    )E9eS)) S QDKK Q QRRRi-- V T4	?? T TUUU'oo//Oo55 PIPPP   MM5/U":;<<<< 	4(( 		! & &"33D99f%%%%& RiRR   r"   Tascc                    ||g k    r	d| _         | S t          |t                    r
|dur||fg}n|                     |          }|s	d| _         | S |d         \  }}t	          |          dk    r+t
                              dd |D              d| d           t                                          ||	           | S )
a  Set the sort order for query results.

        This method supports sorting by single or multiple fields. Note that Redis Search
        natively supports only a single SORTBY field. When multiple fields are specified,
        only the FIRST field is used for the Redis SORTBY clause.

        Args:
            sort_spec: Sort specification in various formats:
                - str: single field name
                - Tuple[str, str]: (field_name, "ASC"|"DESC")
                - List: list of field names or tuples
            asc: Default sort direction when not specified (only used when sort_spec is a string).
                Defaults to True (ascending).

        Returns:
            self: Returns the query object for method chaining.

        Raises:
            TypeError: If sort_spec is not a valid type.
            ValueError: If direction is not "ASC" or "DESC".

        Examples:
            >>> query.sort_by("price")  # Single field, ascending
            >>> query.sort_by(("price", "DESC"))  # Single field, descending
            >>> query.sort_by(["price", "rating"])  # Multiple fields (only first used)
            >>> query.sort_by([("price", "DESC"), ("rating", "ASC")])

        Note:
            When multiple fields are specified, only the first field is used for sorting
            in Redis. Future versions may support multi-field sorting through post-query
            sorting in Python.
        NTr      z Multiple sort fields specified: c                     g | ]
}|d          S )r   r'   )r)   fs     r!   r+   z%BaseQuery.sort_by.<locals>.<listcomp>   s    3I3I3IQAaD3I3I3Ir"   zG. Redis Search only supports single-field sorting. Using first field: 'z!'. Additional fields are ignored.)rK   )	_sortbyr:   r(   rC   r=   loggerwarningr   sort_by)r   r3   rK   rJ   first_field	first_ascr    s         r!   rS   zBaseQuery.sort_by   s    F 	RDLK
 i%% 	6#T// #&'FF **955F 	DLK "(Y v;;??NN13I3I&3I3I3I 1 1Xc1 1 1   	333r"   filter_expressionc                     |t          d          | _        n3t          |t           t          f          r|| _        nt	          d          d| _        dS )aE  Set the filter expression for the query.

        Args:
            filter_expression (Optional[Union[str, FilterExpression]], optional): The filter
                expression or query string to use on the query.

        Raises:
            TypeError: If filter_expression is not a valid FilterExpression or string.
        Nr   zDfilter_expression must be of type FilterExpression or string or None)r   r   r:   r(   r?   r   )r   rV   s     r!   
set_filterzBaseQuery.set_filter   sf     $&6s&;&;D##),<c+BCC 	&7D##V  
 $(   r"   c                     | j         S )z$The filter expression for the query.)r   r.   s    r!   filterzBaseQuery.filter   s     &&r"   c                     | S )z Return self as the query object.r'   r.   s    r!   queryzBaseQuery.query   s	     r"   c                     | j         S )zReturn the query parameters.)r   r.   s    r!   paramszBaseQuery.params   s     |r"   c                 P    | j         |                                 | _         | j         S )zGMaintains compatibility with parent class while providing lazy loading.)r   r2   r.   s    r!   _query_stringzBaseQuery._query_string   s*     #+'+'?'?'A'AD$''r"   valuec                     || _         dS )zESetter for _query_string to maintain compatibility with parent class.N)r   )r   ra   s     r!   r`   zBaseQuery._query_string   s     $)   r"   )skip_decoderc   c                
   |g | _         i | _        t          |t                    r|h}|h| _        nHt          |t
                    r$t          |          }t          |          | _        nt          d          |D ]K}||v r$t                      	                    |d           *t                      	                    |           Ln9 t                      j
        |  t          | d          st                      | _        | S )a  
        Set the fields to return with search results.

        Args:
            *fields: Variable number of field names to return.
            skip_decode: Optional field name or list of field names that should not be
                decoded. Useful for binary data like embeddings.

        Returns:
            self: Returns the query object for method chaining.

        Raises:
            TypeError: If skip_decode is not a string, list, or None.
        Nz/skip_decode must be a string or list of stringsF)decode_fieldr   )_return_fields_return_fields_decode_asr:   r(   r   rB   r   r?   r   return_fieldreturn_fieldshasattr)r   rc   fieldsskip_decode_setrF   r    s        r!   ri   zBaseQuery.return_fields  s    & ""$D,.D) +s++ S#.-,7=((K.. S"%k"2"2+.{+;+;(( QRRR   0 0O++GG((U(CCCC GG((////0 "EGG!6** 4!677 1+.55(r"   )r   )NTN)r#   r   )!__name__
__module____qualname____doc__r   r   r(   r   __annotations__r   r   r
   r   r   r   r/   r2   staticmethodSortSpecr   r	   boolrC   rS   rX   propertyrZ   r\   r^   r`   setterri   __classcell__r    s   @r!   r   r      s          !GT#s(^   7G7G7L7Lc#334LLL)-#---3 3S 3 3 3 3 3 3&; ; ; ; ;GS G G G G AHX$6 A4c4i@P;Q A A A \AH AED D!(+D9=D	D D D D D DN KO( (!)%5E0E*F!G( ( ( (2 'c#334 ' ' ' X'    X S#X    X (s ( ( ( X( )8C= ) ) ) )
 GK2 2 2$,U3S	>-B$C2 2 2 2 2 2 2 2 2 2r"   r   c                        e Zd Z	 	 	 	 	 	 	 ddeeeef                  deee                  deded	ee	         d
e
deeeef                  f fdZdefdZ xZS )FilterQueryN
   r5   FrV   ri   num_resultsdialectrS   in_orderr^   c                 t   |                      |           |r|| _        || _        t                                          d           d| _        |r
 | j        |  |                     d| j                                      |           |r| 	                    |           |r| 
                                 dS dS )a5  A query for running a filtered search with a filter expression.

        Args:
            filter_expression (Optional[Union[str, FilterExpression]]): The optional filter
                expression to query with. Defaults to '*'.
            return_fields (Optional[List[str]], optional): The fields to return.
            num_results (Optional[int], optional): The number of results to return. Defaults to 10.
            dialect (int, optional): The query dialect. Defaults to 2.
            sort_by (Optional[SortSpec], optional): The field(s) to order the results by. Can be:
                - str: single field name (e.g., "price")
                - Tuple[str, str]: (field_name, "ASC"|"DESC") (e.g., ("price", "DESC"))
                - List: list of fields or tuples (e.g., ["price", ("rating", "DESC")])
                Note: Redis Search only supports single-field sorting, so only the first field is used.
                Defaults to None.
            in_order (bool, optional): Requires the terms in the field to have the same order as the
                terms in the query filter. Defaults to False.
            params (Optional[Dict[str, Any]], optional): The parameters for the query. Defaults to None.

        Raises:
            TypeError: If filter_expression is not of type redisvl.query.FilterExpression
        r   Nr   )rX   r   _num_resultsr   r   r   ri   pagingr~   rS   r   )	r   rV   ri   r}   r~   rS   r   r^   r    s	           r!   r   zFilterQuery.__init__;  s    > 	)*** 	"!DL' 	#'   	/D..At())11'::: 	"LL!!! 	MMOOOOO	 	r"   r#   c                 l    t          | j        t                    rt          | j                  S | j        S zEBuild the full query string based on the filter and other components.r:   r   r   r(   r.   s    r!   r2   zFilterQuery._build_query_stringp  2    d-/?@@ 	0t.///&&r"   )NNr|   r5   NFN)rn   ro   rp   r   r
   r(   r   r   intrt   ru   r   r   r   r2   rx   ry   s   @r!   r{   r{   :  s         EI-1&*+/3 3#E#/?*?$@A3  S	*3 	3
 3 (#3 3 c3h(3 3 3 3 3 3j'S ' ' ' ' ' ' ' 'r"   r{   c            
       x     e Zd Z	 	 	 d	deeeef                  dedeeee	f                  f fdZ
defdZ xZS )

CountQueryNr5   rV   r~   r^   c                 
   |                      |           |r|| _        t                                          d           d| _        |                                                     dd                              |           dS )a  A query for a simple count operation provided some filter expression.

        Args:
            filter_expression (Optional[Union[str, FilterExpression]]): The filter expression to
                query with. Defaults to None.
            params (Optional[Dict[str, Any]], optional): The parameters for the query. Defaults to None.

        Raises:
            TypeError: If filter_expression is not of type redisvl.query.FilterExpression

        .. code-block:: python

            from redisvl.query import CountQuery
            from redisvl.query.filter import Tag

            t = Tag("brand") == "Nike"
            query = CountQuery(filter_expression=t)

            count = index.query(query)
        r   Nr   )rX   r   r   r   r   
no_contentr   r~   )r   rV   r~   r^   r    s       r!   r   zCountQuery.__init__x  s    4 	)*** 	"!DL 	#'  	  A&&..w77777r"   r#   c                 l    t          | j        t                    rt          | j                  S | j        S r   r   r.   s    r!   r2   zCountQuery._build_query_string  r   r"   )Nr5   N)rn   ro   rp   r   r
   r(   r   r   r   r   r   r2   rx   ry   s   @r!   r   r   w  s         EI+/	$8 $8#E#/?*?$@A$8 $8 c3h(	$8 $8 $8 $8 $8 $8L'S ' ' ' ' ' ' ' 'r"   r   c                       e Zd ZU dZeed<   dZeed<   dZeed<   dZeed<   dZ	eed	<   d
Z
eed
<   d
Zeed<   dZeed<   dZeed<   dZeed<   dZeed<   dZeed<   dS )BaseVectorQueryvector_distanceDISTANCE_IDvectorVECTOR_PARAM
EF_RUNTIMEEFEF_RUNTIME_PARAMEPSILONEPSILON_PARAMSEARCH_WINDOW_SIZESEARCH_WINDOW_SIZE_PARAMUSE_SEARCH_HISTORYUSE_SEARCH_HISTORY_PARAMSEARCH_BUFFER_CAPACITYSEARCH_BUFFER_CAPACITY_PARAMF_normalize_vector_distanceN)rn   ro   rp   r   r(   rr   r   r   r   r   r   r   r   r   r   r   r   ru   r'   r"   r!   r   r     s         (K((( L#    #J""" c   "M3""" 3222$8c8882222$8c888":C:::(@ #@@@',,,,,,r"   r   c                       e Zd ZdZdZdZdS )HybridPolicyz7Enum for valid hybrid policy options in vector queries.BATCHESADHOC_BFN)rn   ro   rp   rq   r   r   r'   r"   r!   r   r     s        AAGHHHr"   r   c            %           e Zd Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d+deee         ef         ded	eee                  d
eeee	f                  dede
dede
dee         dedee         dee
         dee
         dee         dee
         dee         dee
         def$ fdZdefdZdefdZde
fdZde
fdZdefdZde
fd Zdefd!Zde
fd"Zedee         fd#            Zedee
         fd$            Zedee
         fd%            Zedee         fd&            Zedee
         fd'            Zedee         fd(            Zedee
         fd)            Zedeeef         fd*            Z  xZ!S ),VectorQueryNfloat32r|   Tr5   Fr   vector_field_nameri   rV   dtyper}   return_scorer~   rS   r   hybrid_policy
batch_size
ef_runtimeepsilonsearch_window_sizeuse_search_historysearch_buffer_capacitynormalize_vector_distancec                    || _         || _        || _        || _        d| _        d| _        d| _        d| _        d| _        d| _	        d| _
        || _        |                     |           t                                          d           d| _        |r
 | j        |  |                     d| j                                      |           |r|                     | j                   |	r|                     |	           n|                     | j                   |
r|                                  ||                     |           ||                     |           ||                     |           ||                     |           ||                     |           ||                     |           ||                     |           dS dS )a  A query for running a vector search along with an optional filter
        expression.

        Args:
            vector (List[float]): The vector to perform the vector search with.
            vector_field_name (str): The name of the vector field to search
                against in the database.
            return_fields (List[str]): The declared fields to return with search
                results.
            filter_expression (Union[str, FilterExpression], optional): A filter to apply
                along with the vector search. Defaults to None.
            dtype (str, optional): The dtype of the vector. Defaults to
                "float32".
            num_results (int, optional): The top k results to return from the
                vector search. Defaults to 10.

            return_score (bool, optional): Whether to return the vector
                distance. Defaults to True.
            dialect (int, optional): The RediSearch query dialect.
                Defaults to 2.
            sort_by (Optional[SortSpec]): The field(s) to order the results by. Can be:
                - str: single field name
                - Tuple[str, str]: (field_name, "ASC"|"DESC")
                - List: list of fields or tuples
                Note: Only the first field is used for Redis sorting.
                Defaults to None. Results will be ordered by vector distance.
            in_order (bool): Requires the terms in the field to have
                the same order as the terms in the query filter, regardless of
                the offsets between them. Defaults to False.
            hybrid_policy (Optional[str]): Controls how filters are applied during vector search.
                Options are "BATCHES" (paginates through small batches of nearest neighbors) or
                "ADHOC_BF" (computes scores for all vectors passing the filter).
                "BATCHES" mode is typically faster for queries with selective filters.
                "ADHOC_BF" mode is better when filters match a large portion of the dataset.
                Defaults to None, which lets Redis auto-select the optimal policy.
            batch_size (Optional[int]): When hybrid_policy is "BATCHES", controls the number
                of vectors to fetch in each batch. Larger values may improve performance
                at the cost of memory usage. Only applies when hybrid_policy="BATCHES".
                Defaults to None, which lets Redis auto-select an appropriate batch size.
            ef_runtime (Optional[int]): Controls the size of the dynamic candidate list for HNSW
                algorithm at query time. Higher values improve recall at the expense of
                slower search performance. Defaults to None, which uses the index-defined value.
            epsilon (Optional[float]): The range search approximation factor for HNSW and SVS-VAMANA
                indexes. Sets boundaries for candidates within radius * (1 + epsilon). Higher values
                allow more extensive search and more accurate results at the expense of run time.
                Defaults to None, which uses the index-defined value (typically 0.01).
            search_window_size (Optional[int]): The size of the search window for SVS-VAMANA KNN searches.
                Increasing this value generally yields more accurate but slower search results.
                Defaults to None, which uses the index-defined value (typically 10).
            use_search_history (Optional[str]): For SVS-VAMANA indexes, controls whether to use the
                search buffer or entire search history. Options are "OFF", "ON", or "AUTO".
                "AUTO" is always evaluated internally as "ON". Using the entire history may yield
                a slightly better graph at the cost of more search time.
                Defaults to None, which uses the index-defined value (typically "AUTO").
            search_buffer_capacity (Optional[int]): Tuning parameter for SVS-VAMANA indexes using
                two-level compression (LVQ<X>x<Y> or LeanVec types). Determines the number of vector
                candidates to collect in the first level of search before the re-ranking level.
                Defaults to None, which uses the index-defined value (typically SEARCH_WINDOW_SIZE).
            normalize_vector_distance (bool): Redis supports 3 distance metrics: L2 (euclidean),
                IP (inner product), and COSINE. By default, L2 distance returns an unbounded value.
                COSINE distance returns a value between 0 and 2. IP returns a value determined by
                the magnitude of the vector. Setting this flag to true converts COSINE and L2 distance
                to a similarity score between 0 and 1. Note: setting this flag to true for IP will
                throw a warning since by definition COSINE similarity is normalized IP.

        Raises:
            TypeError: If filter_expression is not of type redisvl.query.FilterExpression

        Note:
            Learn more about vector queries in Redis: https://redis.io/docs/latest/develop/ai/search-and-query/vectors/#knn-vector-search
        Nr   r   )_vector_vector_field_name_dtyper   _hybrid_policy_batch_size_ef_runtime_epsilon_search_window_size_use_search_history_search_buffer_capacityr   rX   r   r   r   ri   r   r~   r   rS   r   set_hybrid_policyset_batch_sizeset_ef_runtimeset_epsilonset_search_window_sizeset_use_search_historyset_search_buffer_capacity)r   r   r   ri   rV   r   r}   r   r~   rS   r   r   r   r   r   r   r   r   r   r    s                      r!   r   zVectorQuery.__init__  s   x "3'6:*.*.)-26 26 6:$*C')*** 	#'   	/D..At())11'::: 	1t/000 	+LL!!!!LL)*** 	MMOOO$""=111!
+++!
+++W%%%)''(:;;;)''(:;;;!-++,BCCCCC .-r"   r#   c                 B   | j         }t          |t                    rt          |          }d| j         d| j         d| j         }| j        r;|d| j        j         z  }| j        t          j
        k    r| j        r|d| j         z  }| j        r|d| j         d| j         z  }| j        |d| j         z  }| j        |d| j         d| j         z  }| j        |d| j         d| j         z  }| j        |d| j         d| j         z  }|d	| j         z  }| d
| dS )zFBuild the full query string for vector search with optional filtering.zKNN z @ $z HYBRID_POLICY z BATCH_SIZE r%   Nz
 EPSILON $z AS z=>[])r   r:   r   r(   r   r   r   r   ra   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r   rV   	knn_querys      r!   r2   zVectorQuery._build_query_stringU  s    3')9:: 	7 #$5 6 6 W4$VV(?VV4CTVV 	
  	?F4+>+DFFFI "l&:::t?O:>D,<>>>	  	HGT_GG0EGGGI =$:d&8:::I #/WT4WW8UWWWI #/WT4WW8UWWWI '3VD/VV43TVVI
 	.D,...	#44	4444r"   c                     	 t          |          | _        nB# t          $ r5 t          dd                    d t           D                                  w xY wd| _        dS )  Set the hybrid policy for the query.

        Args:
            hybrid_policy (str): The hybrid policy to use. Options are "BATCHES"
                                or "ADHOC_BF".

        Raises:
            ValueError: If hybrid_policy is not one of the valid options
        hybrid_policy must be one of , c                     g | ]	}|j         
S r'   ra   r)   ps     r!   r+   z1VectorQuery.set_hybrid_policy.<locals>.<listcomp>      :Y:Y:Yq17:Y:Y:Yr"   Nr   r   r>   r,   r   r   r   s     r!   r   zVectorQuery.set_hybrid_policy  }    	".}"="=D 	 	 	\		:Y:YL:Y:Y:Y0Z0Z\\  	 $(   	    ?Ac                     t          |t                    st          d          |dk    rt          d          || _        d| _        dS a  Set the batch size for the query.

        Args:
            batch_size (int): The batch size to use when hybrid_policy is "BATCHES".

        Raises:
            TypeError: If batch_size is not an integer
            ValueError: If batch_size is not positive
        zbatch_size must be an integerr   zbatch_size must be positiveNr:   r   r?   r>   r   r   r   r   s     r!   r   zVectorQuery.set_batch_size  U     *c** 	=;<<<??:;;;% $(   r"   c                     t          |t                    st          d          |dk    rt          d          || _        d| _        dS )a]  Set the EF_RUNTIME parameter for the query.

        Args:
            ef_runtime (int): The EF_RUNTIME value to use for HNSW algorithm.
                Higher values improve recall at the expense of slower search.

        Raises:
            TypeError: If ef_runtime is not an integer
            ValueError: If ef_runtime is not positive
        zef_runtime must be an integerr   zef_runtime must be positiveN)r:   r   r?   r>   r   r   )r   r   s     r!   r   zVectorQuery.set_ef_runtime  sU     *c** 	=;<<<??:;;;% $(   r"   c                     t          |t          t          f          st          d          |dk     rt	          d          || _        d| _        dS )a  Set the epsilon parameter for the query.

        Args:
            epsilon (float): The range search approximation factor for HNSW and SVS-VAMANA
                indexes. Sets boundaries for candidates within radius * (1 + epsilon).
                Higher values allow more extensive search and more accurate results at the
                expense of run time.

        Raises:
            TypeError: If epsilon is not a float or int
            ValueError: If epsilon is negative
        $epsilon must be of type float or intr   epsilon must be non-negativeNr:   floatr   r?   r>   r   r   r   r   s     r!   r   zVectorQuery.set_epsilon  sY     'E3<00 	DBCCCQ;;;<<< $(   r"   c                     t          |t                    st          d          |dk    rt          d          || _        d| _        dS )a  Set the SEARCH_WINDOW_SIZE parameter for the query.

        Args:
            search_window_size (int): The size of the search window for SVS-VAMANA KNN searches.
                Increasing this value generally yields more accurate but slower search results.

        Raises:
            TypeError: If search_window_size is not an integer
            ValueError: If search_window_size is not positive
        %search_window_size must be an integerr   #search_window_size must be positiveNr:   r   r?   r>   r   r   r   r   s     r!   r   z"VectorQuery.set_search_window_size  sY     ,c22 	ECDDD""BCCC#5  $(   r"   c                     t          |t                    st          d          g d}||vr%t          dd                    |                     || _        d| _        dS )a  Set the USE_SEARCH_HISTORY parameter for the query.

        Args:
            use_search_history (str): For SVS-VAMANA indexes, controls whether to use the
                search buffer or entire search history. Options are "OFF", "ON", or "AUTO".

        Raises:
            TypeError: If use_search_history is not a string
            ValueError: If use_search_history is not one of "OFF", "ON", or "AUTO"
        #use_search_history must be a stringOFFONAUTO"use_search_history must be one of r   Nr:   r(   r?   r>   r,   r   r   r   r   valid_optionss      r!   r   z"VectorQuery.set_use_search_history       ,c22 	CABBB---]22OTYY}5M5MOO   $6  $(   r"   c                     t          |t                    st          d          |dk    rt          d          || _        d| _        dS )a  Set the SEARCH_BUFFER_CAPACITY parameter for the query.

        Args:
            search_buffer_capacity (int): Tuning parameter for SVS-VAMANA indexes using
                two-level compression. Determines the number of vector candidates to collect
                in the first level of search before the re-ranking level.

        Raises:
            TypeError: If search_buffer_capacity is not an integer
            ValueError: If search_buffer_capacity is not positive
        )search_buffer_capacity must be an integerr   'search_buffer_capacity must be positiveNr:   r   r?   r>   r   r   r   r   s     r!   r   z&VectorQuery.set_search_buffer_capacity  sY     0#66 	IGHHH!Q&&FGGG'=$ $(   r"   c                 ,    | j         r| j         j        ndS z~Return the hybrid policy for the query.

        Returns:
            Optional[str]: The hybrid policy for the query.
        Nr   ra   r.   s    r!   r   zVectorQuery.hybrid_policy       -1,?It"((TIr"   c                     | j         S zxReturn the batch size for the query.

        Returns:
            Optional[int]: The batch size for the query.
        r   r.   s    r!   r   zVectorQuery.batch_size       r"   c                     | j         S )zReturn the EF_RUNTIME parameter for the query.

        Returns:
            Optional[int]: The EF_RUNTIME value for the query.
        )r   r.   s    r!   r   zVectorQuery.ef_runtime&  r   r"   c                     | j         S )zReturn the epsilon parameter for the query.

        Returns:
            Optional[float]: The epsilon value for the query.
        r   r.   s    r!   r   zVectorQuery.epsilon/       }r"   c                     | j         S zReturn the SEARCH_WINDOW_SIZE parameter for the query.

        Returns:
            Optional[int]: The SEARCH_WINDOW_SIZE value for the query.
        r   r.   s    r!   r   zVectorQuery.search_window_size8       ''r"   c                     | j         S zReturn the USE_SEARCH_HISTORY parameter for the query.

        Returns:
            Optional[str]: The USE_SEARCH_HISTORY value for the query.
        r   r.   s    r!   r   zVectorQuery.use_search_historyA  r  r"   c                     | j         S zReturn the SEARCH_BUFFER_CAPACITY parameter for the query.

        Returns:
            Optional[int]: The SEARCH_BUFFER_CAPACITY value for the query.
        r   r.   s    r!   r   z"VectorQuery.search_buffer_capacityJ       ++r"   c                 n   t          | j        t                    r| j        }nt          | j        | j                  }| j        |i}| j        | j        || j        <   | j        | j        || j	        <   | j
        | j
        || j        <   | j        | j        || j        <   | j        | j        || j        <   |S zyReturn the parameters for the query.

        Returns:
            Dict[str, Any]: The parameters for the query.
        )r   )r:   r   bytesr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r^   s      r!   r^   zVectorQuery.paramsS  s     dlE** 	F\FF$T\EEEF"&"3V!< ',0,<F4() =$)-F4%& #/484LF401 #/484LF401 '38<8TF445r"   )NNr   r|   Tr5   NFNNNNNNNF)"rn   ro   rp   r
   r   r   r  r(   r   r   r   ru   rt   r   r2   r   r   r   r   r   r   r   rv   r   r   r   r   r   r   r   r   r   r^   rx   ry   s   @r!   r   r     s       
 .2DH!&*'+$($(#',0,004*/'RD RDd5k5()RD RD  S	*	RD
 $E#/?*?$@ARD RD RD RD RD (#RD RD  }RD SMRD SMRD %RD  %SM!RD" %SM#RD$ !)%RD& $('RD RD RD RD RD RDh,5S ,5 ,5 ,5 ,5\(s ( ( ( ((( ( ( ( (&( ( ( ( (((5 ( ( ( (,( ( ( ( ((( ( ( ( (.( ( ( ( (* Jx} J J J XJ  HSM       X   HSM       X  %    X (HSM ( ( ( X( (HSM ( ( ( X( , , , , X, !S#X ! ! ! X! ! ! ! !r"   r   c            %            e Zd ZU dZeed<   dZeed<   dZeed<   dZeed<   	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d3de	e
e         ef         dedee
e                  dee	eef                  dededee         dee         dee         dee         dedededee         dedee         dee         d ef$ fd!Zdefd"Zdefd#Zdefd$Zdefd%Zdefd&Zdefd'Zdefd(Zd)efd*Zed)efd+            Zed)ee         fd,            Zed)ee         fd-            Zed)ee         fd.            Zed)ee         fd/            Z ed)ee         fd0            Z!ed)ee         fd1            Z"ed)e#ee$f         fd2            Z% xZ&S )4VectorRangeQuerydistance_thresholdDISTANCE_THRESHOLD_PARAMr   r   HYBRID_POLICYHYBRID_POLICY_PARAM
BATCH_SIZEBATCH_SIZE_PARAMNr   皙?r|   Tr5   Fr   r   ri   rV   r   r   r   r   r   r}   r   r~   rS   r   r   r   r   c                    || _         || _        || _        || _        d| _        d| _        d| _        d| _        d| _        d| _	        d| _
        || _        t                                          d           d| _        ||                     |           ||                     |           |	|                     |	           |
|                     |
           ||                     |           ||                     |           |                     |           |                     |           |r
 | j        |  |                     d| j                                      |           |r|                     | j                   |r|                     |           n|                     | j                   |r|                                  dS dS )a  A query for running a filtered vector search based on semantic
        distance threshold.

        Args:
            vector (List[float]): The vector to perform the range query with.
            vector_field_name (str): The name of the vector field to search
                against in the database.
            return_fields (List[str]): The declared fields to return with search
                results.
            filter_expression (Union[str, FilterExpression], optional): A filter to apply
                along with the range query. Defaults to None.
            dtype (str, optional): The dtype of the vector. Defaults to
                "float32".
            distance_threshold (float): The threshold for vector distance.
                A smaller threshold indicates a stricter semantic search.
                Defaults to 0.2.
            epsilon (Optional[float]): The relative factor for vector range queries,
                setting boundaries for candidates within radius * (1 + epsilon).
                This controls how extensive the search is beyond the specified radius.
                Higher values increase recall at the expense of performance.
                Defaults to None, which uses the index-defined epsilon (typically 0.01).
            search_window_size (Optional[int]): The size of the search window for SVS-VAMANA range searches.
                Increasing this value generally yields more accurate but slower search results.
                Defaults to None, which uses the index-defined value (typically 10).
            use_search_history (Optional[str]): For SVS-VAMANA indexes, controls whether to use the
                search buffer or entire search history. Options are "OFF", "ON", or "AUTO".
                "AUTO" is always evaluated internally as "ON". Using the entire history may yield
                a slightly better graph at the cost of more search time.
                Defaults to None, which uses the index-defined value (typically "AUTO").
            search_buffer_capacity (Optional[int]): Tuning parameter for SVS-VAMANA indexes using
                two-level compression (LVQ<X>x<Y> or LeanVec types). Determines the number of vector
                candidates to collect in the first level of search before the re-ranking level.
                Defaults to None, which uses the index-defined value (typically SEARCH_WINDOW_SIZE).
            num_results (int): The MAX number of results to return.
                Defaults to 10.
            return_score (bool, optional): Whether to return the vector
                distance. Defaults to True.
            dialect (int, optional): The RediSearch query dialect.
                Defaults to 2.
            sort_by (Optional[SortSpec]): The field(s) to order the results by. Can be:
                - str: single field name
                - Tuple[str, str]: (field_name, "ASC"|"DESC")
                - List: list of fields or tuples
                Note: Only the first field is used for Redis sorting.
                Defaults to None. Results will be ordered by vector distance.
            in_order (bool): Requires the terms in the field to have
                the same order as the terms in the query filter, regardless of
                the offsets between them. Defaults to False.
            hybrid_policy (Optional[str]): Controls how filters are applied during vector search.
                Options are "BATCHES" (paginates through small batches of nearest neighbors) or
                "ADHOC_BF" (computes scores for all vectors passing the filter).
                "BATCHES" mode is typically faster for queries with selective filters.
                "ADHOC_BF" mode is better when filters match a large portion of the dataset.
                Defaults to None, which lets Redis auto-select the optimal policy.
            batch_size (Optional[int]): When hybrid_policy is "BATCHES", controls the number
                of vectors to fetch in each batch. Larger values may improve performance
                at the cost of memory usage. Only applies when hybrid_policy="BATCHES".
                Defaults to None, which lets Redis auto-select an appropriate batch size.
            normalize_vector_distance (bool): Redis supports 3 distance metrics: L2 (euclidean),
                IP (inner product), and COSINE. By default, L2 distance returns an unbounded value.
                COSINE distance returns a value between 0 and 2. IP returns a value determined by
                the magnitude of the vector. Setting this flag to true converts COSINE and L2 distance
                to a similarity score between 0 and 1. Note: setting this flag to true for IP will
                throw a warning since by definition COSINE similarity is normalized IP.

        Raises:
            TypeError: If filter_expression is not of type redisvl.query.FilterExpression

        Note:
            Learn more about vector range queries: https://redis.io/docs/interact/search-and-query/search/vectors/#range-query

        r  Nr   r   )r   r   r   r   _distance_thresholdr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   set_distance_thresholdrX   ri   r   r~   r   rS   r   )r   r   r   ri   rV   r   r  r   r   r   r   r}   r   r~   rS   r   r   r   r   r    s                      r!   r   zVectorRangeQuery.__init__~  s   z "3'*- )-26 26 6:$6:*.*C' 	#' W%%%)''(:;;;)''(:;;;!-++,BCCC$""=111!
+++##$6777)***  	/D..At())11'::: 	1t/000 	+LL!!!!LL)*** 	MMOOOOO	 	r"   c                     t          |t          t          f          st          d          |dk     rt	          d          | j        r$|dk    rt	          d          t          |          }|| _        d| _        dS )a  Set the distance threshold for the query.

        Args:
            distance_threshold (float): Vector distance threshold.

        Raises:
            TypeError: If distance_threshold is not a float or int
            ValueError: If distance_threshold is negative
        z/distance_threshold must be of type float or intr   z'distance_threshold must be non-negativerM   zXdistance_threshold must be between 0 and 1 when normalize_vector_distance is set to TrueN)	r:   r   r   r?   r>   r   r   r  r   )r   r  s     r!   r  z'VectorRangeQuery.set_distance_threshold  s     ,ucl;; 	OMNNN!!FGGG* 	L!A%% n  
 "88J!K!K#5  $(   r"   c                     t          |t          t          f          st          d          |dk     rt	          d          || _        d| _        dS )aZ  Set the epsilon parameter for the range query.

        Args:
            epsilon (float): The relative factor for vector range queries,
                setting boundaries for candidates within radius * (1 + epsilon).

        Raises:
            TypeError: If epsilon is not a float or int
            ValueError: If epsilon is negative
        r   r   r   Nr   r   s     r!   r   zVectorRangeQuery.set_epsilon-  sY     'E3<00 	DBCCCQ;;;<<< $(   r"   c                     t          |t                    st          d          |dk    rt          d          || _        d| _        dS )aB  Set the SEARCH_WINDOW_SIZE parameter for the range query.

        Args:
            search_window_size (int): The size of the search window for SVS-VAMANA range searches.

        Raises:
            TypeError: If search_window_size is not an integer
            ValueError: If search_window_size is not positive
        r   r   r   Nr   r   s     r!   r   z'VectorRangeQuery.set_search_window_sizeA  sY     ,c22 	ECDDD""BCCC#5  $(   r"   c                     t          |t                    st          d          g d}||vr%t          dd                    |                     || _        d| _        dS )a  Set the USE_SEARCH_HISTORY parameter for the range query.

        Args:
            use_search_history (str): Controls whether to use the search buffer or entire history.
                Must be one of "OFF", "ON", or "AUTO".

        Raises:
            TypeError: If use_search_history is not a string
            ValueError: If use_search_history is not one of the valid options
        r   r   r   r   Nr   r   s      r!   r   z'VectorRangeQuery.set_use_search_historyT  r   r"   c                     t          |t                    st          d          |dk    rt          d          || _        d| _        dS )aj  Set the SEARCH_BUFFER_CAPACITY parameter for the range query.

        Args:
            search_buffer_capacity (int): Tuning parameter for SVS-VAMANA indexes using
                two-level compression.

        Raises:
            TypeError: If search_buffer_capacity is not an integer
            ValueError: If search_buffer_capacity is not positive
        r   r   r   Nr   r   s     r!   r   z+VectorRangeQuery.set_search_buffer_capacityk  sY     0#66 	IGHHH!Q&&FGGG'=$ $(   r"   c                     	 t          |          | _        nB# t          $ r5 t          dd                    d t           D                                  w xY wd| _        dS )r   r   r   c                     g | ]	}|j         
S r'   r   r   s     r!   r+   z6VectorRangeQuery.set_hybrid_policy.<locals>.<listcomp>  r   r"   Nr   r   s     r!   r   z"VectorRangeQuery.set_hybrid_policy  r   r   c                     t          |t                    st          d          |dk    rt          d          || _        d| _        dS r   r   r   s     r!   r   zVectorRangeQuery.set_batch_size  r   r"   r#   c                 L   d| j          d| j         d| j         d}g }|                    d| j                    | j        |                    d| j                    | j        |                    d| j                    | j        |                    d	| j                    | j        |                    d
| j                    dd	                    |           d}| j
        }t          |t                    rt          |          }|dk    r| | S d| | d| dS )zLBuild the full query string for vector range queries with optional filtering@z:[VECTOR_RANGE $r   r   z$YIELD_DISTANCE_AS: Nz
$EPSILON: z$SEARCH_WINDOW_SIZE: z$USE_SEARCH_HISTORY: z$SEARCH_BUFFER_CAPACITY: z=>{z; }r   (r%   ))r   r  r   r;   r   r   r   r   r   r,   r   r:   r   r(   )r   
base_query
attr_partsattr_sectionrV   s        r!   r2   z$VectorRangeQuery._build_query_string  s    x0ww$B_wwcgctwww
 
C1ACCDDD =$:4=::;;; #/Pd6NPPQQQ #/Pd6NPPQQQ '3JD,HJJ  
 8dii
33777 !3')9:: 	7 #$5 6 6## 0,000B:B|BB.?BBBBr"   c                     | j         S )zReturn the distance threshold for the query.

        Returns:
            float: The distance threshold for the query.
        )r  r.   s    r!   r  z#VectorRangeQuery.distance_threshold  r  r"   c                     | j         S )zReturn the epsilon for the query.

        Returns:
            Optional[float]: The epsilon for the query, or None if not set.
        r   r.   s    r!   r   zVectorRangeQuery.epsilon  r   r"   c                     | j         S r  r  r.   s    r!   r   z#VectorRangeQuery.search_window_size  r  r"   c                     | j         S r  r  r.   s    r!   r   z#VectorRangeQuery.use_search_history  r  r"   c                     | j         S r	  r
  r.   s    r!   r   z'VectorRangeQuery.search_buffer_capacity  r  r"   c                 ,    | j         r| j         j        ndS r   r   r.   s    r!   r   zVectorRangeQuery.hybrid_policy  r   r"   c                     | j         S r   r   r.   s    r!   r   zVectorRangeQuery.batch_size  r   r"   c                 6   t          | j        t                    r| j        }nt          | j        | j                  }| j        || j        | j        i}| j        ?| j        j	        || j
        <   | j        t          j        k    r| j        | j        || j        <   |S r  )r:   r   r  r   r   r   r  r  r   ra   r  r   r   r   r  r  s      r!   r^   zVectorRangeQuery.params  s     dlE** 	F\FF$T\EEEF v)4+C
 */3/B/HF4+,#|';;;$0040@t,-r"   )NNr   r  NNNNr|   Tr5   NFNNF)'rn   ro   rp   r  r(   rr   r   r  r  r
   r   r   r  r   r   r   ru   rt   r   r  r   r   r   r   r   r   r2   rv   r  r   r   r   r   r   r   r   r   r^   rx   ry   s   @r!   r  r  x  s        $8c888"M3"""....(c((( .2DH$'#',0,004!&*'+$(*/'R Rd5k5()R R  S	*	R
 $E#/?*?$@AR R "R %R %SMR %SMR !)R R R R (#R  !R"  }#R$ SM%R& $('R R R R R Rh( ( ( ( (6(5 ( ( ( ((( ( ( ( (&( ( ( ( (.( ( ( ( (((s ( ( ( ((( ( ( ( (&%CS %C %C %C %CN (E ( ( ( X( %    X (HSM ( ( ( X( (HSM ( ( ( X( , , , , X, Jx} J J J XJ  HSM       X  S#X    X    r"   r  c                       e Zd ZdS )
RangeQueryN)rn   ro   rp   r'   r"   r!   r5  r5  )  s        Dr"   r5  c                       e Zd ZdZ	 	 	 	 	 	 	 	 	 	 	 d'd	ed
eeeeef         f         dedeeee	f                  dee
e                  dedededee         dedeeeef                  deeeee         f                  deeeef                  f fdZed             Zd(deeeee         f                  fdZdedefdZdeeeeef         f         deeef         fdZdeeeeef         f         fdZedeeef         fd             Zedeeeeef         f         fd!            Zd"eeeef                  deeef         fd#Zd"eeef         fd$Zedeeef         fd%            Zdefd&Z xZS ))	TextQueryav  
    TextQuery is a query for running a full text search, along with an optional filter expression.

    .. code-block:: python

        from redisvl.query import TextQuery
        from redisvl.index import SearchIndex

        index = SearchIndex.from_yaml("index.yaml")

        query = TextQuery(
            text="example text",
            text_field_name="text_field",
            text_scorer="BM25STD",
            filter_expression=None,
            num_results=10,
            return_fields=["field1", "field2"],
            stopwords="english",
            dialect=2,
        )

        results = index.query(query)
    BM25STDNr|   Tr5   Fenglishtexttext_field_nametext_scorerrV   ri   r}   r   r~   rS   r   r^   	stopwordstext_weightsc                 j   || _         |                     |          | _        |                     |          | _        || _        |                     |           |                     |           |r|| _        t                      
                    d           d| _        |                     |           |r
 | j        |  |                     d| j                                      |           |	r|                     |	           |
r|                                  |r|                                  dS dS )a  A query for running a full text search, along with an optional filter expression.

        Args:
            text (str): The text string to perform the text search with.
            text_field_name (Union[str, Dict[str, float]]): The name of the document field to perform
                text search on, or a dictionary mapping field names to their weights.
            text_scorer (str, optional): The text scoring algorithm to use.
                Defaults to BM25STD. Options are {TFIDF, BM25STD, BM25, TFIDF.DOCNORM, DISMAX, DOCSCORE}.
                See https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/scoring/
            filter_expression (Union[str, FilterExpression], optional): A filter to apply
                along with the text search. Defaults to None.
            return_fields (List[str]): The declared fields to return with search
                results.
            num_results (int, optional): The top k results to return from the
                search. Defaults to 10.
            return_score (bool, optional): Whether to return the text score.
                Defaults to True.
            dialect (int, optional): The RediSearch query dialect.
                Defaults to 2.
            sort_by (Optional[SortSpec]): The field(s) to order the results by. Can be:
                - str: single field name
                - Tuple[str, str]: (field_name, "ASC"|"DESC")
                - List: list of fields or tuples
                Note: Only the first field is used for Redis sorting.
                Defaults to None. Results will be ordered by text score.
            in_order (bool): Requires the terms in the field to have
                the same order as the terms in the query filter, regardless of
                the offsets between them. Defaults to False.
            params (Optional[Dict[str, Any]], optional): The parameters for the query.
                Defaults to None.
            stopwords (Optional[Union[str, Set[str]]): The set of stop words to remove
                from the query text (client-side filtering). If a language like 'english' or 'spanish' is provided
                a default set of stopwords for that language will be used. Users may specify
                their own stop words by providing a List or Set of words. if set to None,
                then no words will be removed. Defaults to 'english'.

                Note: This parameter controls query-time stopword filtering (client-side).
                For index-level stopwords configuration (server-side), see IndexInfo.stopwords.
                Using query-time stopwords with index-level STOPWORDS 0 is counterproductive.
            text_weights (Optional[Dict[str, float]]): The importance weighting of individual words
                within the query text. Defaults to None, as no modifications will be made to the
                text_scorer score.
        Raises:
            ValueError: if stopwords language string cannot be loaded.
            TypeError: If stopwords is not a valid iterable set of strings.
        r   Nr   )_text_parse_field_weights_field_weights_parse_text_weights_text_weightsr   _set_stopwordsrX   r   r   r   r   scorerri   r   r~   rS   r   with_scores)r   r:  r;  r<  rV   ri   r}   r   r~   rS   r   r^   r=  r>  r    s                 r!   r   zTextQuery.__init__G  sC   | 
"77HH!55lCC'I&&&)*** 	"!DL 	#'  	K    	/D..At())11'::: 	"LL!!! 	MMOOO 		 	r"   c                     | j         S rm   
_stopwordsr.   s    r!   r=  zTextQuery.stopwords  s
    r"   c                    |st                      | _        dS t          |t                    r	 	 t          t                              |                    | _        dS # t          $ rK t                              dd           t          t                              |                    | _        Y nw xY wdS # t          $ r t          d| d          t          $ r}t          d| d|           d}~ww xY wt          |t          t          t          f          r/t          d	 |D                       rt          |          | _        dS t!          d
          )aA  Set the stopwords to use in the query.
        Args:
            stopwords (Optional[Union[str, Set[str]]]): The stopwords to use. If a string
                such as "english" "german" is provided then a default set of stopwords for that
                language will be used. if a list, set, or tuple of strings is provided then those
                will be used as stopwords. Defaults to "english". if set to "None" then no stopwords
                will be removed.
        Raises:
            TypeError: If the stopwords are not a set, list, or tuple of strings.
        r=  T)quietzLoading stopwords for z failed: nltk is not installed.zError trying to load z from nltk. Nc              3   @   K   | ]}t          |t                    V  d S rm   )r:   r(   )r)   words     r!   	<genexpr>z+TextQuery._set_stopwords.<locals>.<genexpr>  s=       ?
 ?
&*JtS!!?
 ?
 ?
 ?
 ?
 ?
r"   z2stopwords must be a set, list, or tuple of strings)r   rJ  r:   r(   nltk_stopwordswordsLookupErrorr   downloadImportErrorr>   	Exceptionr   r   r	   allr?   )r   r=  es      r!   rE  zTextQuery._set_stopwords  s     	R!eeDOOO	3'' 	RUK&).*>*>y*I*I&J&JDOOO" K K KMM+TM:::&).*>*>y*I*I&J&JDOOOK#O    WYWWW    U U U !S!S!SPQ!S!STTTU	Cu#566 	R3 ?
 ?
.7?
 ?
 ?
 <
 <
 	R ")nnDOOOPQQQs0   ,A AB2/B7 1B22B7 7&C7C22C7
user_queryr#   c                     t                      fd|                                D             } fd|D             }t          |          D ]$\  }}| j        v r| d j        |          d||<   %d                    |          S )aN  Convert a raw user query to a redis full text query joined by ORs
        Args:
            user_query (str): The user query to tokenize and escape.

        Returns:
            str: The tokenized and escaped query string.
        Raises:
            ValueError: If the text string becomes empty after stopwords are removed.
        c                     g | ]v}                     |                                                    d                               dd                              dd                                                    wS ),u   “ u   ”)escapestripreplacelower)r)   tokenescapers     r!   r+   z8TextQuery._tokenize_and_escape_query.<locals>.<listcomp>  s~     
 
 
  NN##C((00;;CCE2NNTTVV 
 
 
r"   c                 *    g | ]}|r|j         v|S r'   rI  )r)   ra  r   s     r!   r+   z8TextQuery._tokenize_and_escape_query.<locals>.<listcomp>  s4     
 
 

383O3OE3O3O3Or"   z=>{$weight:r&   | )r   split	enumeraterD  r,   )r   rX  tokens
token_listira  rb  s   `     @r!   _tokenize_and_escape_queryz$TextQuery._tokenize_and_escape_query  s     ..
 
 
 
 $))++	
 
 

 
 
 
%
 
 

 "*-- 	T 	THAu***#( S Sd6H6O S S S
1zz*%%%r"   
field_specc           	         t          |t                    r|diS t          |t                    r|                                D ]\  }}t          |t                    st	          dt          |                     t          |t          t          f          s"t	          d| dt          |                     |dk    rt          d| d|           |S t	          d          )zParse the field specification into a weights dictionary.

        Args:
            field_spec: Either a single field name or dictionary of field:weight mappings

        Returns:
            Dictionary mapping field names to their weights
              ?r6   zWeight for field 'z' must be numeric, got r   z' must be positive, got zGtext_field_name must be a string or dictionary of field:weight mappings)	r:   r(   dictitemsr?   r@   r   r   r>   )r   rk  rF   weights       r!   rA  zTextQuery._parse_field_weights  s    j#&& 	$$
D)) 	!+!1!1!3!3 
 
v!%-- W#$UU$U$UVVV!&3,77 #YUYY4PV<<YY   Q;;$TUTTFTT    Y  r"   field_weightsc                 H    |                      |          | _        d| _        dS )zSet or update the field weights for the query.

        Args:
            field_weights: Either a single field name or dictionary of field:weight mappings
        N)rA  rB  r   )r   rq  s     r!   set_field_weightszTextQuery.set_field_weights  s(     #77FF#'   r"   c                 4    | j                                         S )z{Get the field weights for the query.

        Returns:
            Dictionary mapping field names to their weights
        )rB  copyr.   s    r!   rq  zTextQuery.field_weights  s     "'')))r"   c                     t          | j                  dk    r>t          t          | j                                                            \  }}|dk    r|S | j                                        S )zGet the text field name(s) - for backward compatibility.

        Returns:
            Either a single field name string (if only one field with weight 1.0)
            or a dictionary of field:weight mappings.
        rM   rm  )r=   rB  nextiterro  ru  )r   rF   rp  s      r!   r;  zTextQuery.text_field_name  se     t"##q(( d&9&?&?&A&A!B!BCCME6}}"'')))r"   weightsc                 `   i }|s|S |                                 D ]\  }}|                                                                }|rd|v rt          d| d| d          t	          |t
                    st	          |t                    r|dk     rt          d| d| d          |||<   |S )Nr%   z-Only individual words may be weighted. Got { : }g        z'Weights must be positive number. Got { )ro  r^  r`  r>   r:   r   r   )r   ry  parsed_weightsrN  rp  s        r!   rC  zTextQuery._parse_text_weights(  s     ,. 	"!!#MMOO 	* 	*LD&::<<%%''D 3$;; WTWWFWWW    ..2<VS2I2IC<< QtQQfQQQ   $*N4  r"   c                 H    |                      |          | _        d| _        dS )zSet or update the text weights for the query.

        Args:
            text_weights: Dictionary of word:weight mappings
        N)rC  rD  r   )r   ry  s     r!   set_text_weightszTextQuery.set_text_weights>  s(     "55g>>#'   r"   c                     | j         S )z`Get the text weights.

        Returns:
            Dictionary of word:weight mappings.
        )rD  r.   s    r!   r>  zTextQuery.text_weightsG  s     !!r"   c           
         | j         }t          |t                    rt          |          }|                     | j                  }g }| j                                        D ]G\  }}|dk    r|                    d| d| d           (|                    d| d| d| d           Ht          |          dk    r	|d         }nd	d

                    |          z   dz   }|r|dk    r|d| z  }|S )zDBuild the full query string for text search with optional filtering.rm  r%  z:(r(  z) => { $weight: r|  rM   r   r'  rd  r   z AND )r   r:   r   r(   rj  r@  rB  ro  r;   r=   r,   )r   rV   escaped_queryfield_queriesrF   rp  r:  s          r!   r2   zTextQuery._build_query_stringP  sE    3')9:: 	7 #$5 6 677
CC !06688 	 	ME6}}$$%B%B%B-%B%B%BCCCC $$LLLLLLLL   
 }"" #DDM222S8D 	0!2c!9!9/-///Dr"   )r8  NNr|   Tr5   NFNr9  N)r9  )rn   ro   rp   rq   r(   r
   r   r   r   r   r   r   ru   rt   r   r   r   rv   r=  rE  rj  rA  rs  rq  r;  rC  r  r>  r2   rx   ry   s   @r!   r7  r7  .  s[        8 %DH-1!&*+/4=37[ [[ sDe$445[ 	[
 $E#/?*?$@A[  S	*[ [ [ [ (#[ [ c3h([ E#s3x-01[ tCJ/0[ [ [ [ [ [z   X"R "RsCH}1E(F "R "R "R "RH&S &S & & & &6T#u*%5 56	c5j	   @(uS$sEz:J5J/K ( ( ( ( *tCJ/ * * * X* *sDe,<'<!= * * * X*S%Z 01	c5j	   ,(S%Z(8 ( ( ( ( "d3:. " " " X"S        r"   r7  N)'enumr   typingr   r   r   r   r   r	   r
   redis.commands.search.queryr   
RedisQueryredisvl.query.filterr   redisvl.redis.utilsr   redisvl.utils.logr   redisvl.utils.token_escaperr   redisvl.utils.utilsr   r   rn   rQ   r   rP  r(   rt   r   r{   r   r   r   r   r  r5  r7  r'   r"   r!   <module>r     s         ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ; ; ; ; ; ; 1 1 1 1 1 1 / / / / / / ( ( ( ( ( ( 4 4 4 4 4 4 C C C C C C C C	H		{6455 eCHotE#uS#X2F,G'HHI^ ^ ^ ^ ^
 ^ ^ ^B	:' :' :' :' :') :' :' :'z+' +' +' +' +' +' +' +'\- - - - - - - -(    3   u u u u u/9 u u upn n n n n	 n n nb	 	 	 	 	! 	 	 	
~ ~ ~ ~ ~	 ~ ~ ~ ~ ~r"   