
    Zǻid                    8   d dl mZ d dlZd dlZd dlmZmZmZ d dlZd dl	m
Z
 d dlmZmZmZmZ ddlmZmZ ddlmZmZmZ  ej.                  e      Z	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd	Z	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 dd
Z	 d	 	 	 	 	 	 	 ddZdej:                  f	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 d	 	 	 	 	 	 	 	 	 	 	 ddZ	 d	 	 	 	 	 	 	 	 	 	 	 ddZ 	 d	 	 	 	 	 	 	 	 	 	 	 ddZ!	 d	 	 	 	 	 	 	 	 	 	 	 ddZ"	 	 	 	 	 	 ddZ#	 d	 	 	 	 	 	 	 	 	 	 	 ddZ$g df	 	 	 	 	 	 	 	 	 	 	 d dZ%y)!    )annotationsN)ListLiteralOptional)ValidationError)UPSERT_VECTOR_ON_NODE_QUERY#UPSERT_VECTOR_ON_RELATIONSHIP_QUERYUPSERT_VECTORS_ON_NODE_QUERY$UPSERT_VECTORS_ON_RELATIONSHIP_QUERY   )Neo4jIndexErrorNeo4jInsertionError)
EntityTypeFulltextIndexModelVectorIndexModelc                   	 t        | |||||       	 d|rdnd d| d| d	}	t        j                  d
| d       | j                  |	|||d|       y# t        $ r"}t        d|j                                |d}~ww xY w# t        j                  j                  $ r}t        d|j                         |d}~ww xY w)a  
    This method constructs a Cypher query and executes it
    to create a new vector index in Neo4j.

    See Cypher manual on `creating vector indexes <https://neo4j.com/docs/cypher-manual/current/indexes/semantic-indexes/vector-indexes/#create-vector-index>`_.

    Ensure that the index name provided is unique within the database context.

    Example:

    .. code-block:: python

        from neo4j import GraphDatabase
        from neo4j_graphrag.indexes import create_vector_index

        URI = "neo4j://localhost:7687"
        AUTH = ("neo4j", "password")

        INDEX_NAME = "vector-index-name"

        # Connect to Neo4j database
        driver = GraphDatabase.driver(URI, auth=AUTH)

        # Creating the index
        create_vector_index(
            driver,
            INDEX_NAME,
            label="Document",
            embedding_property="vectorProperty",
            dimensions=1536,
            similarity_fn="euclidean",
            fail_if_exists=False,
        )


    Args:
        driver (neo4j.Driver): Neo4j Python driver instance.
        name (str): The unique name of the index.
        label (str): The node label to be indexed.
        embedding_property (str): The property key of a node which contains embedding values.
        dimensions (int): Vector embedding dimension
        similarity_fn (str): case-insensitive values for the vector similarity function:
            ``euclidean`` or ``cosine``.
        fail_if_exists (bool): If True raise an error if the index already exists. Defaults to False.
        neo4j_database (Optional[str]): The name of the Neo4j database. If not provided, this defaults to the server's default database ("neo4j" by default) (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Raises:
        ValueError: If validation of the input arguments fail.
        neo4j.exceptions.ClientError: If creation of vector index fails.
    )drivernamelabelembedding_property
dimensionssimilarity_fnz(Error for inputs to create_vector_index NzCREATE VECTOR INDEX $name  IF NOT EXISTSz FOR (n:z) ON n.zw OPTIONS { indexConfig: { `vector.dimensions`: toInteger($dimensions), `vector.similarity_function`: $similarity_fn } }zCreating vector index named '')r   r   r   	database_z$Neo4j vector index creation failed: )r   r   r   errorsloggerinfoexecute_queryneo4j
exceptionsClientErrormessage)
r   r   r   r   r   r   fail_if_existsneo4j_databaseequerys
             T/opt/lhia/marcimex/agent/venv/lib/python3.12/site-packages/neo4j_graphrag/indexes.pycreate_vector_indexr+   %   s    x1!'	
Y(~?(SS[\a[bbij|i} ~} } 	 	3D6;<mT$ 	 	
  6qxxzlC
	  '' Y DQYYKPQWXXYs/   A AB 	BA<<BB?!B::B?c                   	 t        | |||       	 d|rdnd d| dd	j	                  |D cg c]
  }d
|z   dz    c}       d}t
        j                  d| d       | j                  |d|i|       y# t        $ r"}t        d|j                                |d}~ww xY wc c}w # t        j                  j                  $ r}t        d|j                         |d}~ww xY w)a  
    This method constructs a Cypher query and executes it
    to create a new fulltext index in Neo4j.

    See Cypher manual on `creating fulltext indexes <https://neo4j.com/docs/cypher-manual/current/indexes/semantic-indexes/full-text-indexes/#create-full-text-indexes>`_.

    Ensure that the index name provided is unique within the database context.

    Example:

    .. code-block:: python

        from neo4j import GraphDatabase
        from neo4j_graphrag.indexes import create_fulltext_index

        URI = "neo4j://localhost:7687"
        AUTH = ("neo4j", "password")

        INDEX_NAME = "fulltext-index-name"

        # Connect to Neo4j database
        driver = GraphDatabase.driver(URI, auth=AUTH)

        # Creating the index
        create_fulltext_index(
            driver,
            INDEX_NAME,
            label="Document",
            node_properties=["vectorProperty"],
            fail_if_exists=False,
        )


    Args:
        driver (neo4j.Driver): Neo4j Python driver instance.
        name (str): The unique name of the index.
        label (str): The node label to be indexed.
        node_properties (list[str]): The node properties to create the fulltext index on.
        fail_if_exists (bool): If True raise an error if the index already exists. Defaults to False.
        neo4j_database (Optional[str]): The name of the Neo4j database. If not provided, this defaults to the server's default database ("neo4j" by default) (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Raises:
        ValueError: If validation of the input arguments fail.
        neo4j.exceptions.ClientError: If creation of fulltext index fails.
    )r   r   r   node_propertiesz+Error for inputs to create_fulltext_index: NzCREATE FULLTEXT INDEX $name r   r   z	 FOR (n:`z`) ON EACH [z, zn.``]zCreating fulltext index named 'r   r   r   z%Neo4j fulltext index creation failed )r   r   r   r   joinr   r    r!   r"   r#   r$   r%   )	r   r   r   r-   r&   r'   r(   propr)   s	            r*   create_fulltext_indexr2   ~   s   jE?	
*2_*U Vg 		/J$54<#-JKLAO 	
 	5dV1=>UVTNnM  9!((*F
	 K '' 3AII;?
	s>   A5 B( B#9B( 5	B >BB #B( (C#CC#c                    	 d}d|i}t         j                  d| d       | j                  |||       y# t        j                  j
                  $ r}t        d|j                         |d}~ww xY w)a  
    This method constructs a Cypher query and executes it
    to drop an index in Neo4j, if the index exists.
    See Cypher manual on `dropping vector indexes <https://neo4j.com/docs/cypher-manual/current/indexes/semantic-indexes/vector-indexes/#drop-vector-indexes>`_.

    Example:

    .. code-block:: python

        from neo4j import GraphDatabase
        from neo4j_graphrag.indexes import drop_index_if_exists

        URI = "neo4j://localhost:7687"
        AUTH = ("neo4j", "password")

        INDEX_NAME = "fulltext-index-name"

        # Connect to Neo4j database
        driver = GraphDatabase.driver(URI, auth=AUTH)

        # Dropping the index if it exists
        drop_index_if_exists(
            driver,
            INDEX_NAME,
        )


    Args:
        driver (neo4j.Driver): Neo4j Python driver instance.
        name (str): The name of the index to delete.
        neo4j_database (Optional[str]): The name of the Neo4j database. If not provided, this defaults to the server's default database ("neo4j" by default) (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Raises:
        neo4j.exceptions.ClientError: If dropping of index fails.
    zDROP INDEX $name IF EXISTSr   zDropping index named 'r   r   zDropping Neo4j index failed: N)r   r    r!   r"   r#   r$   r   r%   )r   r   r'   r)   
parametersr(   s         r*   drop_index_if_existsr5      s    LR,D

 	,TF!45UJ.I'' R =aii[IJPQQRs   36 A1A,,A1c                   |t         j                  k(  rt        }n%|t         j                  k(  rt        }nt        d      t        |      t              k7  rt        d      t        fdD              st        d      	 t        |      D cg c]
  \  }}||d c}}|d}	| j                  ||	|       y	c c}}w # t        j                  j                  $ r}
t        d|
j                         |
d	}
~
ww xY w)
a  
    This method constructs a Cypher query and executes it to upsert
    (insert or update) embeddings on a set of nodes or relationships.

    Example:

    .. code-block:: python

        from neo4j import GraphDatabase
        from neo4j_graphrag.indexes import upsert_vectors

        URI = "neo4j://localhost:7687"
        AUTH = ("neo4j", "password")

        # Connect to Neo4j database
        driver = GraphDatabase.driver(URI, auth=AUTH)

        # Upsert embeddings data for several nodes
        upsert_vectors(
            driver,
            ids=['123', '456', '789'],
            embedding_property="vectorProperty",
            embeddings=[
                [0.12, 0.34, 0.56],
                [0.78, 0.90, 0.12],
                [0.34, 0.56, 0.78],
            ],
            neo4j_database="neo4j",
            entity_type='NODE',
        )

    Args:
        driver (neo4j.Driver): Neo4j Python driver instance.
        ids (List[int]): The element IDs of the nodes or relationships.
        embedding_property (str): The name of the property to store the vectors in.
        embeddings (List[List[float]]): The list of vectors to store, one per ID.
        neo4j_database (Optional[str]): The name of the Neo4j database.
            If not provided, defaults to the server's default database. 'neo4j' by default.
        entity_type (EntityType): Specifies whether to upsert to nodes ('NODE') or relationships ('RELATIONSHIP').
            Defaults to 'NODE'.

    Raises:
        ValueError: If the lengths of IDs and embeddings do not match, or if embeddings are not of uniform dimension.
        Neo4jInsertionError: If an error occurs while attempting to upsert the vectors in Neo4j.
    z3entity_type must be either 'NODE' or 'RELATIONSHIP'z*ids and embeddings must be the same lengthc              3  R   K   | ]  }t        |      t        d          k(     yw)r   N)len).0	embedding
embeddingss     r*   	<genexpr>z!upsert_vectors.<locals>.<genexpr>8  s#     P	s9~Z]!33Ps   $'z'All embeddings must be of the same size)idr:   )rowsr   query_parameters_r   z#Upserting vectors to Neo4j failed: N)r   NODEr
   RELATIONSHIPr   
ValueErrorr8   allzipr!   r"   r#   r$   r   r%   )r   idsr   r;   r'   entity_typer)   r=   r:   r4   r(   s      `       r*   upsert_vectorsrI      s   j joo%,	
//	/4NOO
3x3z?"EFFPZPPBCC &)j%9!B	 	2 #5

 	jN 	 	
 '' !1!))=
	s*   C B=!C =C C> C99C>c                    t        j                  dt        d       	 |||d}| j                  t        ||       y# t
        j                  j                  $ r}t        d|j                         |d}~ww xY w)an  
    .. warning::
        'upsert_vector' is deprecated and will be removed in a future version, please use 'upsert_vectors' instead.

    This method constructs a Cypher query and executes it to upsert (insert or update) a vector property on a specific node.

    Example:

    .. code-block:: python

        from neo4j import GraphDatabase
        from neo4j_graphrag.indexes import upsert_vector

        URI = "neo4j://localhost:7687"
        AUTH = ("neo4j", "password")

        # Connect to Neo4j database
        driver = GraphDatabase.driver(URI, auth=AUTH)

        # Upsert the vector data
        upsert_vector(
            driver,
            node_id="nodeId",
            embedding_property="vectorProperty",
            vector=...,
        )

    Args:
        driver (neo4j.Driver): Neo4j Python driver instance.
        node_id (int): The element id of the node.
        embedding_property (str): The name of the property to store the vector in.
        vector (list[float]): The vector to store.
        neo4j_database (Optional[str]): The name of the Neo4j database. If not provided, this defaults to the server's default database ("neo4j" by default) (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Raises:
        Neo4jInsertionError: If upserting of the vector fails.
    zk'upsert_vector' is deprecated and will be removed in a future version, please use 'upsert_vectors' instead.   
stacklevel)node_element_idr   vectorr   "Upserting vector to Neo4j failed: N
warningswarnDeprecationWarningr!   r   r"   r#   r$   r   r%   r   node_idr   rO   r'   r4   r(   s          r*   upsert_vectorrW   K  s    X MMu
&"4


 	'~ 	 	
 '' !0<
	   = A8A33A8c                    t        j                  dt        d       	 |||d}| j                  t        ||       y# t
        j                  j                  $ r}t        d|j                         |d}~ww xY w)a  
    .. warning::
        'upsert_vector_on_relationship' is deprecated and will be removed in a future version, please use 'upsert_vectors' instead.

    This method constructs a Cypher query and executes it to upsert (insert or update) a vector property on a specific relationship.

    Example:

    .. code-block:: python

        from neo4j import GraphDatabase
        from neo4j_graphrag.indexes import upsert_vector_on_relationship

        URI = "neo4j://localhost:7687"
        AUTH = ("neo4j", "password")

        # Connect to Neo4j database
        driver = GraphDatabase.driver(URI, auth=AUTH)

        # Upsert the vector data
        upsert_vector_on_relationship(
            driver,
            node_id="nodeId",
            embedding_property="vectorProperty",
            vector=...,
        )

    Args:
        driver (neo4j.Driver): Neo4j Python driver instance.
        rel_id (int): The element id of the relationship.
        embedding_property (str): The name of the property to store the vector in.
        vector (list[float]): The vector to store.
        neo4j_database (Optional[str]): The name of the Neo4j database. If not provided, this defaults to the server's default database ("neo4j" by default) (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Raises:
        Neo4jInsertionError: If upserting of the vector fails.
    z{'upsert_vector_on_relationship' is deprecated and will be removed in a future version, please use 'upsert_vectors' instead.rK   rL   )rel_element_idr   rO   r   rP   N
rR   rS   rT   r!   r	   r"   r#   r$   r   r%   r   rel_idr   rO   r'   r4   r(   s          r*   upsert_vector_on_relationshipr^     s    X MM 	F
$"4


 	/~ 	 	
 '' !0<
	rX   c                  K   t        j                  dt        d       	 |||d}| j                  t        ||       d{    y7 # t
        j                  j                  $ r}t        d|j                         |d}~ww xY ww)a  
    .. warning::
        'async_upsert_vector' is deprecated and will be removed in a future version.

    This method constructs a Cypher query and asynchronously executes it
    to upsert (insert or update) a vector property on a specific node.

    Example:

    .. code-block:: python

        from neo4j import AsyncGraphDatabase
        from neo4j_graphrag.indexes import upsert_vector

        URI = "neo4j://localhost:7687"
        AUTH = ("neo4j", "password")

        # Connect to Neo4j database
        driver = AsyncGraphDatabase.driver(URI, auth=AUTH)

        # Upsert the vector data
        async_upsert_vector(
            driver,
            node_id="nodeId",
            embedding_property="vectorProperty",
            vector=...,
        )

    Args:
        driver (neo4j.AsyncDriver): Neo4j Python asynchronous driver instance.
        node_id (int): The element id of the node.
        embedding_property (str): The name of the property to store the vector in.
        vector (list[float]): The vector to store.
        neo4j_database (Optional[str]): The name of the Neo4j database. If not provided, this defaults to the server's default database ("neo4j" by default) (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Raises:
        Neo4jInsertionError: If upserting of the vector fails.
    zL'async_upsert_vector' is deprecated and will be removed in a future version.rK   rL   )rV   r   rO   r   NrP   rQ   rU   s          r*   async_upsert_vectorr`     s     Z MMV
"4


 ""'~ # 
 	
 	
 '' !0<
	:   B!A	 AA	 BA	 	B&A??BBc                  K   t        j                  dt        d       	 |||d}| j                  t        ||       d{    y7 # t
        j                  j                  $ r}t        d|j                         |d}~ww xY ww)a  
    .. warning::
        'async_upsert_vector_on_relationship' is deprecated and will be removed in a future version.

    This method constructs a Cypher query and asynchronously executes it
    to upsert (insert or update) a vector property on a specific relationship.

    Example:

    .. code-block:: python

        from neo4j import AsyncGraphDatabase
        from neo4j_graphrag.indexes import upsert_vector_on_relationship

        URI = "neo4j://localhost:7687"
        AUTH = ("neo4j", "password")

        # Connect to Neo4j database
        driver = AsyncGraphDatabase.driver(URI, auth=AUTH)

        # Upsert the vector data
        async_upsert_vector_on_relationship(
            driver,
            node_id="nodeId",
            embedding_property="vectorProperty",
            vector=...,
        )

    Args:
        driver (neo4j.AsyncDriver): Neo4j Python asynchronous driver instance.
        rel_id (int): The element id of the relationship.
        embedding_property (str): The name of the property to store the vector in.
        vector (list[float]): The vector to store.
        neo4j_database (Optional[str]): The name of the Neo4j database. If not provided, this defaults to the server's default database ("neo4j" by default) (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Raises:
        Neo4jInsertionError: If upserting of the vector fails.
    z\'async_upsert_vector_on_relationship' is deprecated and will be removed in a future version.rK   rL   )r]   r   rO   r   NrP   r[   r\   s          r*   #async_upsert_vector_on_relationshiprc     s     Z MMf
"4


 ""/~ # 
 	
 	
 '' !0<
	ra   c                $    t        | fd      S )a  
    Sorts the provided list of dictionaries containing index information so
    that any item whose 'name' key matches the given 'index_name' appears at
    the front of the list.

    Args:
        records (List[Dict[str, Any]]): The list of records containing index
            information to sort.
        index_name (str): The index name to match against the 'name' key of
            each dictionary.

    Returns:
        List[Dict[str, Any]]: A newly sorted list with items matching
        'index_name' placed first.
    c                ,    | j                  d      k7  S )Nr   )get)x
index_names    r*   <lambda>z%_sort_by_index_name.<locals>.<lambda>_  s    v*)D     )key)sorted)recordsrh   s    `r*   _sort_by_index_namern   M  s    $ 'DEErj   c                    | j                  d|||d|      }t        |j                  |      }t        |      dkD  r|d   S y)a  
    Check if a vector index exists in a Neo4j database and return its
    information. If no matching index is found, returns None.

    Args:
        driver (neo4j.Driver): Neo4j Python driver instance.
        index_name (str): The name of the index to look up.
        label_or_type (str): The label (for nodes) or type (for relationships)
            of the index.
        embedding_property (str): The name of the property containing the
            embeddings.
        neo4j_database (Optional[str]): The name of the Neo4j database.
            If not provided, this defaults to the server's default database ("neo4j" by default)
            (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Returns:
        Optional[Dict[str, Any]]:
            A dictionary containing the first matching index's information if found,
            or None otherwise.
    a  SHOW INDEXES YIELD name, type, entityType, labelsOrTypes, properties, options WHERE type = 'VECTOR' AND (name = $index_name OR (labelsOrTypes[0] = $label_or_type AND properties[0] = $embedding_property)) RETURN name, type, entityType, labelsOrTypes, properties, options)rh   label_or_typer   r?   r   Nr!   rn   rm   r8   )r   rh   rp   r   r'   resultindex_informations          r*   retrieve_vector_index_infort   b  sc    6 !!P %*"4

 ! " F ,FNNJG
! ##rj   c                    | j                  d|||d|      }t        |j                  |      }t        |      dkD  r|d   S y)a  
    Check if a full text index exists in a Neo4j database and return its
    information. If no matching index is found, returns None.

    Args:
        driver (neo4j.Driver): Neo4j Python driver instance.
        index_name (str): The name of the index to look up.
        label_or_type (str): The label (for nodes) or type (for relationships)
            of the index.
        text_properties (List[str]): The names of the text properties indexed.
        neo4j_database (Optional[str]): The name of the Neo4j database.
            If not provided, this defaults to the server's default database ("neo4j" by default)
            (`see reference to documentation <https://neo4j.com/docs/operations-manual/current/database-administration/#manage-databases-default>`_).

    Returns:
        Optional[Dict[str, Any]]:
            A dictionary containing the first matching index's information if found,
            or None otherwise.
    a  SHOW INDEXES YIELD name, type, entityType, labelsOrTypes, properties, options WHERE type = 'FULLTEXT' AND (name = $index_name OR (labelsOrTypes = [$label_or_type] AND properties = $text_properties)) RETURN name, type, entityType, labelsOrTypes, properties, options)rh   rp   text_propertiesr?   r   Nrq   )r   rh   rp   rv   r'   rr   rs   s          r*   retrieve_fulltext_index_inforw     sc    4 !!P %*.

 ! " F ,FNNJG
! ##rj   )FN)r   neo4j.Driverr   strr   ry   r   ry   r   intr   zLiteral['euclidean', 'cosine']r&   boolr'   Optional[str]returnNone)r   rx   r   ry   r   ry   r-   z	list[str]r&   r{   r'   r|   r}   r~   )N)r   rx   r   ry   r'   r|   r}   r~   )r   rx   rG   	List[str]r   ry   r;   zList[List[float]]r'   r|   rH   r   r}   r~   )r   rx   rV   rz   r   ry   rO   list[float]r'   r|   r}   r~   )r   rx   r]   rz   r   ry   rO   r   r'   r|   r}   r~   )r   neo4j.AsyncDriverrV   rz   r   ry   rO   r   r'   r|   r}   r~   )r   r   r]   rz   r   ry   rO   r   r'   r|   r}   r~   )rm   List[neo4j.Record]rh   ry   r}   r   )r   rx   rh   ry   rp   ry   r   ry   r'   r|   r}   Optional[neo4j.Record])r   rx   rh   ry   rp   ry   rv   r   r'   r|   r}   r   )&
__future__r   loggingrR   typingr   r   r   r"   pydanticr   neo4j_graphrag.neo4j_queriesr   r	   r
   r   r#   r   r   typesr   r   r   	getLogger__name__r   r+   r2   r5   rB   rI   rW   r^   r`   rc   rn   rt   rw    rj   r*   <module>r      sV   #   * *  $  = C C			8	$ !$(VYVY
VY VY 	VY
 VY 2VY VY "VY 
VY| !$(II
I I 	I
 I "I 
IZ FJ.R.R #.R5B.R	.Rl %)(ooMM	M M "	M
 "M M 
Mj %)=== = 	=
 "= 
=J %)=== = 	=
 "= 
=J %)>>> > 	>
 "> 
>L %)>>> > 	>
 "> 
>BFF-0FF4 %)... . 	.
 ". .j "$$(--- - 	-
 "- -rj   