o
    Ղi[0                     @   sx   U d Z ddlmZmZmZmZ ddlZddlmZ ddl	m
Z
 eeZG dd dZdaee ed< d	efd
dZdS )zC
Graph Query Service
Provides graph traversal and query operations
    )ListDictAnyOptionalN)Neo4jClient)get_graph_cachec                   @   sP  e Zd ZdZdefddZ			d$dee d	ed
ededeee	  dedee
e	ef  fddZded	ed
edee
e	ef  fddZ	d%de	d	ed
ededee
e	ef  f
ddZ	d%de	d	ed
ededee
e	ef  f
ddZ		d&de	d	ed
edee	 dedee
e	ef  fddZ	d'ded ed	ed
ed!edee
e	ef  fd"d#ZdS )(GraphQueryServicez+
    Graph query and traversal service
    neo4j_clientc                 C   s   || _ t | _d S )N)r	   r   cache)selfr	    r   F/lsinfo/ai/hellotax_ai/base_platform/app/services/graph/graph_query.py__init__   s   zGraphQueryService.__init__   N   document_ids	tenant_idkb_iddepthrelation_typeslimitreturnc              
   C   s  |sg S | j jdt||||||d}| j |}|dur|S |du r'ddg}d|}	|dkr7d|	 d	}
nd
|	 d}
z'| jj|
||||dd}tdt	| dt	| d | j 
|| |W S  ty } ztd|  g W  Y d}~S d}~ww )a  
        Expand neighbors from seed documents

        Args:
            document_ids: Seed document IDs
            tenant_id: Tenant ID
            kb_id: Knowledge base ID
            depth: Expansion depth (1 or 2)
            relation_types: Relation types to follow (default: REFERENCES, SIMILAR_TO)
            limit: Maximum neighbors per document

        Returns:
            List of neighbor documents with metadata
        	neighbors)doc_idsr   r   r   r   r   N
REFERENCES
SIMILAR_TO|r   z
            MATCH (d:Document)
            WHERE d.id IN $doc_ids
              AND d.tenant_id = $tenant_id
              AND d.kb_id = $kb_id
            MATCH (d)-[r:aQ  ]-(neighbor:Document)
            WHERE neighbor.tenant_id = $tenant_id
              AND neighbor.kb_id = $kb_id
              AND NOT (neighbor.id IN $doc_ids)
              AND (neighbor.doc_status IS NULL OR NOT (neighbor.doc_status IN ['obsolete', 'expired']))
            RETURN DISTINCT
                neighbor.id as id,
                neighbor.title as title,
                neighbor.summary as summary,
                type(r) as relation_type,
                COALESCE(r.similarity, r.confidence, 0.5) as score
            ORDER BY score DESC
            LIMIT $limit
            z
            MATCH (d:Document)
            WHERE d.id IN $doc_ids
              AND d.tenant_id = $tenant_id
              AND d.kb_id = $kb_id
            MATCH path = (d)-[:as  *1..2]-(neighbor:Document)
            WHERE neighbor.tenant_id = $tenant_id
              AND neighbor.kb_id = $kb_id
              AND NOT (neighbor.id IN $doc_ids)
              AND (neighbor.doc_status IS NULL OR NOT (neighbor.doc_status IN ['obsolete', 'expired']))
            WITH DISTINCT neighbor, length(path) as distance
            RETURN
                neighbor.id as id,
                neighbor.title as title,
                neighbor.summary as summary,
                'multi_hop' as relation_type,
                1.0 / distance as score
            ORDER BY score DESC
            LIMIT $limit
            )r   r   r   r   
parametersz	Expanded z neighbors from z seed documentszFailed to expand neighbors: )r
   _generate_keysortedgetjoinr	   execute_queryloggerinfolenset	Exceptionerror)r   r   r   r   r   r   r   	cache_keycached_resultrel_patternqueryresultser   r   r   expand_neighbors   sR   	

 	z"GraphQueryService.expand_neighborsdocument_idc           	   
   C   s   | j jd|||d}| j |}|dur|S d}z| jj||||dd}| j || |W S  tyJ } ztd|  g W  Y d}~S d}~ww )z
        Get entities contained in a document

        Args:
            document_id: Document ID
            tenant_id: Tenant ID
            kb_id: Knowledge base ID

        Returns:
            List of entities
        doc_entities)doc_idr   r   Na8  
        MATCH (d:Document {id: $doc_id, tenant_id: $tenant_id, kb_id: $kb_id})
        MATCH (d)-[r:CONTAINS]->(e:Entity)
        RETURN
            e.name as name,
            e.type as type,
            e.description as description,
            r.salience as salience
        ORDER BY r.salience DESC
        r   z!Failed to get document entities: )	r
   r   r!   r	   r#   r'   r(   r$   r)   )	r   r1   r   r   r*   r+   r-   r.   r/   r   r   r   get_document_entities   s2   
z'GraphQueryService.get_document_entities
   entity_namec              
   C   ^   d}z| j j|||||dd}|W S  ty. } ztd|  g W  Y d}~S d}~ww )a
  
        Find documents containing a specific entity

        Args:
            entity_name: Entity name
            tenant_id: Tenant ID
            kb_id: Knowledge base ID
            limit: Maximum results

        Returns:
            List of documents
        a  
        MATCH (e:Entity {name: $entity_name, tenant_id: $tenant_id, kb_id: $kb_id})
        MATCH (e)<-[r:CONTAINS]-(d:Document)
        WHERE d.tenant_id = $tenant_id
          AND d.kb_id = $kb_id
        RETURN
            d.id as id,
            d.title as title,
            d.summary as summary,
            r.salience as salience
        ORDER BY r.salience DESC
        LIMIT $limit
        r6   r   r   r   r   z!Failed to find entity documents: Nr	   r#   r(   r$   r)   r   r6   r   r   r   r-   r.   r/   r   r   r   find_entity_documents   s    	z'GraphQueryService.find_entity_documentsc              
   C   r7   )a  
        Get entities related to a specific entity

        Args:
            entity_name: Entity name
            tenant_id: Tenant ID
            kb_id: Knowledge base ID
            limit: Maximum results

        Returns:
            List of related entities
        a  
        MATCH (e1:Entity {name: $entity_name, tenant_id: $tenant_id, kb_id: $kb_id})
        MATCH (e1)-[r:RELATED_TO|CO_OCCURS_WITH]-(e2:Entity)
        WHERE e2.tenant_id = $tenant_id
          AND e2.kb_id = $kb_id
        RETURN
            e2.name as name,
            e2.type as type,
            e2.description as description,
            type(r) as relation_type,
            COALESCE(r.confidence, r.count, 0.5) as score
        ORDER BY score DESC
        LIMIT $limit
        r8   r   z Failed to get related entities: Nr9   r:   r   r   r   get_related_entities   s    	z&GraphQueryService.get_related_entities
query_textentity_typec              
   C   s   | j jd|||||d}| j |}|dur|S g d}|r#|d d|}	d|	 d}
||||d	}|r;||d
< z| jj|
|d}| j || |W S  tyi } zt	
d|  g W  Y d}~S d}~ww )a3  
        Search entities by name

        Args:
            query_text: Search query
            tenant_id: Tenant ID
            kb_id: Knowledge base ID
            entity_type: Optional entity type filter
            limit: Maximum results

        Returns:
            List of matching entities
        search_entities)r-   r   r   r>   r   N)ze.tenant_id = $tenant_idze.kb_id = $kb_idz(toLower(e.name) CONTAINS toLower($query)ze.type = $entity_typez AND z(
        MATCH (e:Entity)
        WHERE z
        RETURN
            e.name as name,
            e.type as type,
            e.description as description
        LIMIT $limit
        )r-   r   r   r   r>   r   zFailed to search entities: )r
   r   r!   appendr"   r	   r#   r'   r(   r$   r)   )r   r=   r   r   r>   r   r*   r+   where_clauseswhere_clauser-   paramsr.   r/   r   r   r   r?     sD   

z!GraphQueryService.search_entities   source_doc_idtarget_doc_id	max_depthc           	   
   C   sv   d}z| j j|dt|||||dd}|r|d W S dW S  ty: } ztd|  W Y d}~dS d}~ww )aL  
        Find shortest path between two documents

        Args:
            source_doc_id: Source document ID
            target_doc_id: Target document ID
            tenant_id: Tenant ID
            kb_id: Knowledge base ID
            max_depth: Maximum path depth

        Returns:
            Path information or None
        a  
        MATCH (d1:Document {id: $source_id, tenant_id: $tenant_id, kb_id: $kb_id})
        MATCH (d2:Document {id: $target_id, tenant_id: $tenant_id, kb_id: $kb_id})
        MATCH path = shortestPath((d1)-[*..{max_depth}]-(d2))
        RETURN
            [node in nodes(path) | node.id] as node_ids,
            [rel in relationships(path) | type(rel)] as relation_types,
            length(path) as path_length
        z{max_depth})	source_id	target_idr   r   r   r   NzFailed to find shortest path: )r	   r#   replacestrr(   r$   r)   )	r   rE   rF   r   r   rG   r-   r.   r/   r   r   r   get_shortest_pathh  s    
	z#GraphQueryService.get_shortest_path)r   Nr   )r5   )Nr   )rD   )__name__
__module____qualname____doc__r   r   r   intr   rK   r   r   r0   r4   r;   r<   r?   rL   r   r   r   r   r      s    	

k
>
5
6
Rr   graph_query_servicer   c                  C   s    t du rddlm}  t| a t S )z*Get or create graph query service instanceNr   r	   )rR   app.services.graph.neo4j_clientr	   r   rS   r   r   r   get_graph_query_service  s   rU   )rP   typingr   r   r   r   loggingrT   r   app.services.graph.graph_cacher   	getLoggerrM   r$   r   rR   __annotations__rU   r   r   r   r   <module>   s    
   