pgvector-haystack 1.0.0__tar.gz → 1.1.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-1.0.0 → pgvector_haystack-1.1.0}/CHANGELOG.md +6 -1
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/PKG-INFO +2 -3
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/pyproject.toml +11 -7
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/src/haystack_integrations/document_stores/pgvector/document_store.py +95 -27
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/tests/test_document_store.py +22 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/tests/test_retrievers.py +2 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/.gitignore +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/LICENSE.txt +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/README.md +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/examples/embedding_retrieval.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/examples/hybrid_retrieval.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/pydoc/config.yml +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/src/haystack_integrations/components/retrievers/pgvector/__init__.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/src/haystack_integrations/components/retrievers/pgvector/embedding_retriever.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/src/haystack_integrations/components/retrievers/pgvector/keyword_retriever.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/src/haystack_integrations/document_stores/pgvector/__init__.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/src/haystack_integrations/document_stores/pgvector/filters.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/tests/__init__.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/tests/conftest.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/tests/test_embedding_retrieval.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/tests/test_filters.py +0 -0
- {pgvector_haystack-1.0.0 → pgvector_haystack-1.1.0}/tests/test_keyword_retrieval.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [integrations/pgvector-v1.0.0] - 2024-09-12
|
|
4
4
|
|
|
5
5
|
### 🚀 Features
|
|
6
6
|
|
|
@@ -10,10 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
- `PgVector` - Fallback to default filter policy when deserializing retrievers without the init parameter (#900)
|
|
12
12
|
|
|
13
|
+
### 🧪 Testing
|
|
14
|
+
|
|
15
|
+
- Do not retry tests in `hatch run test` command (#954)
|
|
16
|
+
|
|
13
17
|
### ⚙️ Miscellaneous Tasks
|
|
14
18
|
|
|
15
19
|
- Retry tests to reduce flakyness (#836)
|
|
16
20
|
- Update ruff invocation to include check parameter (#853)
|
|
21
|
+
- PgVector - remove legacy filter support (#1068)
|
|
17
22
|
|
|
18
23
|
## [integrations/pgvector-v0.4.0] - 2024-06-20
|
|
19
24
|
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pgvector-haystack
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.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
|
|
7
7
|
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
8
8
|
Author-email: deepset GmbH <info@deepset.ai>
|
|
9
|
-
License
|
|
10
|
-
License-File: LICENSE.txt
|
|
9
|
+
License: Apache-2.0
|
|
11
10
|
Classifier: Development Status :: 4 - Beta
|
|
12
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
12
|
Classifier: Programming Language :: Python
|
|
@@ -42,6 +42,7 @@ root = "../.."
|
|
|
42
42
|
git_describe_command = 'git describe --tags --match="integrations/pgvector-v[0-9]*"'
|
|
43
43
|
|
|
44
44
|
[tool.hatch.envs.default]
|
|
45
|
+
installer = "uv"
|
|
45
46
|
dependencies = [
|
|
46
47
|
"coverage[toml]>=6.5",
|
|
47
48
|
"pytest",
|
|
@@ -62,12 +63,13 @@ docs = ["pydoc-markdown pydoc/config.yml"]
|
|
|
62
63
|
python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
|
63
64
|
|
|
64
65
|
[tool.hatch.envs.lint]
|
|
66
|
+
installer = "uv"
|
|
65
67
|
detached = true
|
|
66
|
-
dependencies = ["black>=23.1.0", "mypy>=1.0.0", "ruff>=0.0.243"]
|
|
68
|
+
dependencies = ["pip", "black>=23.1.0", "mypy>=1.0.0", "ruff>=0.0.243"]
|
|
67
69
|
[tool.hatch.envs.lint.scripts]
|
|
68
70
|
typing = "mypy --install-types --non-interactive --explicit-package-bases {args:src/ tests}"
|
|
69
|
-
style = ["ruff check {args:.
|
|
70
|
-
fmt = ["black {args:.}", "ruff --fix {args:.
|
|
71
|
+
style = ["ruff check {args:.}", "black --check --diff {args:.}"]
|
|
72
|
+
fmt = ["black {args:.}", "ruff check --fix {args:.}", "style"]
|
|
71
73
|
all = ["style", "typing"]
|
|
72
74
|
|
|
73
75
|
[tool.black]
|
|
@@ -78,6 +80,8 @@ skip-string-normalization = true
|
|
|
78
80
|
[tool.ruff]
|
|
79
81
|
target-version = "py38"
|
|
80
82
|
line-length = 120
|
|
83
|
+
|
|
84
|
+
[tool.ruff.lint]
|
|
81
85
|
select = [
|
|
82
86
|
"A",
|
|
83
87
|
"ARG",
|
|
@@ -128,13 +132,13 @@ unfixable = [
|
|
|
128
132
|
"F401",
|
|
129
133
|
]
|
|
130
134
|
|
|
131
|
-
[tool.ruff.isort]
|
|
132
|
-
known-first-party = ["
|
|
135
|
+
[tool.ruff.lint.isort]
|
|
136
|
+
known-first-party = ["haystack_integrations"]
|
|
133
137
|
|
|
134
|
-
[tool.ruff.flake8-tidy-imports]
|
|
138
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
135
139
|
ban-relative-imports = "parents"
|
|
136
140
|
|
|
137
|
-
[tool.ruff.per-file-ignores]
|
|
141
|
+
[tool.ruff.lint.per-file-ignores]
|
|
138
142
|
# Tests can use magic values, assertions, and relative imports
|
|
139
143
|
"tests/**/*" = ["PLR2004", "S101", "TID252"]
|
|
140
144
|
# examples can contain "print" commands
|
|
@@ -24,7 +24,7 @@ from .filters import _convert_filters_to_where_clause_and_params
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
25
25
|
|
|
26
26
|
CREATE_TABLE_STATEMENT = """
|
|
27
|
-
CREATE TABLE IF NOT EXISTS {table_name} (
|
|
27
|
+
CREATE TABLE IF NOT EXISTS {schema_name}.{table_name} (
|
|
28
28
|
id VARCHAR(128) PRIMARY KEY,
|
|
29
29
|
embedding VECTOR({embedding_dimension}),
|
|
30
30
|
content TEXT,
|
|
@@ -36,7 +36,7 @@ meta JSONB)
|
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
38
|
INSERT_STATEMENT = """
|
|
39
|
-
INSERT INTO {table_name}
|
|
39
|
+
INSERT INTO {schema_name}.{table_name}
|
|
40
40
|
(id, embedding, content, dataframe, blob_data, blob_meta, blob_mime_type, meta)
|
|
41
41
|
VALUES (%(id)s, %(embedding)s, %(content)s, %(dataframe)s, %(blob_data)s, %(blob_meta)s, %(blob_mime_type)s, %(meta)s)
|
|
42
42
|
"""
|
|
@@ -54,7 +54,7 @@ meta = EXCLUDED.meta
|
|
|
54
54
|
|
|
55
55
|
KEYWORD_QUERY = """
|
|
56
56
|
SELECT {table_name}.*, ts_rank_cd(to_tsvector({language}, content), query) AS score
|
|
57
|
-
FROM {table_name}, plainto_tsquery({language}, %s) query
|
|
57
|
+
FROM {schema_name}.{table_name}, plainto_tsquery({language}, %s) query
|
|
58
58
|
WHERE to_tsvector({language}, content) @@ query
|
|
59
59
|
"""
|
|
60
60
|
|
|
@@ -78,6 +78,7 @@ class PgvectorDocumentStore:
|
|
|
78
78
|
self,
|
|
79
79
|
*,
|
|
80
80
|
connection_string: Secret = Secret.from_env_var("PG_CONN_STR"),
|
|
81
|
+
schema_name: str = "public",
|
|
81
82
|
table_name: str = "haystack_documents",
|
|
82
83
|
language: str = "english",
|
|
83
84
|
embedding_dimension: int = 768,
|
|
@@ -96,7 +97,12 @@ class PgvectorDocumentStore:
|
|
|
96
97
|
A specific table to store Haystack documents will be created if it doesn't exist yet.
|
|
97
98
|
|
|
98
99
|
:param connection_string: The connection string to use to connect to the PostgreSQL database, defined as an
|
|
99
|
-
environment variable
|
|
100
|
+
environment variable. It can be provided in either URI format
|
|
101
|
+
e.g.: `PG_CONN_STR="postgresql://USER:PASSWORD@HOST:PORT/DB_NAME"`, or keyword/value format
|
|
102
|
+
e.g.: `PG_CONN_STR="host=HOST port=PORT dbname=DBNAME user=USER password=PASSWORD"`
|
|
103
|
+
See [PostgreSQL Documentation](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)
|
|
104
|
+
for more details.
|
|
105
|
+
:param schema_name: The name of the schema the table is created in. The schema must already exist.
|
|
100
106
|
:param table_name: The name of the table to use to store Haystack documents.
|
|
101
107
|
:param language: The language to be used to parse query and document content in keyword retrieval.
|
|
102
108
|
To see the list of available languages, you can run the following SQL query in your PostgreSQL database:
|
|
@@ -133,6 +139,7 @@ class PgvectorDocumentStore:
|
|
|
133
139
|
|
|
134
140
|
self.connection_string = connection_string
|
|
135
141
|
self.table_name = table_name
|
|
142
|
+
self.schema_name = schema_name
|
|
136
143
|
self.embedding_dimension = embedding_dimension
|
|
137
144
|
if vector_function not in VALID_VECTOR_FUNCTIONS:
|
|
138
145
|
msg = f"vector_function must be one of {VALID_VECTOR_FUNCTIONS}, but got {vector_function}"
|
|
@@ -149,29 +156,41 @@ class PgvectorDocumentStore:
|
|
|
149
156
|
self._connection = None
|
|
150
157
|
self._cursor = None
|
|
151
158
|
self._dict_cursor = None
|
|
159
|
+
self._table_initialized = False
|
|
152
160
|
|
|
153
161
|
@property
|
|
154
162
|
def cursor(self):
|
|
155
|
-
if self._cursor is None:
|
|
163
|
+
if self._cursor is None or not self._connection_is_valid(self._connection):
|
|
156
164
|
self._create_connection()
|
|
157
165
|
|
|
158
166
|
return self._cursor
|
|
159
167
|
|
|
160
168
|
@property
|
|
161
169
|
def dict_cursor(self):
|
|
162
|
-
if self._dict_cursor is None:
|
|
170
|
+
if self._dict_cursor is None or not self._connection_is_valid(self._connection):
|
|
163
171
|
self._create_connection()
|
|
164
172
|
|
|
165
173
|
return self._dict_cursor
|
|
166
174
|
|
|
167
175
|
@property
|
|
168
176
|
def connection(self):
|
|
169
|
-
if self._connection is None:
|
|
177
|
+
if self._connection is None or not self._connection_is_valid(self._connection):
|
|
170
178
|
self._create_connection()
|
|
171
179
|
|
|
172
180
|
return self._connection
|
|
173
181
|
|
|
174
182
|
def _create_connection(self):
|
|
183
|
+
"""
|
|
184
|
+
Internal method to create a connection to the PostgreSQL database.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
# close the connection if it already exists
|
|
188
|
+
if self._connection:
|
|
189
|
+
try:
|
|
190
|
+
self._connection.close()
|
|
191
|
+
except Error as e:
|
|
192
|
+
logger.debug("Failed to close connection: %s", str(e))
|
|
193
|
+
|
|
175
194
|
conn_str = self.connection_string.resolve_value() or ""
|
|
176
195
|
connection = connect(conn_str)
|
|
177
196
|
connection.autocommit = True
|
|
@@ -182,16 +201,40 @@ class PgvectorDocumentStore:
|
|
|
182
201
|
self._cursor = self._connection.cursor()
|
|
183
202
|
self._dict_cursor = self._connection.cursor(row_factory=dict_row)
|
|
184
203
|
|
|
185
|
-
|
|
204
|
+
if not self._table_initialized:
|
|
205
|
+
self._initialize_table()
|
|
206
|
+
|
|
207
|
+
return self._connection
|
|
208
|
+
|
|
209
|
+
def _initialize_table(self):
|
|
210
|
+
"""
|
|
211
|
+
Internal method to initialize the table.
|
|
212
|
+
"""
|
|
186
213
|
if self.recreate_table:
|
|
187
214
|
self.delete_table()
|
|
215
|
+
|
|
188
216
|
self._create_table_if_not_exists()
|
|
189
217
|
self._create_keyword_index_if_not_exists()
|
|
190
218
|
|
|
191
219
|
if self.search_strategy == "hnsw":
|
|
192
220
|
self._handle_hnsw()
|
|
193
221
|
|
|
194
|
-
|
|
222
|
+
self._table_initialized = True
|
|
223
|
+
|
|
224
|
+
@staticmethod
|
|
225
|
+
def _connection_is_valid(connection):
|
|
226
|
+
"""
|
|
227
|
+
Internal method to check if the connection is still valid.
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
# implementation inspired to psycopg pool
|
|
231
|
+
# https://github.com/psycopg/psycopg/blob/d38cf7798b0c602ff43dac9f20bbab96237a9c38/psycopg_pool/psycopg_pool/pool.py#L528
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
connection.execute("")
|
|
235
|
+
except Error:
|
|
236
|
+
return False
|
|
237
|
+
return True
|
|
195
238
|
|
|
196
239
|
def to_dict(self) -> Dict[str, Any]:
|
|
197
240
|
"""
|
|
@@ -203,6 +246,7 @@ class PgvectorDocumentStore:
|
|
|
203
246
|
return default_to_dict(
|
|
204
247
|
self,
|
|
205
248
|
connection_string=self.connection_string.to_dict(),
|
|
249
|
+
schema_name=self.schema_name,
|
|
206
250
|
table_name=self.table_name,
|
|
207
251
|
embedding_dimension=self.embedding_dimension,
|
|
208
252
|
vector_function=self.vector_function,
|
|
@@ -262,7 +306,9 @@ class PgvectorDocumentStore:
|
|
|
262
306
|
"""
|
|
263
307
|
|
|
264
308
|
create_sql = SQL(CREATE_TABLE_STATEMENT).format(
|
|
265
|
-
|
|
309
|
+
schema_name=Identifier(self.schema_name),
|
|
310
|
+
table_name=Identifier(self.table_name),
|
|
311
|
+
embedding_dimension=SQLLiteral(self.embedding_dimension),
|
|
266
312
|
)
|
|
267
313
|
|
|
268
314
|
self._execute_sql(create_sql, error_msg="Could not create table in PgvectorDocumentStore")
|
|
@@ -270,12 +316,18 @@ class PgvectorDocumentStore:
|
|
|
270
316
|
def delete_table(self):
|
|
271
317
|
"""
|
|
272
318
|
Deletes the table used to store Haystack documents.
|
|
273
|
-
The name of the
|
|
319
|
+
The name of the schema (`schema_name`) and the name of the table (`table_name`)
|
|
320
|
+
are defined when initializing the `PgvectorDocumentStore`.
|
|
274
321
|
"""
|
|
322
|
+
delete_sql = SQL("DROP TABLE IF EXISTS {schema_name}.{table_name}").format(
|
|
323
|
+
schema_name=Identifier(self.schema_name),
|
|
324
|
+
table_name=Identifier(self.table_name),
|
|
325
|
+
)
|
|
275
326
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
327
|
+
self._execute_sql(
|
|
328
|
+
delete_sql,
|
|
329
|
+
error_msg=f"Could not delete table {self.schema_name}.{self.table_name} in PgvectorDocumentStore",
|
|
330
|
+
)
|
|
279
331
|
|
|
280
332
|
def _create_keyword_index_if_not_exists(self):
|
|
281
333
|
"""
|
|
@@ -283,15 +335,16 @@ class PgvectorDocumentStore:
|
|
|
283
335
|
"""
|
|
284
336
|
index_exists = bool(
|
|
285
337
|
self._execute_sql(
|
|
286
|
-
"SELECT 1 FROM pg_indexes WHERE tablename = %s AND indexname = %s",
|
|
287
|
-
(self.table_name, self.keyword_index_name),
|
|
338
|
+
"SELECT 1 FROM pg_indexes WHERE schemaname = %s AND tablename = %s AND indexname = %s",
|
|
339
|
+
(self.schema_name, self.table_name, self.keyword_index_name),
|
|
288
340
|
"Could not check if keyword index exists",
|
|
289
341
|
).fetchone()
|
|
290
342
|
)
|
|
291
343
|
|
|
292
344
|
sql_create_index = SQL(
|
|
293
|
-
"CREATE INDEX {index_name} ON {table_name} USING GIN (to_tsvector({language}, content))"
|
|
345
|
+
"CREATE INDEX {index_name} ON {schema_name}.{table_name} USING GIN (to_tsvector({language}, content))"
|
|
294
346
|
).format(
|
|
347
|
+
schema_name=Identifier(self.schema_name),
|
|
295
348
|
index_name=Identifier(self.keyword_index_name),
|
|
296
349
|
table_name=Identifier(self.table_name),
|
|
297
350
|
language=SQLLiteral(self.language),
|
|
@@ -314,8 +367,8 @@ class PgvectorDocumentStore:
|
|
|
314
367
|
|
|
315
368
|
index_exists = bool(
|
|
316
369
|
self._execute_sql(
|
|
317
|
-
"SELECT 1 FROM pg_indexes WHERE tablename = %s AND indexname = %s",
|
|
318
|
-
(self.table_name, self.hnsw_index_name),
|
|
370
|
+
"SELECT 1 FROM pg_indexes WHERE schemaname = %s AND tablename = %s AND indexname = %s",
|
|
371
|
+
(self.schema_name, self.table_name, self.hnsw_index_name),
|
|
319
372
|
"Could not check if HNSW index exists",
|
|
320
373
|
).fetchone()
|
|
321
374
|
)
|
|
@@ -345,8 +398,13 @@ class PgvectorDocumentStore:
|
|
|
345
398
|
if key in HNSW_INDEX_CREATION_VALID_KWARGS
|
|
346
399
|
}
|
|
347
400
|
|
|
348
|
-
sql_create_index = SQL(
|
|
349
|
-
index_name
|
|
401
|
+
sql_create_index = SQL(
|
|
402
|
+
"CREATE INDEX {index_name} ON {schema_name}.{table_name} USING hnsw (embedding {ops}) "
|
|
403
|
+
).format(
|
|
404
|
+
schema_name=Identifier(self.schema_name),
|
|
405
|
+
index_name=Identifier(self.hnsw_index_name),
|
|
406
|
+
table_name=Identifier(self.table_name),
|
|
407
|
+
ops=SQL(pg_ops),
|
|
350
408
|
)
|
|
351
409
|
|
|
352
410
|
if actual_hnsw_index_creation_kwargs:
|
|
@@ -365,7 +423,9 @@ class PgvectorDocumentStore:
|
|
|
365
423
|
Returns how many documents are present in the document store.
|
|
366
424
|
"""
|
|
367
425
|
|
|
368
|
-
sql_count = SQL("SELECT COUNT(*) FROM {table_name}").format(
|
|
426
|
+
sql_count = SQL("SELECT COUNT(*) FROM {schema_name}.{table_name}").format(
|
|
427
|
+
schema_name=Identifier(self.schema_name), table_name=Identifier(self.table_name)
|
|
428
|
+
)
|
|
369
429
|
|
|
370
430
|
count = self._execute_sql(sql_count, error_msg="Could not count documents in PgvectorDocumentStore").fetchone()[
|
|
371
431
|
0
|
|
@@ -391,7 +451,9 @@ class PgvectorDocumentStore:
|
|
|
391
451
|
msg = "Invalid filter syntax. See https://docs.haystack.deepset.ai/docs/metadata-filtering for details."
|
|
392
452
|
raise ValueError(msg)
|
|
393
453
|
|
|
394
|
-
sql_filter = SQL("SELECT * FROM {table_name}").format(
|
|
454
|
+
sql_filter = SQL("SELECT * FROM {schema_name}.{table_name}").format(
|
|
455
|
+
schema_name=Identifier(self.schema_name), table_name=Identifier(self.table_name)
|
|
456
|
+
)
|
|
395
457
|
|
|
396
458
|
params = ()
|
|
397
459
|
if filters:
|
|
@@ -430,7 +492,9 @@ class PgvectorDocumentStore:
|
|
|
430
492
|
|
|
431
493
|
db_documents = self._from_haystack_to_pg_documents(documents)
|
|
432
494
|
|
|
433
|
-
sql_insert = SQL(INSERT_STATEMENT).format(
|
|
495
|
+
sql_insert = SQL(INSERT_STATEMENT).format(
|
|
496
|
+
schema_name=Identifier(self.schema_name), table_name=Identifier(self.table_name)
|
|
497
|
+
)
|
|
434
498
|
|
|
435
499
|
if policy == DuplicatePolicy.OVERWRITE:
|
|
436
500
|
sql_insert += SQL(UPDATE_STATEMENT)
|
|
@@ -539,8 +603,10 @@ class PgvectorDocumentStore:
|
|
|
539
603
|
|
|
540
604
|
document_ids_str = ", ".join(f"'{document_id}'" for document_id in document_ids)
|
|
541
605
|
|
|
542
|
-
delete_sql = SQL("DELETE FROM {table_name} WHERE id IN ({document_ids_str})").format(
|
|
543
|
-
|
|
606
|
+
delete_sql = SQL("DELETE FROM {schema_name}.{table_name} WHERE id IN ({document_ids_str})").format(
|
|
607
|
+
schema_name=Identifier(self.schema_name),
|
|
608
|
+
table_name=Identifier(self.table_name),
|
|
609
|
+
document_ids_str=SQL(document_ids_str),
|
|
544
610
|
)
|
|
545
611
|
|
|
546
612
|
self._execute_sql(delete_sql, error_msg="Could not delete documents from PgvectorDocumentStore")
|
|
@@ -566,6 +632,7 @@ class PgvectorDocumentStore:
|
|
|
566
632
|
raise ValueError(msg)
|
|
567
633
|
|
|
568
634
|
sql_select = SQL(KEYWORD_QUERY).format(
|
|
635
|
+
schema_name=Identifier(self.schema_name),
|
|
569
636
|
table_name=Identifier(self.table_name),
|
|
570
637
|
language=SQLLiteral(self.language),
|
|
571
638
|
query=SQLLiteral(query),
|
|
@@ -639,7 +706,8 @@ class PgvectorDocumentStore:
|
|
|
639
706
|
elif vector_function == "l2_distance":
|
|
640
707
|
score_definition = f"embedding <-> {query_embedding_for_postgres} AS score"
|
|
641
708
|
|
|
642
|
-
sql_select = SQL("SELECT *, {score} FROM {table_name}").format(
|
|
709
|
+
sql_select = SQL("SELECT *, {score} FROM {schema_name}.{table_name}").format(
|
|
710
|
+
schema_name=Identifier(self.schema_name),
|
|
643
711
|
table_name=Identifier(self.table_name),
|
|
644
712
|
score=SQL(score_definition),
|
|
645
713
|
)
|
|
@@ -41,12 +41,32 @@ class TestDocumentStore(CountDocumentsTest, WriteDocumentsTest, DeleteDocumentsT
|
|
|
41
41
|
retrieved_docs = document_store.filter_documents()
|
|
42
42
|
assert retrieved_docs == docs
|
|
43
43
|
|
|
44
|
+
def test_connection_check_and_recreation(self, document_store: PgvectorDocumentStore):
|
|
45
|
+
original_connection = document_store.connection
|
|
46
|
+
|
|
47
|
+
with patch.object(PgvectorDocumentStore, "_connection_is_valid", return_value=False):
|
|
48
|
+
new_connection = document_store.connection
|
|
49
|
+
|
|
50
|
+
# verify that a new connection is created
|
|
51
|
+
assert new_connection is not original_connection
|
|
52
|
+
assert document_store._connection == new_connection
|
|
53
|
+
assert original_connection.closed
|
|
54
|
+
|
|
55
|
+
assert document_store._cursor is not None
|
|
56
|
+
assert document_store._dict_cursor is not None
|
|
57
|
+
|
|
58
|
+
# test with new connection
|
|
59
|
+
with patch.object(PgvectorDocumentStore, "_connection_is_valid", return_value=True):
|
|
60
|
+
same_connection = document_store.connection
|
|
61
|
+
assert same_connection is document_store._connection
|
|
62
|
+
|
|
44
63
|
|
|
45
64
|
@pytest.mark.usefixtures("patches_for_unit_tests")
|
|
46
65
|
def test_init(monkeypatch):
|
|
47
66
|
monkeypatch.setenv("PG_CONN_STR", "some_connection_string")
|
|
48
67
|
|
|
49
68
|
document_store = PgvectorDocumentStore(
|
|
69
|
+
schema_name="my_schema",
|
|
50
70
|
table_name="my_table",
|
|
51
71
|
embedding_dimension=512,
|
|
52
72
|
vector_function="l2_distance",
|
|
@@ -59,6 +79,7 @@ def test_init(monkeypatch):
|
|
|
59
79
|
keyword_index_name="my_keyword_index",
|
|
60
80
|
)
|
|
61
81
|
|
|
82
|
+
assert document_store.schema_name == "my_schema"
|
|
62
83
|
assert document_store.table_name == "my_table"
|
|
63
84
|
assert document_store.embedding_dimension == 512
|
|
64
85
|
assert document_store.vector_function == "l2_distance"
|
|
@@ -93,6 +114,7 @@ def test_to_dict(monkeypatch):
|
|
|
93
114
|
"init_parameters": {
|
|
94
115
|
"connection_string": {"env_vars": ["PG_CONN_STR"], "strict": True, "type": "env_var"},
|
|
95
116
|
"table_name": "my_table",
|
|
117
|
+
"schema_name": "public",
|
|
96
118
|
"embedding_dimension": 512,
|
|
97
119
|
"vector_function": "l2_distance",
|
|
98
120
|
"recreate_table": True,
|
|
@@ -50,6 +50,7 @@ class TestEmbeddingRetriever:
|
|
|
50
50
|
"type": "haystack_integrations.document_stores.pgvector.document_store.PgvectorDocumentStore",
|
|
51
51
|
"init_parameters": {
|
|
52
52
|
"connection_string": {"env_vars": ["PG_CONN_STR"], "strict": True, "type": "env_var"},
|
|
53
|
+
"schema_name": "public",
|
|
53
54
|
"table_name": "haystack",
|
|
54
55
|
"embedding_dimension": 768,
|
|
55
56
|
"vector_function": "cosine_similarity",
|
|
@@ -175,6 +176,7 @@ class TestKeywordRetriever:
|
|
|
175
176
|
"type": "haystack_integrations.document_stores.pgvector.document_store.PgvectorDocumentStore",
|
|
176
177
|
"init_parameters": {
|
|
177
178
|
"connection_string": {"env_vars": ["PG_CONN_STR"], "strict": True, "type": "env_var"},
|
|
179
|
+
"schema_name": "public",
|
|
178
180
|
"table_name": "haystack",
|
|
179
181
|
"embedding_dimension": 768,
|
|
180
182
|
"vector_function": "cosine_similarity",
|
|
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
|
|
File without changes
|
|
File without changes
|