
    mi                        d Z ddlmZ ddlZddlZddlmZ ddlmZ	 ddl
mZ  ee          ZdZdZdZd	Zd#dZd$dZd%dZd&dZd'dZd(d!Zd)d"ZdS )*uI  Redis-based caching for embedding vectors and search results.

Design
------
Embedding cache
    Key:  ``emb:{sha256(text)}``
    Value: JSON-serialised ``list[float]``
    TTL:  1 hour  (embeddings are deterministic and model-stable)

Search result cache
    Key:  ``search:{user_id}:{sha256(query+filters+page)}``
    Value: JSON-serialised search result dict
    TTL:  2 minutes  (short enough that permission updates are visible quickly;
          permissions are append-only so cached results are always a safe subset)

基于 Redis 的查询缓存模块。
提供两层缓存：
1. 向量缓存 —— 相同文本的 embedding 结果缓存 1 小时，避免重复调用向量化 API
2. 搜索结果缓存 —— 相同用户+查询+筛选条件的搜索结果缓存 2 分钟，
   TTL 足够短以确保权限变更后能及时生效
    )annotationsN)Any)
get_loggeri  x   zemb:zsearch:textstrreturnc                    t          j        |                     d                                                    d d         S )Nzutf-8    )hashlibsha256encode	hexdigest)r   s    /D:\work\zm-rag\backend\app\utils\query_cache.py_sha256r   )   s3    >$++g..//99;;CRC@@    redisaioredis.Redislist[float] | Nonec                   K   	 t            t          |           }|                     |           d{V }|rt          j        |          S n@# t
          $ r3}t                              dt          |                     Y d}~nd}~ww xY wdS )z<Return a cached embedding vector, or ``None`` on cache miss.Nembedding_cache_get_errorerror)	_EMBEDDING_PREFIXr   getjsonloads	Exceptionloggerwarningr   )r   r   keyrawexcs        r   get_cached_embeddingr$   2   s      D"3GDMM33IIcNN"""""" 	#:c??"	# D D D2#c((CCCCCCCCD4s   AA 
B)BBvectorlist[float]Nonec                &  K   	 t            t          |           }|                     |t          t	          j        |                     d{V  dS # t          $ r4}t                              dt          |                     Y d}~dS d}~ww xY w)z#Store an embedding vector in Redis.Nembedding_cache_set_errorr   )
r   r   setex_EMBEDDING_TTLr   dumpsr   r   r    r   )r   r   r%   r!   r#   s        r   set_cached_embeddingr-   >   s      D"3GDMM33kk#~tz&/A/ABBBBBBBBBBB D D D2#c((CCCCCCCCCDs   AA 
B)BBuser_idqueryfiltersdictpageintc                n    t          j        |||ddd          }t           |  dt          |           S )N)qfpTF)	sort_keysensure_ascii:)r   r,   _SEARCH_PREFIXr   )r.   r/   r0   r2   fingerprints        r   _search_cache_keyr=   N   sL    *'--E  K >g>>(<(<>>>r   dict[str, Any] | Nonec                ^  K   	 t          ||||          }|                     |           d{V }|r9t                              d||dd                    t	          j        |          S n@# t          $ r3}t                              dt          |                     Y d}~nd}~ww xY wdS )z8Return a cached search result dict, or ``None`` on miss.Nsearch_cache_hit(   r.   r/   search_cache_get_errorr   )	r=   r   r   debugr   r   r   r    r   )r   r.   r/   r0   r2   r!   r"   r#   s           r   get_cached_searchrE   U   s      A>>IIcNN"""""" 	#LL+WE#2#JLOOO:c??"	#  A A A/s3xx@@@@@@@@A4s   A'A- -
B*7)B%%B*resultdict[str, Any]c                h  K   	 t          ||||          }|                     |t          t          j        |d                     d{V  t
                              d||dd                    dS # t          $ r4}t
                              dt          |                     Y d}~dS d}~ww xY w)	z$Store a search result dict in Redis.F)r9   Nsearch_cache_setrA   rB   search_cache_set_errorr   )
r=   r*   _SEARCH_TTLr   r,   r   rD   r   r    r   )r   r.   r/   r0   r2   rF   r!   r#   s           r   set_cached_searchrL   d   s      A>>kk#{DJvE,R,R,RSSSSSSSSS'crc
KKKKK A A A/s3xx@@@@@@@@@As   A-A3 3
B1=)B,,B1c                  K   	 t            | d}d}d}	 |                     ||d           d{V \  }}|r" | j        |  d{V  |t          |          z  }|dk    rnM|rt                              d||           dS dS # t          $ r4}t                              d	t          |          
           Y d}~dS d}~ww xY w)u   Delete all cached search results for a user (call after permission update).

    权限变更后清除该用户的所有搜索缓存。
    z:*r   Td   )matchcountNsearch_cache_invalidated)r.   keyssearch_cache_invalidate_errorr   )	r;   scanunlinklenr   infor   r    r   )r   r.   patterncursordeletedrR   r#   s          r   invalidate_search_cacher[   u   s/     
H#0W000		!&F'!M!MMMMMMMLFD % #elD))))))))3t99${{		  	SKK2G'KRRRRR	S 	S H H H6c#hhGGGGGGGGGHs   A=B 
C)B>>C)r   r   r	   r   )r   r   r   r   r	   r   )r   r   r   r   r%   r&   r	   r'   )
r.   r   r/   r   r0   r1   r2   r3   r	   r   )r   r   r.   r   r/   r   r0   r1   r2   r3   r	   r>   )r   r   r.   r   r/   r   r0   r1   r2   r3   rF   rG   r	   r'   )r   r   r.   r   r	   r'   )__doc__
__future__r   r   r   typingr   redis.asyncioasyncioaioredisapp.utils.loggerr   __name__r   r+   rK   r   r;   r   r$   r-   r=   rE   rL   r[    r   r   <module>re      s7   , # " " " " "                     ' ' ' ' ' '	H		 A A A A	 	 	 	D D D D ? ? ? ?   A A A A"H H H H H Hr   