> ## Documentation Index
> Fetch the complete documentation index at: https://docs.langchain.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Neo4j vector index integration

> Integrate with the Neo4j vector index vector store using LangChain Python.

> [Neo4j](https://neo4j.com/) is an open-source graph database with integrated support for vector similarity search

It supports:

* approximate nearest neighbor search
* Euclidean similarity and cosine similarity
* Hybrid search combining vector and keyword searches

This notebook shows how to use the Neo4j vector index (`Neo4jVector`).

See the [installation instruction](https://neo4j.com/docs/operations-manual/current/installation/).

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# Pip install necessary package
pip install -qU  neo4j
pip install -qU  langchain-openai langchain-neo4j
pip install -qU  tiktoken
```

We want to use `OpenAIEmbeddings` so we have to get the OpenAI API Key.

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import getpass
import os

if "OPENAI_API_KEY" not in os.environ:
    os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")
```

```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
OpenAI API Key: ········
```

<Warning>
  The `langchain-community` package is no longer maintained. Examples that import from `langchain_community` may be outdated or broken. Use with caution.
</Warning>

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from langchain_community.document_loaders import TextLoader
from langchain_core.documents import Document
from langchain_neo4j import Neo4jVector
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
loader = TextLoader("../../how_to/state_of_the_union.txt")

documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings()
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# Neo4jVector requires the Neo4j database credentials

url = "bolt://localhost:7687"
username = "neo4j"
password = "password"

# You can also use environment variables instead of directly passing named parameters
# os.environ["NEO4J_URI"] = "bolt://localhost:7687"
# os.environ["NEO4J_USERNAME"] = "neo4j"
# os.environ["NEO4J_PASSWORD"] = "pleaseletmein"
```

## Similarity search with cosine distance (Default)

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# The Neo4jVector Module will connect to Neo4j and create a vector index if needed.

db = Neo4jVector.from_documents(
    docs, OpenAIEmbeddings(), url=url, username=username, password=password
)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
query = "What did the president say about Ketanji Brown Jackson"
docs_with_score = db.similarity_search_with_score(query, k=2)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
for doc, score in docs_with_score:
    print("-" * 80)
    print("Score: ", score)
    print(doc.page_content)
    print("-" * 80)
```

```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
--------------------------------------------------------------------------------
Score:  0.9076391458511353
Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections.

Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.

One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.

And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Score:  0.8912242650985718
A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.

And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system.

We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling.

We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers.

We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster.

We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.
--------------------------------------------------------------------------------
```

## Working with vectorstore

Above, we created a vectorstore from scratch. However, often times we want to work with an existing vectorstore.
In order to do that, we can initialize it directly.

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
index_name = "vector"  # default index name

store = Neo4jVector.from_existing_index(
    OpenAIEmbeddings(),
    url=url,
    username=username,
    password=password,
    index_name=index_name,
)
```

We can also initialize a vectorstore from existing graph using the `from_existing_graph` method. This method pulls relevant text information from the database, and calculates and stores the text embeddings back to the database.

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# First we create sample data in graph
store.query(
    "CREATE (p:Person {name: 'Tomaz', location:'Slovenia', hobby:'Bicycle', age: 33})"
)
```

```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[]
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# Now we initialize from existing graph
existing_graph = Neo4jVector.from_existing_graph(
    embedding=OpenAIEmbeddings(),
    url=url,
    username=username,
    password=password,
    index_name="person_index",
    node_label="Person",
    text_node_properties=["name", "location"],
    embedding_node_property="embedding",
)
result = existing_graph.similarity_search("Slovenia", k=1)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
result[0]
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
Document(page_content='\nname: Tomaz\nlocation: Slovenia', metadata={'age': 33, 'hobby': 'Bicycle'})
```

Neo4j also supports relationship vector indexes, where an embedding is stored as a relationship property and indexed. A relationship vector index cannot be populated via LangChain, but you can connect it to existing relationship vector indexes.

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# First we create sample data and index in graph
store.query(
    "MERGE (p:Person {name: 'Tomaz'}) "
    "MERGE (p1:Person {name:'Leann'}) "
    "MERGE (p1)-[:FRIEND {text:'example text', embedding:$embedding}]->(p2)",
    params={"embedding": OpenAIEmbeddings().embed_query("example text")},
)
# Create a vector index
relationship_index = "relationship_vector"
store.query(
    """
CREATE VECTOR INDEX $relationship_index
IF NOT EXISTS
FOR ()-[r:FRIEND]-() ON (r.embedding)
OPTIONS {indexConfig: {
 `vector.dimensions`: 1536,
 `vector.similarity_function`: 'cosine'
}}
""",
    params={"relationship_index": relationship_index},
)
```

```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[]
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
relationship_vector = Neo4jVector.from_existing_relationship_index(
    OpenAIEmbeddings(),
    url=url,
    username=username,
    password=password,
    index_name=relationship_index,
    text_node_property="text",
)
relationship_vector.similarity_search("Example")
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[Document(page_content='example text')]
```

### Metadata filtering

Neo4j vector store also supports metadata filtering by combining parallel runtime and exact nearest neighbor search.
*Requires Neo4j 5.18 or greater version.*

Equality filtering has the following syntax.

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
existing_graph.similarity_search(
    "Slovenia",
    filter={"hobby": "Bicycle", "name": "Tomaz"},
)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[Document(page_content='\nname: Tomaz\nlocation: Slovenia', metadata={'age': 33, 'hobby': 'Bicycle'})]
```

Metadata filtering also support the following operators:

* `$eq: Equal`
* `$ne: Not Equal`
* `$lt: Less than`
* `$lte: Less than or equal`
* `$gt: Greater than`
* `$gte: Greater than or equal`
* `$in: In a list of values`
* `$nin: Not in a list of values`
* `$between: Between two values`
* `$like: Text contains value`
* `$ilike: lowered text contains value`

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
existing_graph.similarity_search(
    "Slovenia",
    filter={"hobby": {"$eq": "Bicycle"}, "age": {"$gt": 15}},
)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[Document(page_content='\nname: Tomaz\nlocation: Slovenia', metadata={'age': 33, 'hobby': 'Bicycle'})]
```

You can also use `OR` operator between filters

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
existing_graph.similarity_search(
    "Slovenia",
    filter={"$or": [{"hobby": {"$eq": "Bicycle"}}, {"age": {"$gt": 15}}]},
)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[Document(page_content='\nname: Tomaz\nlocation: Slovenia', metadata={'age': 33, 'hobby': 'Bicycle'})]
```

### Add documents

We can add documents to the existing vectorstore.

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
store.add_documents([Document(page_content="foo")])
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
['acbd18db4cc2f85cedef654fccc4a4d8']
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
docs_with_score = store.similarity_search_with_score("foo")
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
docs_with_score[0]
```

```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
(Document(page_content='foo'), 0.9999997615814209)
```

## Customize response with retrieval query

You can also customize responses by using a custom Cypher snippet that can fetch other information from the graph.
Under the hood, the final Cypher statement is constructed like so:

```
read_query = (
  "CALL db.index.vector.queryNodes($index, $k, $embedding) "
  "YIELD node, score "
) + retrieval_query
```

The retrieval query must return the following three columns:

* `text`: Union\[str, Dict] = Value used to populate `page_content` of a document
* `score`: Float = Similarity score
* `metadata`: Dict = Additional metadata of a document

Learn more in this [blog post](https://medium.com/neo4j/implementing-rag-how-to-write-a-graph-retrieval-query-in-langchain-74abf13044f2).

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
retrieval_query = """
RETURN "Name:" + node.name AS text, score, {foo:"bar"} AS metadata
"""
retrieval_example = Neo4jVector.from_existing_index(
    OpenAIEmbeddings(),
    url=url,
    username=username,
    password=password,
    index_name="person_index",
    retrieval_query=retrieval_query,
)
retrieval_example.similarity_search("Foo", k=1)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[Document(page_content='Name:Tomaz', metadata={'foo': 'bar'})]
```

Here is an example of passing all node properties except for `embedding` as a dictionary to `text` column,

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
retrieval_query = """
RETURN node {.name, .age, .hobby} AS text, score, {foo:"bar"} AS metadata
"""
retrieval_example = Neo4jVector.from_existing_index(
    OpenAIEmbeddings(),
    url=url,
    username=username,
    password=password,
    index_name="person_index",
    retrieval_query=retrieval_query,
)
retrieval_example.similarity_search("Foo", k=1)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[Document(page_content='name: Tomaz\nage: 33\nhobby: Bicycle\n', metadata={'foo': 'bar'})]
```

You can also pass Cypher parameters to the retrieval query.
Parameters can be used for additional filtering, traversals, etc...

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
retrieval_query = """
RETURN node {.*, embedding:Null, extra: $extra} AS text, score, {foo:"bar"} AS metadata
"""
retrieval_example = Neo4jVector.from_existing_index(
    OpenAIEmbeddings(),
    url=url,
    username=username,
    password=password,
    index_name="person_index",
    retrieval_query=retrieval_query,
)
retrieval_example.similarity_search("Foo", k=1, params={"extra": "ParamInfo"})
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
[Document(page_content='location: Slovenia\nextra: ParamInfo\nname: Tomaz\nage: 33\nhobby: Bicycle\nembedding: None\n', metadata={'foo': 'bar'})]
```

## Hybrid search (vector + keyword)

Neo4j integrates both vector and keyword indexes, which allows you to use a hybrid search approach

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# The Neo4jVector Module will connect to Neo4j and create a vector and keyword indices if needed.
hybrid_db = Neo4jVector.from_documents(
    docs,
    OpenAIEmbeddings(),
    url=url,
    username=username,
    password=password,
    search_type="hybrid",
)
```

To load the hybrid search from existing indexes, you have to provide both the vector and keyword indices

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
index_name = "vector"  # default index name
keyword_index_name = "keyword"  # default keyword index name

store = Neo4jVector.from_existing_index(
    OpenAIEmbeddings(),
    url=url,
    username=username,
    password=password,
    index_name=index_name,
    keyword_index_name=keyword_index_name,
    search_type="hybrid",
)
```

## Retriever options

This section shows how to use `Neo4jVector` as a retriever.

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
retriever = store.as_retriever()
retriever.invoke(query)[0]
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
Document(page_content='Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. \n\nTonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. \n\nOne of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n\nAnd I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.', metadata={'source': '../../how_to/state_of_the_union.txt'})
```

## Question answering with sources

This section goes over how to do question-answering with sources over an Index. It does this by using the `RetrievalQAWithSourcesChain`, which does the lookup of the documents from an Index.

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from langchain_classic.chains import RetrievalQAWithSourcesChain
from langchain_openai import ChatOpenAI
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
chain = RetrievalQAWithSourcesChain.from_chain_type(
    ChatOpenAI(temperature=0), chain_type="stuff", retriever=retriever
)
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
chain.invoke(
    {"question": "What did the president say about Justice Breyer"},
    return_only_outputs=True,
)
```

```text theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
{'answer': 'The president honored Justice Stephen Breyer for his service to the country and mentioned his retirement from the United States Supreme Court.\n',
 'sources': '../../how_to/state_of_the_union.txt'}
```

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
```

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/oss/python/integrations/vectorstores/neo4jvector.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
