pgvector-haystack 8.1.0__tar.gz → 8.1.1__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-8.1.0 → pgvector_haystack-8.1.1}/CHANGELOG.md +7 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/PKG-INFO +1 -1
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/document_stores/pgvector/document_store.py +9 -9
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/test_document_store.py +21 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/test_document_store_async.py +21 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/.gitignore +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/LICENSE.txt +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/README.md +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/examples/embedding_retrieval.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/examples/hybrid_retrieval.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/pydoc/config_docusaurus.yml +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/pyproject.toml +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/components/retrievers/pgvector/__init__.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/components/retrievers/pgvector/embedding_retriever.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/components/retrievers/pgvector/keyword_retriever.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/components/retrievers/py.typed +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/document_stores/pgvector/__init__.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/document_stores/pgvector/converters.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/document_stores/pgvector/filters.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/src/haystack_integrations/document_stores/py.typed +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/__init__.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/conftest.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/test_converters.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/test_filters.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/test_retrieval.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/test_retrieval_async.py +0 -0
- {pgvector_haystack-8.1.0 → pgvector_haystack-8.1.1}/tests/test_retrievers.py +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/pgvector-v8.1.0] - 2026-10-20
|
|
4
|
+
|
|
5
|
+
### 🚀 Features
|
|
6
|
+
|
|
7
|
+
- *(pgvector)* Make PgvectorDocumentStore connection overridable for PostgreSQL-backed stores (#4129)
|
|
8
|
+
|
|
9
|
+
|
|
3
10
|
## [integrations/pgvector-v8.0.0] - 2026-10-16
|
|
4
11
|
|
|
5
12
|
### 🧪 Testing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pgvector-haystack
|
|
3
|
-
Version: 8.1.
|
|
3
|
+
Version: 8.1.1
|
|
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
|
|
@@ -1023,18 +1023,19 @@ class PgvectorDocumentStore:
|
|
|
1023
1023
|
if not document_ids:
|
|
1024
1024
|
return
|
|
1025
1025
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
delete_sql = SQL("DELETE FROM {schema_name}.{table_name} WHERE id IN ({document_ids_str})").format(
|
|
1026
|
+
# pass the ids as a bound parameter, so they are never interpreted as SQL
|
|
1027
|
+
delete_sql = SQL("DELETE FROM {schema_name}.{table_name} WHERE id = ANY(%s)").format(
|
|
1029
1028
|
schema_name=Identifier(self.schema_name),
|
|
1030
1029
|
table_name=Identifier(self.table_name),
|
|
1031
|
-
document_ids_str=SQL(document_ids_str),
|
|
1032
1030
|
)
|
|
1033
1031
|
|
|
1034
1032
|
self._ensure_db_setup()
|
|
1035
1033
|
assert self._cursor is not None
|
|
1036
1034
|
self._execute_sql(
|
|
1037
|
-
cursor=self._cursor,
|
|
1035
|
+
cursor=self._cursor,
|
|
1036
|
+
sql_query=delete_sql,
|
|
1037
|
+
params=(document_ids,),
|
|
1038
|
+
error_msg="Could not delete documents from PgvectorDocumentStore",
|
|
1038
1039
|
)
|
|
1039
1040
|
|
|
1040
1041
|
async def delete_documents_async(self, document_ids: list[str]) -> None:
|
|
@@ -1046,12 +1047,10 @@ class PgvectorDocumentStore:
|
|
|
1046
1047
|
if not document_ids:
|
|
1047
1048
|
return
|
|
1048
1049
|
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
delete_sql = SQL("DELETE FROM {schema_name}.{table_name} WHERE id IN ({document_ids_str})").format(
|
|
1050
|
+
# pass the ids as a bound parameter, so they are never interpreted as SQL
|
|
1051
|
+
delete_sql = SQL("DELETE FROM {schema_name}.{table_name} WHERE id = ANY(%s)").format(
|
|
1052
1052
|
schema_name=Identifier(self.schema_name),
|
|
1053
1053
|
table_name=Identifier(self.table_name),
|
|
1054
|
-
document_ids_str=SQL(document_ids_str),
|
|
1055
1054
|
)
|
|
1056
1055
|
|
|
1057
1056
|
await self._ensure_db_setup_async()
|
|
@@ -1059,6 +1058,7 @@ class PgvectorDocumentStore:
|
|
|
1059
1058
|
await self._execute_sql_async(
|
|
1060
1059
|
cursor=self._async_cursor,
|
|
1061
1060
|
sql_query=delete_sql,
|
|
1061
|
+
params=(document_ids,),
|
|
1062
1062
|
error_msg="Could not delete documents from PgvectorDocumentStore",
|
|
1063
1063
|
)
|
|
1064
1064
|
|
|
@@ -605,6 +605,27 @@ def test_delete_documents_empty_list_is_noop(document_store: PgvectorDocumentSto
|
|
|
605
605
|
assert after == before
|
|
606
606
|
|
|
607
607
|
|
|
608
|
+
@pytest.mark.integration
|
|
609
|
+
def test_delete_documents_with_quotes_in_ids(document_store: PgvectorDocumentStore):
|
|
610
|
+
"""
|
|
611
|
+
The ids are passed as a bound parameter, so quotes in them are not interpreted as SQL.
|
|
612
|
+
"""
|
|
613
|
+
docs = [
|
|
614
|
+
Document(id="O'Brien", content="quote in id"),
|
|
615
|
+
Document(id="x') OR ('1'='1", content="injection-shaped id"),
|
|
616
|
+
Document(id="1", content="hello"),
|
|
617
|
+
Document(id="2", content="world"),
|
|
618
|
+
]
|
|
619
|
+
document_store.write_documents(docs)
|
|
620
|
+
|
|
621
|
+
document_store.delete_documents(["O'Brien"])
|
|
622
|
+
assert sorted(doc.id for doc in document_store.filter_documents()) == ["1", "2", "x') OR ('1'='1"]
|
|
623
|
+
|
|
624
|
+
# only matches the document with this exact id, instead of every document
|
|
625
|
+
document_store.delete_documents(["x') OR ('1'='1"])
|
|
626
|
+
assert sorted(doc.id for doc in document_store.filter_documents()) == ["1", "2"]
|
|
627
|
+
|
|
628
|
+
|
|
608
629
|
@pytest.mark.integration
|
|
609
630
|
def test_update_by_filter_empty_meta_raises_error(document_store: PgvectorDocumentStore):
|
|
610
631
|
docs = [Document(content="Doc 1", meta={"category": "A"})]
|
|
@@ -189,6 +189,27 @@ class TestDocumentStoreAsync(
|
|
|
189
189
|
"""
|
|
190
190
|
await document_store.delete_table_async() # if throw error, test fails
|
|
191
191
|
|
|
192
|
+
async def test_delete_documents_async_with_quotes_in_ids(self, document_store: PgvectorDocumentStore):
|
|
193
|
+
"""
|
|
194
|
+
The ids are passed as a bound parameter, so quotes in them are not interpreted as SQL.
|
|
195
|
+
"""
|
|
196
|
+
docs = [
|
|
197
|
+
Document(id="O'Brien", content="quote in id"),
|
|
198
|
+
Document(id="x') OR ('1'='1", content="injection-shaped id"),
|
|
199
|
+
Document(id="1", content="hello"),
|
|
200
|
+
Document(id="2", content="world"),
|
|
201
|
+
]
|
|
202
|
+
await document_store.write_documents_async(docs)
|
|
203
|
+
|
|
204
|
+
await document_store.delete_documents_async(["O'Brien"])
|
|
205
|
+
remaining = await document_store.filter_documents_async()
|
|
206
|
+
assert sorted(doc.id for doc in remaining) == ["1", "2", "x') OR ('1'='1"]
|
|
207
|
+
|
|
208
|
+
# only matches the document with this exact id, instead of every document
|
|
209
|
+
await document_store.delete_documents_async(["x') OR ('1'='1"])
|
|
210
|
+
remaining = await document_store.filter_documents_async()
|
|
211
|
+
assert sorted(doc.id for doc in remaining) == ["1", "2"]
|
|
212
|
+
|
|
192
213
|
async def test_close_async_and_reopen(self, document_store: PgvectorDocumentStore):
|
|
193
214
|
assert await document_store.count_documents_async() == 0
|
|
194
215
|
assert document_store._async_connection is not None
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|