pgvector-haystack 0.2.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pgvector_haystack-0.4.0/CHANGELOG.md +35 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/PKG-INFO +1 -1
- pgvector_haystack-0.4.0/examples/hybrid_retrieval.py +69 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/pydoc/config.yml +1 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/src/haystack_integrations/document_stores/pgvector/document_store.py +63 -32
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/tests/conftest.py +0 -1
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/tests/test_document_store.py +14 -9
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/tests/test_retrievers.py +12 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/.gitignore +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/LICENSE.txt +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/README.md +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/examples/embedding_retrieval.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/pyproject.toml +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/src/haystack_integrations/components/retrievers/pgvector/__init__.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/src/haystack_integrations/components/retrievers/pgvector/embedding_retriever.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/src/haystack_integrations/components/retrievers/pgvector/keyword_retriever.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/src/haystack_integrations/document_stores/pgvector/__init__.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/src/haystack_integrations/document_stores/pgvector/filters.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/tests/__init__.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/tests/test_embedding_retrieval.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/tests/test_filters.py +0 -0
- {pgvector_haystack-0.2.0 → pgvector_haystack-0.4.0}/tests/test_keyword_retrieval.py +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [unreleased]
|
|
4
|
+
|
|
5
|
+
### 🚀 Features
|
|
6
|
+
|
|
7
|
+
- Defer the database connection to when it's needed (#773)
|
|
8
|
+
|
|
9
|
+
## [integrations/pgvector-v0.2.0] - 2024-05-08
|
|
10
|
+
|
|
11
|
+
### 🚀 Features
|
|
12
|
+
|
|
13
|
+
- `MongoDBAtlasEmbeddingRetriever` (#427)
|
|
14
|
+
- Implement keyword retrieval for pgvector integration (#644)
|
|
15
|
+
|
|
16
|
+
### 🐛 Bug Fixes
|
|
17
|
+
|
|
18
|
+
- Fix order of API docs (#447)
|
|
19
|
+
|
|
20
|
+
This PR will also push the docs to Readme
|
|
21
|
+
|
|
22
|
+
### 📚 Documentation
|
|
23
|
+
|
|
24
|
+
- Update category slug (#442)
|
|
25
|
+
- Disable-class-def (#556)
|
|
26
|
+
|
|
27
|
+
## [integrations/pgvector-v0.1.0] - 2024-02-14
|
|
28
|
+
|
|
29
|
+
### 🐛 Bug Fixes
|
|
30
|
+
|
|
31
|
+
- Fix linting (#328)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
<!-- generated by git-cliff -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pgvector-haystack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: An integration of pgvector (vector search extension for Postgres) with Haystack
|
|
5
5
|
Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations
|
|
6
6
|
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/blob/main/integrations/pgvector/README.md
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Before running this example, ensure you have PostgreSQL installed with the pgvector extension.
|
|
2
|
+
# For a quick setup using Docker:
|
|
3
|
+
# docker run -d -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres
|
|
4
|
+
# -e POSTGRES_DB=postgres ankane/pgvector
|
|
5
|
+
|
|
6
|
+
# Install required packages for this example, including pgvector-haystack and other libraries needed
|
|
7
|
+
# for Markdown conversion and embeddings generation. Use the following command:
|
|
8
|
+
# pip install pgvector-haystack markdown-it-py mdit_plain "sentence-transformers>=2.2.0"
|
|
9
|
+
|
|
10
|
+
# Download some Markdown files to index.
|
|
11
|
+
# git clone https://github.com/anakin87/neural-search-pills
|
|
12
|
+
|
|
13
|
+
import glob
|
|
14
|
+
|
|
15
|
+
from haystack import Pipeline
|
|
16
|
+
from haystack.components.converters import MarkdownToDocument
|
|
17
|
+
from haystack.components.embedders import SentenceTransformersDocumentEmbedder, SentenceTransformersTextEmbedder
|
|
18
|
+
from haystack.components.joiners import DocumentJoiner
|
|
19
|
+
from haystack.components.preprocessors import DocumentSplitter
|
|
20
|
+
from haystack.components.writers import DocumentWriter
|
|
21
|
+
from haystack_integrations.components.retrievers.pgvector import PgvectorEmbeddingRetriever, PgvectorKeywordRetriever
|
|
22
|
+
from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
|
|
23
|
+
|
|
24
|
+
# Set an environment variable `PG_CONN_STR` with the connection string to your PostgreSQL database.
|
|
25
|
+
# e.g., "postgresql://USER:PASSWORD@HOST:PORT/DB_NAME"
|
|
26
|
+
|
|
27
|
+
# Initialize PgvectorDocumentStore
|
|
28
|
+
document_store = PgvectorDocumentStore(
|
|
29
|
+
table_name="haystack_test",
|
|
30
|
+
embedding_dimension=768,
|
|
31
|
+
vector_function="cosine_similarity",
|
|
32
|
+
recreate_table=True,
|
|
33
|
+
search_strategy="hnsw",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Create the indexing Pipeline and index some documents
|
|
37
|
+
file_paths = glob.glob("neural-search-pills/pills/*.md")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
indexing = Pipeline()
|
|
41
|
+
indexing.add_component("converter", MarkdownToDocument())
|
|
42
|
+
indexing.add_component("splitter", DocumentSplitter(split_by="sentence", split_length=2))
|
|
43
|
+
indexing.add_component("document_embedder", SentenceTransformersDocumentEmbedder())
|
|
44
|
+
indexing.add_component("writer", DocumentWriter(document_store))
|
|
45
|
+
indexing.connect("converter", "splitter")
|
|
46
|
+
indexing.connect("splitter", "document_embedder")
|
|
47
|
+
indexing.connect("document_embedder", "writer")
|
|
48
|
+
|
|
49
|
+
indexing.run({"converter": {"sources": file_paths}})
|
|
50
|
+
|
|
51
|
+
# Create the querying Pipeline and try a query
|
|
52
|
+
querying = Pipeline()
|
|
53
|
+
querying.add_component("text_embedder", SentenceTransformersTextEmbedder())
|
|
54
|
+
querying.add_component("retriever", PgvectorEmbeddingRetriever(document_store=document_store, top_k=3))
|
|
55
|
+
querying.add_component("keyword_retriever", PgvectorKeywordRetriever(document_store=document_store, top_k=3))
|
|
56
|
+
querying.add_component(
|
|
57
|
+
"joiner",
|
|
58
|
+
DocumentJoiner(join_mode="reciprocal_rank_fusion", top_k=3),
|
|
59
|
+
)
|
|
60
|
+
querying.connect("text_embedder", "retriever")
|
|
61
|
+
querying.connect("keyword_retriever", "joiner")
|
|
62
|
+
querying.connect("retriever", "joiner")
|
|
63
|
+
|
|
64
|
+
query = "cross-encoder"
|
|
65
|
+
results = querying.run({"text_embedder": {"text": query}, "keyword_retriever": {"query": query}})
|
|
66
|
+
|
|
67
|
+
for doc in results["joiner"]["documents"]:
|
|
68
|
+
print(doc)
|
|
69
|
+
print("-" * 10)
|
|
@@ -3,6 +3,7 @@ loaders:
|
|
|
3
3
|
search_path: [../src]
|
|
4
4
|
modules: [
|
|
5
5
|
"haystack_integrations.components.retrievers.pgvector.embedding_retriever",
|
|
6
|
+
"haystack_integrations.components.retrievers.pgvector.keyword_retriever",
|
|
6
7
|
"haystack_integrations.document_stores.pgvector.document_store",
|
|
7
8
|
]
|
|
8
9
|
ignore_when_discovered: ["__init__"]
|
|
@@ -69,10 +69,6 @@ VECTOR_FUNCTION_TO_POSTGRESQL_OPS = {
|
|
|
69
69
|
|
|
70
70
|
HNSW_INDEX_CREATION_VALID_KWARGS = ["m", "ef_construction"]
|
|
71
71
|
|
|
72
|
-
HNSW_INDEX_NAME = "haystack_hnsw_index"
|
|
73
|
-
|
|
74
|
-
KEYWORD_INDEX_NAME = "haystack_keyword_index"
|
|
75
|
-
|
|
76
72
|
|
|
77
73
|
class PgvectorDocumentStore:
|
|
78
74
|
"""
|
|
@@ -91,7 +87,9 @@ class PgvectorDocumentStore:
|
|
|
91
87
|
search_strategy: Literal["exact_nearest_neighbor", "hnsw"] = "exact_nearest_neighbor",
|
|
92
88
|
hnsw_recreate_index_if_exists: bool = False,
|
|
93
89
|
hnsw_index_creation_kwargs: Optional[Dict[str, int]] = None,
|
|
90
|
+
hnsw_index_name: str = "haystack_hnsw_index",
|
|
94
91
|
hnsw_ef_search: Optional[int] = None,
|
|
92
|
+
keyword_index_name: str = "haystack_keyword_index",
|
|
95
93
|
):
|
|
96
94
|
"""
|
|
97
95
|
Creates a new PgvectorDocumentStore instance.
|
|
@@ -127,9 +125,11 @@ class PgvectorDocumentStore:
|
|
|
127
125
|
:param hnsw_index_creation_kwargs: Additional keyword arguments to pass to the HNSW index creation.
|
|
128
126
|
Only used if search_strategy is set to `"hnsw"`. You can find the list of valid arguments in the
|
|
129
127
|
[pgvector documentation](https://github.com/pgvector/pgvector?tab=readme-ov-file#hnsw)
|
|
128
|
+
:param hnsw_index_name: Index name for the HNSW index.
|
|
130
129
|
:param hnsw_ef_search: The `ef_search` parameter to use at query time. Only used if search_strategy is set to
|
|
131
130
|
`"hnsw"`. You can find more information about this parameter in the
|
|
132
131
|
[pgvector documentation](https://github.com/pgvector/pgvector?tab=readme-ov-file#hnsw).
|
|
132
|
+
:param keyword_index_name: Index name for the Keyword index.
|
|
133
133
|
"""
|
|
134
134
|
|
|
135
135
|
self.connection_string = connection_string
|
|
@@ -143,28 +143,57 @@ class PgvectorDocumentStore:
|
|
|
143
143
|
self.search_strategy = search_strategy
|
|
144
144
|
self.hnsw_recreate_index_if_exists = hnsw_recreate_index_if_exists
|
|
145
145
|
self.hnsw_index_creation_kwargs = hnsw_index_creation_kwargs or {}
|
|
146
|
+
self.hnsw_index_name = hnsw_index_name
|
|
146
147
|
self.hnsw_ef_search = hnsw_ef_search
|
|
148
|
+
self.keyword_index_name = keyword_index_name
|
|
147
149
|
self.language = language
|
|
150
|
+
self._connection = None
|
|
151
|
+
self._cursor = None
|
|
152
|
+
self._dict_cursor = None
|
|
148
153
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
self.
|
|
154
|
+
@property
|
|
155
|
+
def cursor(self):
|
|
156
|
+
if self._cursor is None:
|
|
157
|
+
self._create_connection()
|
|
158
|
+
|
|
159
|
+
return self._cursor
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def dict_cursor(self):
|
|
163
|
+
if self._dict_cursor is None:
|
|
164
|
+
self._create_connection()
|
|
152
165
|
|
|
153
|
-
|
|
154
|
-
self._cursor = connection.cursor()
|
|
155
|
-
self._dict_cursor = connection.cursor(row_factory=dict_row)
|
|
166
|
+
return self._dict_cursor
|
|
156
167
|
|
|
168
|
+
@property
|
|
169
|
+
def connection(self):
|
|
170
|
+
if self._connection is None:
|
|
171
|
+
self._create_connection()
|
|
172
|
+
|
|
173
|
+
return self._connection
|
|
174
|
+
|
|
175
|
+
def _create_connection(self):
|
|
176
|
+
conn_str = self.connection_string.resolve_value() or ""
|
|
177
|
+
connection = connect(conn_str)
|
|
178
|
+
connection.autocommit = True
|
|
157
179
|
connection.execute("CREATE EXTENSION IF NOT EXISTS vector")
|
|
158
|
-
register_vector(connection)
|
|
180
|
+
register_vector(connection) # Note: this must be called before creating the cursors.
|
|
159
181
|
|
|
160
|
-
|
|
182
|
+
self._connection = connection
|
|
183
|
+
self._cursor = self._connection.cursor()
|
|
184
|
+
self._dict_cursor = self._connection.cursor(row_factory=dict_row)
|
|
185
|
+
|
|
186
|
+
# Init schema
|
|
187
|
+
if self.recreate_table:
|
|
161
188
|
self.delete_table()
|
|
162
189
|
self._create_table_if_not_exists()
|
|
163
190
|
self._create_keyword_index_if_not_exists()
|
|
164
191
|
|
|
165
|
-
if search_strategy == "hnsw":
|
|
192
|
+
if self.search_strategy == "hnsw":
|
|
166
193
|
self._handle_hnsw()
|
|
167
194
|
|
|
195
|
+
return self._connection
|
|
196
|
+
|
|
168
197
|
def to_dict(self) -> Dict[str, Any]:
|
|
169
198
|
"""
|
|
170
199
|
Serializes the component to a dictionary.
|
|
@@ -182,7 +211,9 @@ class PgvectorDocumentStore:
|
|
|
182
211
|
search_strategy=self.search_strategy,
|
|
183
212
|
hnsw_recreate_index_if_exists=self.hnsw_recreate_index_if_exists,
|
|
184
213
|
hnsw_index_creation_kwargs=self.hnsw_index_creation_kwargs,
|
|
214
|
+
hnsw_index_name=self.hnsw_index_name,
|
|
185
215
|
hnsw_ef_search=self.hnsw_ef_search,
|
|
216
|
+
keyword_index_name=self.keyword_index_name,
|
|
186
217
|
language=self.language,
|
|
187
218
|
)
|
|
188
219
|
|
|
@@ -208,11 +239,11 @@ class PgvectorDocumentStore:
|
|
|
208
239
|
:param sql_query: The SQL query to execute.
|
|
209
240
|
:param params: The parameters to pass to the SQL query.
|
|
210
241
|
:param error_msg: The error message to use if an exception is raised.
|
|
211
|
-
:param cursor: The cursor to use to execute the SQL query. Defaults to self.
|
|
242
|
+
:param cursor: The cursor to use to execute the SQL query. Defaults to self.cursor.
|
|
212
243
|
"""
|
|
213
244
|
|
|
214
245
|
params = params or ()
|
|
215
|
-
cursor = cursor or self.
|
|
246
|
+
cursor = cursor or self.cursor
|
|
216
247
|
|
|
217
248
|
sql_query_str = sql_query.as_string(cursor) if not isinstance(sql_query, str) else sql_query
|
|
218
249
|
logger.debug("SQL query: %s\nParameters: %s", sql_query_str, params)
|
|
@@ -220,7 +251,7 @@ class PgvectorDocumentStore:
|
|
|
220
251
|
try:
|
|
221
252
|
result = cursor.execute(sql_query, params)
|
|
222
253
|
except Error as e:
|
|
223
|
-
self.
|
|
254
|
+
self.connection.rollback()
|
|
224
255
|
detailed_error_msg = f"{error_msg}.\nYou can find the SQL query and the parameters in the debug logs."
|
|
225
256
|
raise DocumentStoreError(detailed_error_msg) from e
|
|
226
257
|
|
|
@@ -254,7 +285,7 @@ class PgvectorDocumentStore:
|
|
|
254
285
|
index_exists = bool(
|
|
255
286
|
self._execute_sql(
|
|
256
287
|
"SELECT 1 FROM pg_indexes WHERE tablename = %s AND indexname = %s",
|
|
257
|
-
(self.table_name,
|
|
288
|
+
(self.table_name, self.keyword_index_name),
|
|
258
289
|
"Could not check if keyword index exists",
|
|
259
290
|
).fetchone()
|
|
260
291
|
)
|
|
@@ -262,7 +293,7 @@ class PgvectorDocumentStore:
|
|
|
262
293
|
sql_create_index = SQL(
|
|
263
294
|
"CREATE INDEX {index_name} ON {table_name} USING GIN (to_tsvector({language}, content))"
|
|
264
295
|
).format(
|
|
265
|
-
index_name=Identifier(
|
|
296
|
+
index_name=Identifier(self.keyword_index_name),
|
|
266
297
|
table_name=Identifier(self.table_name),
|
|
267
298
|
language=SQLLiteral(self.language),
|
|
268
299
|
)
|
|
@@ -285,7 +316,7 @@ class PgvectorDocumentStore:
|
|
|
285
316
|
index_exists = bool(
|
|
286
317
|
self._execute_sql(
|
|
287
318
|
"SELECT 1 FROM pg_indexes WHERE tablename = %s AND indexname = %s",
|
|
288
|
-
(self.table_name,
|
|
319
|
+
(self.table_name, self.hnsw_index_name),
|
|
289
320
|
"Could not check if HNSW index exists",
|
|
290
321
|
).fetchone()
|
|
291
322
|
)
|
|
@@ -298,7 +329,7 @@ class PgvectorDocumentStore:
|
|
|
298
329
|
)
|
|
299
330
|
return
|
|
300
331
|
|
|
301
|
-
sql_drop_index = SQL("DROP INDEX IF EXISTS {index_name}").format(index_name=Identifier(
|
|
332
|
+
sql_drop_index = SQL("DROP INDEX IF EXISTS {index_name}").format(index_name=Identifier(self.hnsw_index_name))
|
|
302
333
|
self._execute_sql(sql_drop_index, error_msg="Could not drop HNSW index")
|
|
303
334
|
|
|
304
335
|
self._create_hnsw_index()
|
|
@@ -316,7 +347,7 @@ class PgvectorDocumentStore:
|
|
|
316
347
|
}
|
|
317
348
|
|
|
318
349
|
sql_create_index = SQL("CREATE INDEX {index_name} ON {table_name} USING hnsw (embedding {ops}) ").format(
|
|
319
|
-
index_name=Identifier(
|
|
350
|
+
index_name=Identifier(self.hnsw_index_name), table_name=Identifier(self.table_name), ops=SQL(pg_ops)
|
|
320
351
|
)
|
|
321
352
|
|
|
322
353
|
if actual_hnsw_index_creation_kwargs:
|
|
@@ -371,7 +402,7 @@ class PgvectorDocumentStore:
|
|
|
371
402
|
sql_filter,
|
|
372
403
|
params,
|
|
373
404
|
error_msg="Could not filter documents from PgvectorDocumentStore.",
|
|
374
|
-
cursor=self.
|
|
405
|
+
cursor=self.dict_cursor,
|
|
375
406
|
)
|
|
376
407
|
|
|
377
408
|
records = result.fetchall()
|
|
@@ -408,16 +439,16 @@ class PgvectorDocumentStore:
|
|
|
408
439
|
|
|
409
440
|
sql_insert += SQL(" RETURNING id")
|
|
410
441
|
|
|
411
|
-
sql_query_str = sql_insert.as_string(self.
|
|
442
|
+
sql_query_str = sql_insert.as_string(self.cursor) if not isinstance(sql_insert, str) else sql_insert
|
|
412
443
|
logger.debug("SQL query: %s\nParameters: %s", sql_query_str, db_documents)
|
|
413
444
|
|
|
414
445
|
try:
|
|
415
|
-
self.
|
|
446
|
+
self.cursor.executemany(sql_insert, db_documents, returning=True)
|
|
416
447
|
except IntegrityError as ie:
|
|
417
|
-
self.
|
|
448
|
+
self.connection.rollback()
|
|
418
449
|
raise DuplicateDocumentError from ie
|
|
419
450
|
except Error as e:
|
|
420
|
-
self.
|
|
451
|
+
self.connection.rollback()
|
|
421
452
|
error_msg = (
|
|
422
453
|
"Could not write documents to PgvectorDocumentStore. \n"
|
|
423
454
|
"You can find the SQL query and the parameters in the debug logs."
|
|
@@ -428,9 +459,9 @@ class PgvectorDocumentStore:
|
|
|
428
459
|
# https://www.psycopg.org/psycopg3/docs/api/cursors.html#psycopg.Cursor.executemany
|
|
429
460
|
written_docs = 0
|
|
430
461
|
while True:
|
|
431
|
-
if self.
|
|
462
|
+
if self.cursor.fetchone():
|
|
432
463
|
written_docs += 1
|
|
433
|
-
if not self.
|
|
464
|
+
if not self.cursor.nextset():
|
|
434
465
|
break
|
|
435
466
|
|
|
436
467
|
return written_docs
|
|
@@ -483,8 +514,8 @@ class PgvectorDocumentStore:
|
|
|
483
514
|
|
|
484
515
|
# postgresql returns the embedding as a string
|
|
485
516
|
# so we need to convert it to a list of floats
|
|
486
|
-
if document.get("embedding"):
|
|
487
|
-
haystack_dict["embedding"] =
|
|
517
|
+
if document.get("embedding") is not None:
|
|
518
|
+
haystack_dict["embedding"] = document["embedding"].tolist()
|
|
488
519
|
|
|
489
520
|
haystack_document = Document.from_dict(haystack_dict)
|
|
490
521
|
|
|
@@ -555,7 +586,7 @@ class PgvectorDocumentStore:
|
|
|
555
586
|
sql_query,
|
|
556
587
|
(query, *where_params),
|
|
557
588
|
error_msg="Could not retrieve documents from PgvectorDocumentStore.",
|
|
558
|
-
cursor=self.
|
|
589
|
+
cursor=self.dict_cursor,
|
|
559
590
|
)
|
|
560
591
|
|
|
561
592
|
records = result.fetchall()
|
|
@@ -633,7 +664,7 @@ class PgvectorDocumentStore:
|
|
|
633
664
|
sql_query,
|
|
634
665
|
params,
|
|
635
666
|
error_msg="Could not retrieve documents from PgvectorDocumentStore.",
|
|
636
|
-
cursor=self.
|
|
667
|
+
cursor=self.dict_cursor,
|
|
637
668
|
)
|
|
638
669
|
|
|
639
670
|
records = result.fetchall()
|
|
@@ -40,7 +40,6 @@ def patches_for_unit_tests():
|
|
|
40
40
|
) as mock_create_kw_index, patch(
|
|
41
41
|
"haystack_integrations.document_stores.pgvector.document_store.PgvectorDocumentStore._handle_hnsw"
|
|
42
42
|
) as mock_hnsw:
|
|
43
|
-
|
|
44
43
|
yield mock_connect, mock_register, mock_delete, mock_create, mock_create_kw_index, mock_hnsw
|
|
45
44
|
|
|
46
45
|
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
from unittest.mock import patch
|
|
6
6
|
|
|
7
|
+
import numpy as np
|
|
7
8
|
import pytest
|
|
8
9
|
from haystack.dataclasses.document import ByteStream, Document
|
|
9
10
|
from haystack.document_stores.errors import DuplicateDocumentError
|
|
10
11
|
from haystack.document_stores.types import DuplicatePolicy
|
|
11
12
|
from haystack.testing.document_store import CountDocumentsTest, DeleteDocumentsTest, WriteDocumentsTest
|
|
13
|
+
from haystack.utils import Secret
|
|
12
14
|
from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
|
|
13
15
|
from pandas import DataFrame
|
|
14
16
|
|
|
@@ -51,7 +53,9 @@ def test_init(monkeypatch):
|
|
|
51
53
|
search_strategy="hnsw",
|
|
52
54
|
hnsw_recreate_index_if_exists=True,
|
|
53
55
|
hnsw_index_creation_kwargs={"m": 32, "ef_construction": 128},
|
|
56
|
+
hnsw_index_name="my_hnsw_index",
|
|
54
57
|
hnsw_ef_search=50,
|
|
58
|
+
keyword_index_name="my_keyword_index",
|
|
55
59
|
)
|
|
56
60
|
|
|
57
61
|
assert document_store.table_name == "my_table"
|
|
@@ -61,7 +65,9 @@ def test_init(monkeypatch):
|
|
|
61
65
|
assert document_store.search_strategy == "hnsw"
|
|
62
66
|
assert document_store.hnsw_recreate_index_if_exists
|
|
63
67
|
assert document_store.hnsw_index_creation_kwargs == {"m": 32, "ef_construction": 128}
|
|
68
|
+
assert document_store.hnsw_index_name == "my_hnsw_index"
|
|
64
69
|
assert document_store.hnsw_ef_search == 50
|
|
70
|
+
assert document_store.keyword_index_name == "my_keyword_index"
|
|
65
71
|
|
|
66
72
|
|
|
67
73
|
@pytest.mark.usefixtures("patches_for_unit_tests")
|
|
@@ -76,7 +82,9 @@ def test_to_dict(monkeypatch):
|
|
|
76
82
|
search_strategy="hnsw",
|
|
77
83
|
hnsw_recreate_index_if_exists=True,
|
|
78
84
|
hnsw_index_creation_kwargs={"m": 32, "ef_construction": 128},
|
|
85
|
+
hnsw_index_name="my_hnsw_index",
|
|
79
86
|
hnsw_ef_search=50,
|
|
87
|
+
keyword_index_name="my_keyword_index",
|
|
80
88
|
)
|
|
81
89
|
|
|
82
90
|
assert document_store.to_dict() == {
|
|
@@ -91,7 +99,9 @@ def test_to_dict(monkeypatch):
|
|
|
91
99
|
"hnsw_recreate_index_if_exists": True,
|
|
92
100
|
"language": "english",
|
|
93
101
|
"hnsw_index_creation_kwargs": {"m": 32, "ef_construction": 128},
|
|
102
|
+
"hnsw_index_name": "my_hnsw_index",
|
|
94
103
|
"hnsw_ef_search": 50,
|
|
104
|
+
"keyword_index_name": "my_keyword_index",
|
|
95
105
|
},
|
|
96
106
|
}
|
|
97
107
|
|
|
@@ -170,7 +180,7 @@ def test_from_pg_to_haystack_documents():
|
|
|
170
180
|
"blob_meta": None,
|
|
171
181
|
"blob_mime_type": None,
|
|
172
182
|
"meta": {"meta_key": "meta_value"},
|
|
173
|
-
"embedding":
|
|
183
|
+
"embedding": np.array([0.1, 0.2, 0.3]),
|
|
174
184
|
},
|
|
175
185
|
{
|
|
176
186
|
"id": "2",
|
|
@@ -180,7 +190,7 @@ def test_from_pg_to_haystack_documents():
|
|
|
180
190
|
"blob_meta": None,
|
|
181
191
|
"blob_mime_type": None,
|
|
182
192
|
"meta": {"meta_key": "meta_value"},
|
|
183
|
-
"embedding":
|
|
193
|
+
"embedding": np.array([0.4, 0.5, 0.6]),
|
|
184
194
|
},
|
|
185
195
|
{
|
|
186
196
|
"id": "3",
|
|
@@ -190,16 +200,11 @@ def test_from_pg_to_haystack_documents():
|
|
|
190
200
|
"blob_meta": {"blob_meta_key": "blob_meta_value"},
|
|
191
201
|
"blob_mime_type": "mime_type",
|
|
192
202
|
"meta": {"meta_key": "meta_value"},
|
|
193
|
-
"embedding":
|
|
203
|
+
"embedding": np.array([0.7, 0.8, 0.9]),
|
|
194
204
|
},
|
|
195
205
|
]
|
|
196
206
|
|
|
197
|
-
|
|
198
|
-
"haystack_integrations.document_stores.pgvector.document_store.PgvectorDocumentStore.__init__"
|
|
199
|
-
) as mock_init:
|
|
200
|
-
mock_init.return_value = None
|
|
201
|
-
ds = PgvectorDocumentStore(connection_string="test")
|
|
202
|
-
|
|
207
|
+
ds = PgvectorDocumentStore(connection_string=Secret.from_token("test"))
|
|
203
208
|
haystack_docs = ds._from_pg_to_haystack_documents(pg_docs)
|
|
204
209
|
|
|
205
210
|
assert haystack_docs[0].id == "1"
|
|
@@ -48,7 +48,9 @@ class TestEmbeddingRetriever:
|
|
|
48
48
|
"hnsw_recreate_index_if_exists": False,
|
|
49
49
|
"language": "english",
|
|
50
50
|
"hnsw_index_creation_kwargs": {},
|
|
51
|
+
"hnsw_index_name": "haystack_hnsw_index",
|
|
51
52
|
"hnsw_ef_search": None,
|
|
53
|
+
"keyword_index_name": "haystack_keyword_index",
|
|
52
54
|
},
|
|
53
55
|
},
|
|
54
56
|
"filters": {"field": "value"},
|
|
@@ -75,7 +77,9 @@ class TestEmbeddingRetriever:
|
|
|
75
77
|
"search_strategy": "exact_nearest_neighbor",
|
|
76
78
|
"hnsw_recreate_index_if_exists": False,
|
|
77
79
|
"hnsw_index_creation_kwargs": {},
|
|
80
|
+
"hnsw_index_name": "haystack_hnsw_index",
|
|
78
81
|
"hnsw_ef_search": None,
|
|
82
|
+
"keyword_index_name": "haystack_keyword_index",
|
|
79
83
|
},
|
|
80
84
|
},
|
|
81
85
|
"filters": {"field": "value"},
|
|
@@ -96,7 +100,9 @@ class TestEmbeddingRetriever:
|
|
|
96
100
|
assert document_store.search_strategy == "exact_nearest_neighbor"
|
|
97
101
|
assert not document_store.hnsw_recreate_index_if_exists
|
|
98
102
|
assert document_store.hnsw_index_creation_kwargs == {}
|
|
103
|
+
assert document_store.hnsw_index_name == "haystack_hnsw_index"
|
|
99
104
|
assert document_store.hnsw_ef_search is None
|
|
105
|
+
assert document_store.keyword_index_name == "haystack_keyword_index"
|
|
100
106
|
|
|
101
107
|
assert retriever.filters == {"field": "value"}
|
|
102
108
|
assert retriever.top_k == 5
|
|
@@ -149,7 +155,9 @@ class TestKeywordRetriever:
|
|
|
149
155
|
"hnsw_recreate_index_if_exists": False,
|
|
150
156
|
"language": "english",
|
|
151
157
|
"hnsw_index_creation_kwargs": {},
|
|
158
|
+
"hnsw_index_name": "haystack_hnsw_index",
|
|
152
159
|
"hnsw_ef_search": None,
|
|
160
|
+
"keyword_index_name": "haystack_keyword_index",
|
|
153
161
|
},
|
|
154
162
|
},
|
|
155
163
|
"filters": {"field": "value"},
|
|
@@ -175,7 +183,9 @@ class TestKeywordRetriever:
|
|
|
175
183
|
"search_strategy": "exact_nearest_neighbor",
|
|
176
184
|
"hnsw_recreate_index_if_exists": False,
|
|
177
185
|
"hnsw_index_creation_kwargs": {},
|
|
186
|
+
"hnsw_index_name": "haystack_hnsw_index",
|
|
178
187
|
"hnsw_ef_search": None,
|
|
188
|
+
"keyword_index_name": "haystack_keyword_index",
|
|
179
189
|
},
|
|
180
190
|
},
|
|
181
191
|
"filters": {"field": "value"},
|
|
@@ -195,7 +205,9 @@ class TestKeywordRetriever:
|
|
|
195
205
|
assert document_store.search_strategy == "exact_nearest_neighbor"
|
|
196
206
|
assert not document_store.hnsw_recreate_index_if_exists
|
|
197
207
|
assert document_store.hnsw_index_creation_kwargs == {}
|
|
208
|
+
assert document_store.hnsw_index_name == "haystack_hnsw_index"
|
|
198
209
|
assert document_store.hnsw_ef_search is None
|
|
210
|
+
assert document_store.keyword_index_name == "haystack_keyword_index"
|
|
199
211
|
|
|
200
212
|
assert retriever.filters == {"field": "value"}
|
|
201
213
|
assert retriever.top_k == 5
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|