Skip to content

Commit eeedf49

Browse files
Anush008willtai
andauthored
feat: Qdrant external retriever (#154)
* feat: Qdrant external retriever * test: ci updates * chore: add qdrant-client to dev deps * chore: poetry.lock * Update docs/source/api.rst Co-authored-by: willtai <wtaisen@gmail.com> * chore: fix mypy nit * Update docs/source/user_guide_rag.rst Co-authored-by: willtai <wtaisen@gmail.com> --------- Co-authored-by: willtai <wtaisen@gmail.com> Co-authored-by: willtai <william.tai@neo4j.com>
1 parent 6a8cea6 commit eeedf49

File tree

21 files changed

+996
-39
lines changed

21 files changed

+996
-39
lines changed

.github/workflows/pr-e2e-tests.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ jobs:
4545
ports:
4646
- 7687:7687
4747
- 7474:7474
48+
qdrant:
49+
image: qdrant/qdrant
50+
ports:
51+
- 6333:6333
4852

4953
steps:
5054
- name: Install graphviz package

.github/workflows/scheduled-e2e-tests.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ jobs:
5252
credentials:
5353
username: ${{ secrets.DOCKERHUB_USERNAME }}
5454
password: ${{ secrets.DOCKERHUB_TOKEN }}
55+
qdrant:
56+
image: qdrant/qdrant
57+
ports:
58+
- 6333:6333
5559

5660
steps:
5761
- name: Install graphviz package

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
- Added support for Cohere LLM and embeddings - added optional dependency to `cohere`.
3636
- Added support for Anthropic LLM - added optional dependency to `anthropic`.
3737
- Added support for MistralAI LLM - added optional dependency to `mistralai`.
38+
- Added support for Qdrant - added optional dependency to `qdrant-client`.
3839

3940
### Fixed
4041
- Resolved import issue with the Vertex AI Embeddings class.

docs/source/api.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ PineconeNeo4jRetriever
163163
.. autoclass:: neo4j_graphrag.retrievers.external.pinecone.pinecone.PineconeNeo4jRetriever
164164
:members: search
165165

166+
QdrantNeo4jRetriever
167+
====================
168+
169+
.. autoclass:: neo4j_graphrag.retrievers.external.qdrant.qdrant.QdrantNeo4jRetriever
170+
:members: search
171+
166172

167173
********
168174
Embedder

docs/source/user_guide_rag.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ We provide implementations for the following retrievers:
327327
- Use this retriever when vectors are saved in a Weaviate vector database
328328
* - :ref:`PineconeNeo4jRetriever <pinecone-neo4j-retriever-user-guide>`
329329
- Use this retriever when vectors are saved in a Pinecone vector database
330+
* - :ref:`QdrantNeo4jRetriever <qdrant-neo4j-retriever-user-guide>`
331+
- Use this retriever when vectors are saved in a Qdrant vector database
330332

331333
Retrievers all expose a `search` method that we will discuss in the next sections.
332334

@@ -672,6 +674,35 @@ Pinecone Retrievers
672674
673675
Also see :ref:`pineconeneo4jretriever`.
674676

677+
.. _qdrant-neo4j-retriever-user-guide:
678+
679+
Qdrant Retrievers
680+
-----------------
681+
682+
.. note::
683+
684+
In order to import this retriever, the Qdrant Python client must be installed:
685+
`pip install qdrant-client`
686+
687+
688+
.. code:: python
689+
690+
from qdrant_client import QdrantClient
691+
from neo4j_graphrag.retrievers import QdrantNeo4jRetriever
692+
693+
client = QdrantClient(...) # construct the Qdrant client instance
694+
695+
retriever = QdrantNeo4jRetriever(
696+
driver=driver,
697+
client=client,
698+
collection_name="my-collection",
699+
id_property_external="neo4j_id", # The payload field that contains identifier to a corresponding Neo4j node id property
700+
id_property_neo4j="id",
701+
embedder=embedder,
702+
)
703+
704+
See :ref:`qdrantneo4jretriever`.
705+
675706

676707
Other Retrievers
677708
===================

examples/qdrant/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
### Start services locally
2+
3+
Run the following command to spin up Neo4j and Qdrant containers.
4+
5+
```bash
6+
docker compose -f tests/e2e/docker-compose.yml up
7+
```
8+
9+
### Write data (once)
10+
11+
Run this from the project root to write data to both Neo4J and Qdrant.
12+
13+
```bash
14+
poetry run python tests/e2e/qdrant_e2e/populate_dbs.py
15+
```
16+
17+
### Install Qdrant client
18+
19+
```bash
20+
pip install qdrant-client
21+
```
22+
23+
### Search
24+
25+
```bash
26+
# search by vector
27+
poetry run python -m examples.qdrant.vector_search
28+
29+
# search by text, with embeddings generated locally
30+
poetry run python -m examples.qdrant.text_search
31+
```

examples/qdrant/__init__.py

Whitespace-only changes.

examples/qdrant/text_search.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from langchain_huggingface.embeddings import HuggingFaceEmbeddings
2+
from neo4j import GraphDatabase
3+
from neo4j_graphrag.retrievers import QdrantNeo4jRetriever
4+
from qdrant_client import QdrantClient
5+
6+
NEO4J_URL = "neo4j://localhost:7687"
7+
NEO4J_AUTH = ("neo4j", "password")
8+
9+
10+
def main() -> None:
11+
with GraphDatabase.driver(NEO4J_URL, auth=NEO4J_AUTH) as neo4j_driver:
12+
embedder = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
13+
retriever = QdrantNeo4jRetriever(
14+
driver=neo4j_driver,
15+
client=QdrantClient(url="http://localhost:6333"),
16+
collection_name="Jeopardy",
17+
id_property_external="neo4j_id",
18+
id_property_neo4j="id",
19+
embedder=embedder, # type: ignore
20+
)
21+
22+
res = retriever.search(query_text="biology", top_k=2)
23+
print(res)
24+
25+
26+
if __name__ == "__main__":
27+
main()

examples/qdrant/vector_search.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from neo4j import GraphDatabase
2+
from neo4j_graphrag.retrievers import QdrantNeo4jRetriever
3+
from qdrant_client import QdrantClient
4+
5+
from examples.embedding_biology import EMBEDDING_BIOLOGY
6+
7+
NEO4J_URL = "neo4j://localhost:7687"
8+
NEO4J_AUTH = ("neo4j", "password")
9+
10+
11+
def main() -> None:
12+
with GraphDatabase.driver(NEO4J_URL, auth=NEO4J_AUTH) as neo4j_driver:
13+
retriever = QdrantNeo4jRetriever(
14+
driver=neo4j_driver,
15+
client=QdrantClient(url="http://localhost:6333"),
16+
collection_name="Jeopardy",
17+
id_property_external="neo4j_id",
18+
id_property_neo4j="id",
19+
)
20+
res = retriever.search(query_vector=EMBEDDING_BIOLOGY, top_k=2)
21+
print(res)
22+
23+
24+
if __name__ == "__main__":
25+
main()

poetry.lock

Lines changed: 110 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)