
    Xj!;                        d dl Z d dlmZ d dl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  ee          Z G d d	          Zdad
efdZdZ	  e            ZdS # e$ r#Ze                    de            Y dZ[dS dZ[ww xY w)    N)contextmanager)Any)DriverGraphDatabaseSession)settings)ExternalServiceError)
get_loggerc                   >   e Zd Zd Zd Zedefd            Z	 	 d)dede	ee
f         dz  dedz  dee	ee
f                  fd	Z	 	 d)dede	ee
f         dz  dedz  dee	ee
f                  fd
Zd ZdefdZdededefdZ	 	 d)dedededededededz  dedededz  dedz  fdZdedededz  dededefdZ	 	 d*dededed ed!edee	ee
f                  fd"Z	 d+dededed!edee	ee
f                  f
d#Zd$ed%eddfd&Zd,dededz  de	eef         fd'Zd( ZdS )-Neo4jClientc                 <    d | _         |                                  d S N)driver_initialize_driverselfs    G/lsinfo/ai/hellotax_ai/base_platform/app/services/graph/neo4j_client.py__init__zNeo4jClient.__init__   s!    %)!!!!!    c           
         t           j        st                              d           d S 	 t	          j        t           j        t           j        t           j        ft           j	        ddd          | _        | j        
                                 t                              dt           j                    d S # t          $ r}t                              d|            t           j        dk    r t                              d	           Y d }~d S t          d
dt           j         dt!          |                     |d }~ww xY w)Nz:Knowledge graph is disabled, skipping Neo4j initializationi  2   <   )authdatabasemax_connection_lifetimemax_connection_pool_sizeconnection_acquisition_timeoutz'Neo4j driver initialized successfully: z#Failed to initialize Neo4j driver: testzMNeo4j connection failed in test environment, continuing without graph supportNeo4jzFailed to connect to Neo4j at : )r   ENABLE_KNOWLEDGE_GRAPHloggerinfor   r   	NEO4J_URI
NEO4J_USERNEO4J_PASSWORDNEO4J_DATABASEverify_connectivity	ExceptionerrorENVIRONMENTwarningr	   str)r   es     r   r   zNeo4jClient._initialize_driver   sH   . 	KKTUUUF	'.")8+BC!0(,)+/1  DK K++---KKV(BTVVWWWWW 		 		 		LLBqBBCCC#v--c   &X(:LXXPSTUPVPVXX 		s   B	B5 5
D??AD:.D::D?returnc              #      K   | j         st          d          | j                                         }	 |V  |                                 d S # |                                 w xY w)NzHNeo4j driver not initialized. Enable ENABLE_KNOWLEDGE_GRAPH in settings.)r   RuntimeErrorsessionclose)r   r2   s     r   get_sessionzNeo4jClient.get_session.   sk      { 	Z   +%%''	MMMMMOOOOOGMMOOOOs   A A#Nquery
parameters	tenant_idc                    | j         st          d          |pi }||nd|d<   t          j                    }	 |                                 5 }|                    ||          }d |D             }t          j                    |z
  }	t
                              t          |	dz  d          t          |                    	                    d           |cd d d            S # 1 swxY w Y   d S # t          $ r^}
t          j                    |z
  }	t
                              t          |	dz  d          	                              d
|
             d }
~
ww xY w)NNeo4j driver not initializedr   r7   c                 ,    g | ]}t          |          S  dict.0records     r   
<listcomp>z-Neo4jClient.execute_query.<locals>.<listcomp>H   s    ===F4<<===r        )duration_msresult_countzNeo4j query executedrD   zQuery execution failed: )r   r1   timer4   runr"   bindroundlenr#   r)   r*   )r   r5   r6   r7   paramsstartr2   resultrecordselapsedr.   s              r   execute_queryzNeo4jClient.execute_query:   s    { 	?=>>>!r+4+@iia{		!!## w UF33==f===)++-gna(@(@sSZ||\\aa*                      	 	 	ikkE)GKKE'D.!$<$<K==CC.1..   	s=   C- BC C-  C$$C- 'C$(C- -
E7AEEc                   
 | j         st          d          |pi 
||nd
d<   
fd}t          j                    }	 |                                 5 }|                    |          }t          j                    |z
  }t
                              t          |dz  d                                        d           |cd d d            S # 1 swxY w Y   d S # t          $ r^}	t          j                    |z
  }t
                              t          |dz  d                    
                    d	|	             d }	~	ww xY w)
Nr9   r   r7   c                 H    |                                }d |D             S )Nc                 ,    g | ]}t          |          S r;   r<   r>   s     r   rA   z@Neo4jClient.execute_write.<locals>._write_tx.<locals>.<listcomp>b   s    666VDLL666r   )rH   )txrN   rL   r5   s     r   	_write_txz,Neo4jClient.execute_write.<locals>._write_tx`   s*    VVE6**F66v6666r   rB   rC   rF   z Neo4j write transaction executedzWrite transaction failed: )r   r1   rG   r4   execute_writer"   rI   rJ   r#   r)   r*   )r   r5   r6   r7   rV   rM   r2   rO   rP   r.   rL   s    `        @r   rW   zNeo4jClient.execute_writeU   s    { 	?=>>>!r+4+@iia{	7 	7 	7 	7 	7 	7 		!!## w!//	::)++-gna(@(@AAFF6                      	 	 	ikkE)GKKE'D.!$<$<K==CC0Q00   	s>    C A,C C CC CC 
E$AD==Ec                    | j         st                              d           d S g d}	 |                                 5 }|D ]}|                    |           t                              d           d d d            d S # 1 swxY w Y   d S # t          $ r(}t                              d|            Y d }~d S d }~ww xY w)Nz5Neo4j driver not initialized, skipping index creation)zXCREATE INDEX document_tenant_kb IF NOT EXISTS FOR (d:Document) ON (d.tenant_id, d.kb_id)zACREATE INDEX document_id IF NOT EXISTS FOR (d:Document) ON (d.id)zTCREATE INDEX entity_tenant_kb IF NOT EXISTS FOR (e:Entity) ON (e.tenant_id, e.kb_id)zACREATE INDEX entity_name IF NOT EXISTS FOR (e:Entity) ON (e.name)zACREATE INDEX entity_type IF NOT EXISTS FOR (e:Entity) ON (e.type)zNCREATE INDEX tag_tenant_kb IF NOT EXISTS FOR (t:Tag) ON (t.tenant_id, t.kb_id)zXCREATE INDEX category_tenant_kb IF NOT EXISTS FOR (c:Category) ON (c.tenant_id, c.kb_id)zDCREATE INDEX user_tenant IF NOT EXISTS FOR (u:User) ON (u.tenant_id)zRCREATE INDEX chunk_tenant_kb IF NOT EXISTS FOR (c:Chunk) ON (c.tenant_id, c.kb_id)zACREATE INDEX chunk_id IF NOT EXISTS FOR (c:Chunk) ON (c.chunk_id)zKCREATE INDEX chunk_doc_number IF NOT EXISTS FOR (c:Chunk) ON (c.doc_number)zGCREATE INDEX chunk_level IF NOT EXISTS FOR (c:Chunk) ON (c.chunk_level)z"Neo4j indexes created successfullyzFailed to create indexes: )r   r"   r,   r4   rH   r#   r)   r*   )r   indexesr2   index_queryr.   s        r   create_indexeszNeo4jClient.create_indexest   sE   { 	NNRSSSF
 
 
	;!!## Bw#* - -KKK,,,,@AAAB B B B B B B B B B B B B B B B B B  	; 	; 	;LL9a99:::::::::	;s:   B 5A?2B ?BB BB 
B>B99B>c                 r    d}|                      ||           t                              d|            d S )NzZ
        MATCH (n)
        WHERE n.tenant_id = $tenant_id
        DETACH DELETE n
        )r7   zCleared graph data for tenant )rW   r"   r#   )r   r7   r5   s      r   clear_tenant_datazNeo4jClient.clear_tenant_data   sA    p5I666@Y@@AAAAAr   kb_idc                 ,   d}	 |                      |d|i|          }|r|d                             dd          nd}t                              d| d| d|            |S # t          $ r&}t                              d	| d
|             d }~ww xY w)Nz
        MATCH (n)
        WHERE n.tenant_id = $tenant_id AND n.kb_id = $kb_id
        WITH count(n) AS deleted_count, collect(n) AS nodes
        FOREACH (n IN nodes | DETACH DELETE n)
        RETURN deleted_count
        r^   r6   r7   r   deleted_countzDeleted z Neo4j nodes for kb_id=z, tenant_id=z,Failed to delete Neo4j graph data for kb_id=r    )rW   getr"   r#   r)   r*   )r   r^   r7   r5   resultsra   r.   s          r   delete_kb_datazNeo4jClient.delete_kb_data   s     x		((GU;KW`(aaGBIPGAJNN?A>>>qMKK_=____T]__   !  	 	 	LLTTTQRTTUUU	s   AA# #
B-!BBchunk_iddocument_id
chunk_textchunk_levelchunk_index	is_parentparent_chunk_iddoc_type
doc_numberc                 `    d}|                      ||||d d         |||||
||	d
|           d S )NaM  
        MERGE (c:Chunk {chunk_id: $chunk_id, tenant_id: $tenant_id, kb_id: $kb_id})
        SET c.document_id = $document_id,
            c.text = $chunk_text,
            c.chunk_level = $chunk_level,
            c.chunk_index = $chunk_index,
            c.is_parent = $is_parent,
            c.parent_chunk_id = $parent_chunk_id,
            c.doc_type = $doc_type,
            c.doc_number = $doc_number,
            c.updated_at = datetime()
        WITH c
        MATCH (d:Document {id: $document_id, tenant_id: $tenant_id, kb_id: $kb_id})
        MERGE (d)-[:CONTAINS]->(c)
        i  )
re   rf   rg   rh   ri   rj   rk   rl   rm   r^   r`   rW   )r   re   rf   rg   rh   ri   rj   rk   r7   r^   rl   rm   r5   s                r   create_chunk_nodezNeo4jClient.create_chunk_node   sj     n	$*(#.**&#2$(    	 	
 	
 	
 	
 	
r   source_chunk_idtarget_doc_numbertarget_article
confidencec           	      P    |rd}nd}|                      ||||||d|           d S )Na  
            MATCH (source:Chunk {chunk_id: $source_chunk_id, tenant_id: $tenant_id, kb_id: $kb_id})
            MATCH (target:Chunk {doc_number: $target_doc_number, tenant_id: $tenant_id, kb_id: $kb_id})
            WHERE target.chunk_level = 'article' AND target.text CONTAINS $target_article
            MERGE (source)-[r:REFERENCES]->(target)
            SET r.confidence = $confidence,
                r.target_article = $target_article,
                r.created_at = datetime()
            a  
            MATCH (source:Chunk {chunk_id: $source_chunk_id, tenant_id: $tenant_id, kb_id: $kb_id})
            MATCH (target:Chunk {doc_number: $target_doc_number, tenant_id: $tenant_id, kb_id: $kb_id})
            WHERE target.is_parent = true
            WITH source, target
            LIMIT 1
            MERGE (source)-[r:REFERENCES]->(target)
            SET r.confidence = $confidence,
                r.created_at = datetime()
            )rq   rr   rs   rt   r^   r`   ro   )r   rq   rr   rs   rt   r7   r^   r5   s           r   create_chunk_referencez"Neo4jClient.create_chunk_reference   sg      	a PEE aE#2%6"0(    	 
	
 
	
 
	
 
	
 
	
r            ?	max_depthmin_confidencec                 J    d| d}|                      ||||d|          }|S )Nz
        MATCH path = (source:Chunk {chunk_id: $chunk_id, tenant_id: $tenant_id, kb_id: $kb_id})
                     -[r:REFERENCES*1..a  ]->(target:Chunk)
        WHERE ALL(rel IN relationships(path) WHERE rel.confidence >= $min_confidence)
        RETURN DISTINCT
            target.chunk_id as chunk_id,
            target.document_id as document_id,
            target.text as text,
            target.chunk_level as chunk_level,
            target.chunk_index as chunk_index,
            target.doc_number as doc_number,
            target.doc_type as doc_type,
            target.is_parent as is_parent,
            target.parent_chunk_id as parent_chunk_id,
            [rel IN relationships(path) | rel.confidence] as confidence_path,
            length(path) as depth
        ORDER BY depth, confidence_path[0] DESC
        LIMIT 20
        re   rz   r^   r`   rQ   )r   re   r7   r^   ry   rz   r5   rc   s           r   get_referenced_chunksz!Neo4jClient.get_referenced_chunks   sX     C  `i  C  C  C$$$,Y^__ % 
 

 r   c                 B    d}|                      ||||d|          }|S )Na  
        MATCH (source:Chunk)-[r:REFERENCES]->(target:Chunk {chunk_id: $chunk_id, tenant_id: $tenant_id, kb_id: $kb_id})
        WHERE r.confidence >= $min_confidence
        RETURN
            source.chunk_id as chunk_id,
            source.document_id as document_id,
            source.text as text,
            source.chunk_level as chunk_level,
            source.doc_number as doc_number,
            r.confidence as confidence
        ORDER BY r.confidence DESC
        LIMIT 20
        r|   r`   r}   )r   re   r7   r^   rz   r5   rc   s          r   get_chunks_referencingz"Neo4jClient.get_chunks_referencing   s?     M$$$,Y^__ % 
 

 r   source_doc_idtarget_doc_idc                 >    d}|                      |||d           d S )Nz
        MERGE (s:Document {id: $source_id})
        MERGE (t:Document {id: $target_id})
        MERGE (s)-[r:SUPERSEDES]->(t)
        SET r.created_at = datetime()
        )	source_id	target_id)r6   ro   )r   r   r   r5   s       r   create_supersedes_relationz&Neo4jClient.create_supersedes_relation   s>     EMVV 	 	
 	
 	
 	
 	
r   c                    d}d|i}|
|dz  }||d<   t                               d|            d| d}|                     ||          }t                               d|            t          d	 |D                       d
d
d
d
d
d
d}|D ]u}|                    dd          }	|                    dd
          }
|	dk    r|
|d<   :|	dk    r|
|d<   F|	dk    r|
|d<   R|	dk    r|
|d<   ^|	dk    r|
|d<   j|	dk    r|
|d<   vd|dnd d|dnd d}|                     ||          }t                               d |            |r|d
         d!         d"z  nd
|d#<   t                               d$|            |S )%NzWHERE n.tenant_id = $tenant_idr7   z AND n.kb_id = $kb_idr^   zGetting stats with params: z
        MATCH (n)
        zY
        RETURN
            labels(n)[0] as label,
            count(n) as count
        zNode query results: c              3   &   K   | ]}|d          V  dS )countNr;   )r?   rs     r   	<genexpr>z(Neo4jClient.get_stats.<locals>.<genexpr>  s&      ;;aqz;;;;;;r   r   )total_nodes	documentsentitieschunkstags
categoriesuserslabel r   Documentr   Entityr   Chunkr   Tagr   Categoryr   Userr   z[
        MATCH (n1)-[r]-(n2)
        WHERE COALESCE(n1.tenant_id, -1) = $tenant_id
        z#AND COALESCE(n1.kb_id, -1) = $kb_idz=
        AND COALESCE(n2.tenant_id, -1) = $tenant_id
        z#AND COALESCE(n2.kb_id, -1) = $kb_idz7
        RETURN count(DISTINCT r) as rel_count
        zRelationship query results: 	rel_countrC   total_relationshipszFinal stats: )r"   r#   rQ   sumrb   )r   r7   r^   where_clauserL   r5   rc   statsr@   r   r   	rel_queryrel_resultss                r   	get_statszNeo4jClient.get_stats   sf   7y)33L#F7O:&::;;; \  \  \  \$$UF334744555;;7;;;;;
 
  	' 	'FJJw++EJJw**E
""%*k""(""$)j!!'!!"'h% %f*$$&+l##&!&g z  `e  `q  w\  w\  wy  z  z  ej  ev  |a  |a  |~  z  z  z	((F;;@;@@AAAKV']{1~k'Ba'G'G\]#$+E++,,,r   c                 ~    | j         r5| j                                          t                              d           d S d S )NzNeo4j driver closed)r   r3   r"   r#   r   s    r   r3   zNeo4jClient.close#  sC    ; 	/KKK-.....	/ 	/r   )NN)rw   rx   )rx   r   )__name__
__module____qualname__r   r   r   r   r4   r-   r=   r   intlistrQ   rW   r[   r]   rd   boolrp   floatrv   r~   r   r   r   r3   r;   r   r   r   r      s       " " "  4 	W 	 	 	 ^	 -1 $	  cNT) :	
 
d38n	   < -1 $	  cNT) :	
 
d38n	   >; ; ;4B3 B B B B
C C C    0  $!%
 

 
 	

 
 
 
 t
 
 
 *
 $J
 
 
 
@

 
 d
	

 
 
 
 
 
 
<  #   	
   
d38n	   " RU	 		(+	47	IN		d38n		 	 	 	
 
C 
TX 
 
 
 
' '3 'sTz 'T#s(^ ' ' ' 'R/ / / / /r   r   r/   c                  :    t           t                      a t           S r   )_neo4j_client_instancer   r;   r   r   get_neo4j_clientr   ,  s    %!,!!r   z0Failed to create default Neo4j client instance: )rG   
contextlibr   typingr   neo4jr   r   r   
app.configr   app.core.exceptionsr	   common_loggingr
   r   r"   r   r   r   neo4j_clientr)   r.   r,   r;   r   r   <module>r      sU    % % % % % %       0 0 0 0 0 0 0 0 0 0       4 4 4 4 4 4 % % % % % %	H		X/ X/ X/ X/ X/ X/ X/ X/v  "+ " " " " K;==LLL K K K
NNIaIIJJJJJJJJJKs   
A B!A??B