pgvector-haystack 1.3.0__tar.gz → 3.0.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.
Files changed (22) hide show
  1. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/CHANGELOG.md +17 -0
  2. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/PKG-INFO +3 -4
  3. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/pyproject.toml +4 -4
  4. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/src/haystack_integrations/document_stores/pgvector/document_store.py +9 -15
  5. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/src/haystack_integrations/document_stores/pgvector/filters.py +0 -5
  6. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/tests/test_document_store.py +7 -23
  7. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/tests/test_filters.py +2 -15
  8. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/.gitignore +0 -0
  9. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/LICENSE.txt +0 -0
  10. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/README.md +0 -0
  11. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/examples/embedding_retrieval.py +0 -0
  12. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/examples/hybrid_retrieval.py +0 -0
  13. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/pydoc/config.yml +0 -0
  14. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/src/haystack_integrations/components/retrievers/pgvector/__init__.py +0 -0
  15. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/src/haystack_integrations/components/retrievers/pgvector/embedding_retriever.py +0 -0
  16. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/src/haystack_integrations/components/retrievers/pgvector/keyword_retriever.py +0 -0
  17. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/src/haystack_integrations/document_stores/pgvector/__init__.py +0 -0
  18. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/tests/__init__.py +0 -0
  19. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/tests/conftest.py +0 -0
  20. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/tests/test_embedding_retrieval.py +0 -0
  21. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/tests/test_keyword_retrieval.py +0 -0
  22. {pgvector_haystack-1.3.0 → pgvector_haystack-3.0.0}/tests/test_retrievers.py +0 -0
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/pgvector-v2.0.0] - 2025-02-13
4
+
5
+ ### 🧹 Chores
6
+
7
+ - Pgvector - remove support for dataframe (#1370)
8
+
9
+ ## [integrations/pgvector-v1.3.0] - 2025-02-03
10
+
11
+ ### 🚀 Features
12
+
13
+ - Pgvector - add like and not like filters (#1341)
14
+
15
+ ### 🧹 Chores
16
+
17
+ - Inherit from `FilterDocumentsTestWithDataframe` in Document Stores (#1290)
18
+
19
+
3
20
  ## [integrations/pgvector-v1.2.1] - 2025-01-10
4
21
 
5
22
  ### 🐛 Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pgvector-haystack
3
- Version: 1.3.0
3
+ Version: 3.0.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
@@ -11,15 +11,14 @@ License-File: LICENSE.txt
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.8
15
14
  Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Programming Language :: Python :: Implementation :: CPython
20
19
  Classifier: Programming Language :: Python :: Implementation :: PyPy
21
- Requires-Python: >=3.8
22
- Requires-Dist: haystack-ai
20
+ Requires-Python: >=3.9
21
+ Requires-Dist: haystack-ai>=2.11.0
23
22
  Requires-Dist: pgvector
24
23
  Requires-Dist: psycopg[binary]
25
24
  Description-Content-Type: text/markdown
@@ -7,7 +7,7 @@ name = "pgvector-haystack"
7
7
  dynamic = ["version"]
8
8
  description = "An integration of pgvector (vector search extension for Postgres) with Haystack"
9
9
  readme = "README.md"
10
- requires-python = ">=3.8"
10
+ requires-python = ">=3.9"
11
11
  license = "Apache-2.0"
12
12
  keywords = []
13
13
  authors = [{ name = "deepset GmbH", email = "info@deepset.ai" }]
@@ -15,7 +15,6 @@ classifiers = [
15
15
  "License :: OSI Approved :: Apache Software License",
16
16
  "Development Status :: 4 - Beta",
17
17
  "Programming Language :: Python",
18
- "Programming Language :: Python :: 3.8",
19
18
  "Programming Language :: Python :: 3.9",
20
19
  "Programming Language :: Python :: 3.10",
21
20
  "Programming Language :: Python :: 3.11",
@@ -23,7 +22,7 @@ classifiers = [
23
22
  "Programming Language :: Python :: Implementation :: CPython",
24
23
  "Programming Language :: Python :: Implementation :: PyPy",
25
24
  ]
26
- dependencies = ["haystack-ai", "pgvector", "psycopg[binary]"]
25
+ dependencies = ["haystack-ai>=2.11.0", "pgvector", "psycopg[binary]"]
27
26
 
28
27
  [project.urls]
29
28
  Source = "https://github.com/deepset-ai/haystack-core-integrations"
@@ -60,7 +59,7 @@ cov-retry = ["test-cov-retry", "cov-report"]
60
59
  docs = ["pydoc-markdown pydoc/config.yml"]
61
60
 
62
61
  [[tool.hatch.envs.all.matrix]]
63
- python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
62
+ python = [ "3.9", "3.10", "3.11", "3.12"]
64
63
 
65
64
  [tool.hatch.envs.lint]
66
65
  installer = "uv"
@@ -163,6 +162,7 @@ markers = ["integration: integration tests"]
163
162
  module = [
164
163
  "haystack.*",
165
164
  "haystack_integrations.*",
165
+ "numpy.*",
166
166
  "pgvector.*",
167
167
  "psycopg.*",
168
168
  "pytest.*",
@@ -1,10 +1,9 @@
1
1
  # SPDX-FileCopyrightText: 2023-present deepset GmbH <info@deepset.ai>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- import logging
5
4
  from typing import Any, Dict, List, Literal, Optional
6
5
 
7
- from haystack import default_from_dict, default_to_dict
6
+ from haystack import default_from_dict, default_to_dict, logging
8
7
  from haystack.dataclasses.document import ByteStream, Document
9
8
  from haystack.document_stores.errors import DocumentStoreError, DuplicateDocumentError
10
9
  from haystack.document_stores.types import DuplicatePolicy
@@ -28,7 +27,6 @@ CREATE TABLE IF NOT EXISTS {schema_name}.{table_name} (
28
27
  id VARCHAR(128) PRIMARY KEY,
29
28
  embedding VECTOR({embedding_dimension}),
30
29
  content TEXT,
31
- dataframe JSONB,
32
30
  blob_data BYTEA,
33
31
  blob_meta JSONB,
34
32
  blob_mime_type VARCHAR(255),
@@ -37,15 +35,14 @@ meta JSONB)
37
35
 
38
36
  INSERT_STATEMENT = """
39
37
  INSERT INTO {schema_name}.{table_name}
40
- (id, embedding, content, dataframe, blob_data, blob_meta, blob_mime_type, meta)
41
- VALUES (%(id)s, %(embedding)s, %(content)s, %(dataframe)s, %(blob_data)s, %(blob_meta)s, %(blob_mime_type)s, %(meta)s)
38
+ (id, embedding, content, blob_data, blob_meta, blob_mime_type, meta)
39
+ VALUES (%(id)s, %(embedding)s, %(content)s, %(blob_data)s, %(blob_meta)s, %(blob_mime_type)s, %(meta)s)
42
40
  """
43
41
 
44
42
  UPDATE_STATEMENT = """
45
43
  ON CONFLICT (id) DO UPDATE SET
46
44
  embedding = EXCLUDED.embedding,
47
45
  content = EXCLUDED.content,
48
- dataframe = EXCLUDED.dataframe,
49
46
  blob_data = EXCLUDED.blob_data,
50
47
  blob_meta = EXCLUDED.blob_meta,
51
48
  blob_mime_type = EXCLUDED.blob_mime_type,
@@ -195,7 +192,7 @@ class PgvectorDocumentStore:
195
192
  try:
196
193
  self._connection.close()
197
194
  except Error as e:
198
- logger.debug("Failed to close connection: %s", str(e))
195
+ logger.debug("Failed to close connection: {e}", e=str(e))
199
196
 
200
197
  conn_str = self.connection_string.resolve_value() or ""
201
198
  connection = connect(conn_str)
@@ -297,7 +294,7 @@ class PgvectorDocumentStore:
297
294
  cursor = cursor or self.cursor
298
295
 
299
296
  sql_query_str = sql_query.as_string(cursor) if not isinstance(sql_query, str) else sql_query
300
- logger.debug("SQL query: %s\nParameters: %s", sql_query_str, params)
297
+ logger.debug("SQL query: {query}\nParameters: {parameters}", query=sql_query_str, parameters=params)
301
298
 
302
299
  try:
303
300
  result = cursor.execute(sql_query, params)
@@ -514,7 +511,7 @@ class PgvectorDocumentStore:
514
511
  sql_insert += SQL(" RETURNING id")
515
512
 
516
513
  sql_query_str = sql_insert.as_string(self.cursor) if not isinstance(sql_insert, str) else sql_insert
517
- logger.debug("SQL query: %s\nParameters: %s", sql_query_str, db_documents)
514
+ logger.debug("SQL query: {query}\nParameters: {parameters}", query=sql_query_str, parameters=db_documents)
518
515
 
519
516
  try:
520
517
  self.cursor.executemany(sql_insert, db_documents, returning=True)
@@ -555,18 +552,16 @@ class PgvectorDocumentStore:
555
552
  db_document["blob_data"] = blob.data if blob else None
556
553
  db_document["blob_meta"] = Jsonb(blob.meta) if blob and blob.meta else None
557
554
  db_document["blob_mime_type"] = blob.mime_type if blob and blob.mime_type else None
558
-
559
- db_document["dataframe"] = Jsonb(db_document["dataframe"]) if db_document["dataframe"] else None
560
555
  db_document["meta"] = Jsonb(db_document["meta"])
561
556
 
562
557
  if "sparse_embedding" in db_document:
563
558
  sparse_embedding = db_document.pop("sparse_embedding", None)
564
559
  if sparse_embedding:
565
560
  logger.warning(
566
- "Document %s has the `sparse_embedding` field set,"
561
+ "Document {doc_id} has the `sparse_embedding` field set,"
567
562
  "but storing sparse embeddings in Pgvector is not currently supported."
568
563
  "The `sparse_embedding` field will be ignored.",
569
- db_document["id"],
564
+ doc_id=db_document["id"],
570
565
  )
571
566
 
572
567
  db_documents.append(db_document)
@@ -586,8 +581,7 @@ class PgvectorDocumentStore:
586
581
  blob_meta = haystack_dict.pop("blob_meta")
587
582
  blob_mime_type = haystack_dict.pop("blob_mime_type")
588
583
 
589
- # postgresql returns the embedding as a string
590
- # so we need to convert it to a list of floats
584
+ # convert the embedding to a list of floats
591
585
  if document.get("embedding") is not None:
592
586
  haystack_dict["embedding"] = document["embedding"].tolist()
593
587
 
@@ -6,7 +6,6 @@ from itertools import chain
6
6
  from typing import Any, Dict, List, Literal, Tuple
7
7
 
8
8
  from haystack.errors import FilterError
9
- from pandas import DataFrame
10
9
  from psycopg.sql import SQL
11
10
  from psycopg.types.json import Jsonb
12
11
 
@@ -93,10 +92,6 @@ def _parse_comparison_condition(condition: Dict[str, Any]) -> Tuple[str, List[An
93
92
  raise FilterError(msg)
94
93
 
95
94
  value: Any = condition["value"]
96
- if isinstance(value, DataFrame):
97
- # DataFrames are stored as JSONB and we query them as such
98
- value = Jsonb(value.to_json())
99
- field = f"({field})::jsonb"
100
95
 
101
96
  if field.startswith("meta."):
102
97
  field = _treat_meta_field(field, value)
@@ -12,7 +12,6 @@ from haystack.document_stores.errors import DuplicateDocumentError
12
12
  from haystack.document_stores.types import DuplicatePolicy
13
13
  from haystack.testing.document_store import CountDocumentsTest, DeleteDocumentsTest, WriteDocumentsTest
14
14
  from haystack.utils import Secret
15
- from pandas import DataFrame
16
15
 
17
16
  from haystack_integrations.document_stores.pgvector import PgvectorDocumentStore
18
17
 
@@ -33,15 +32,6 @@ class TestDocumentStore(CountDocumentsTest, WriteDocumentsTest, DeleteDocumentsT
33
32
  retrieved_docs = document_store.filter_documents()
34
33
  assert retrieved_docs == docs
35
34
 
36
- def test_write_dataframe(self, document_store: PgvectorDocumentStore):
37
- dataframe = DataFrame({"col1": [1, 2], "col2": [3, 4]})
38
- docs = [Document(id="1", dataframe=dataframe)]
39
-
40
- document_store.write_documents(docs)
41
-
42
- retrieved_docs = document_store.filter_documents()
43
- assert retrieved_docs == docs
44
-
45
35
  def test_connection_check_and_recreation(self, document_store: PgvectorDocumentStore):
46
36
  original_connection = document_store.connection
47
37
 
@@ -145,7 +135,7 @@ def test_from_haystack_to_pg_documents():
145
135
  ),
146
136
  Document(
147
137
  id="2",
148
- dataframe=DataFrame({"col1": [1, 2], "col2": [3, 4]}),
138
+ content="This is another text",
149
139
  meta={"meta_key": "meta_value"},
150
140
  embedding=[0.4, 0.5, 0.6],
151
141
  score=0.6,
@@ -169,30 +159,30 @@ def test_from_haystack_to_pg_documents():
169
159
 
170
160
  assert pg_docs[0]["id"] == "1"
171
161
  assert pg_docs[0]["content"] == "This is a text"
172
- assert pg_docs[0]["dataframe"] is None
173
162
  assert pg_docs[0]["blob_data"] is None
174
163
  assert pg_docs[0]["blob_meta"] is None
175
164
  assert pg_docs[0]["blob_mime_type"] is None
165
+ assert "dataframe" not in pg_docs[0]
176
166
  assert pg_docs[0]["meta"].obj == {"meta_key": "meta_value"}
177
167
  assert pg_docs[0]["embedding"] == [0.1, 0.2, 0.3]
178
168
  assert "score" not in pg_docs[0]
179
169
 
180
170
  assert pg_docs[1]["id"] == "2"
181
- assert pg_docs[1]["content"] is None
182
- assert pg_docs[1]["dataframe"].obj == DataFrame({"col1": [1, 2], "col2": [3, 4]}).to_json()
171
+ assert pg_docs[1]["content"] == "This is another text"
183
172
  assert pg_docs[1]["blob_data"] is None
184
173
  assert pg_docs[1]["blob_meta"] is None
185
174
  assert pg_docs[1]["blob_mime_type"] is None
175
+ assert "dataframe" not in pg_docs[1]
186
176
  assert pg_docs[1]["meta"].obj == {"meta_key": "meta_value"}
187
177
  assert pg_docs[1]["embedding"] == [0.4, 0.5, 0.6]
188
178
  assert "score" not in pg_docs[1]
189
179
 
190
180
  assert pg_docs[2]["id"] == "3"
191
181
  assert pg_docs[2]["content"] is None
192
- assert pg_docs[2]["dataframe"] is None
193
182
  assert pg_docs[2]["blob_data"] == b"test"
194
183
  assert pg_docs[2]["blob_meta"].obj == {"blob_meta_key": "blob_meta_value"}
195
184
  assert pg_docs[2]["blob_mime_type"] == "mime_type"
185
+ assert "dataframe" not in pg_docs[2]
196
186
  assert pg_docs[2]["meta"].obj == {"meta_key": "meta_value"}
197
187
  assert pg_docs[2]["embedding"] == [0.7, 0.8, 0.9]
198
188
  assert "score" not in pg_docs[2]
@@ -203,7 +193,6 @@ def test_from_pg_to_haystack_documents():
203
193
  {
204
194
  "id": "1",
205
195
  "content": "This is a text",
206
- "dataframe": None,
207
196
  "blob_data": None,
208
197
  "blob_meta": None,
209
198
  "blob_mime_type": None,
@@ -212,8 +201,7 @@ def test_from_pg_to_haystack_documents():
212
201
  },
213
202
  {
214
203
  "id": "2",
215
- "content": None,
216
- "dataframe": DataFrame({"col1": [1, 2], "col2": [3, 4]}).to_json(),
204
+ "content": "This is another text",
217
205
  "blob_data": None,
218
206
  "blob_meta": None,
219
207
  "blob_mime_type": None,
@@ -223,7 +211,6 @@ def test_from_pg_to_haystack_documents():
223
211
  {
224
212
  "id": "3",
225
213
  "content": None,
226
- "dataframe": None,
227
214
  "blob_data": b"test",
228
215
  "blob_meta": {"blob_meta_key": "blob_meta_value"},
229
216
  "blob_mime_type": "mime_type",
@@ -237,15 +224,13 @@ def test_from_pg_to_haystack_documents():
237
224
 
238
225
  assert haystack_docs[0].id == "1"
239
226
  assert haystack_docs[0].content == "This is a text"
240
- assert haystack_docs[0].dataframe is None
241
227
  assert haystack_docs[0].blob is None
242
228
  assert haystack_docs[0].meta == {"meta_key": "meta_value"}
243
229
  assert haystack_docs[0].embedding == [0.1, 0.2, 0.3]
244
230
  assert haystack_docs[0].score is None
245
231
 
246
232
  assert haystack_docs[1].id == "2"
247
- assert haystack_docs[1].content is None
248
- assert haystack_docs[1].dataframe.equals(DataFrame({"col1": [1, 2], "col2": [3, 4]}))
233
+ assert haystack_docs[1].content == "This is another text"
249
234
  assert haystack_docs[1].blob is None
250
235
  assert haystack_docs[1].meta == {"meta_key": "meta_value"}
251
236
  assert haystack_docs[1].embedding == [0.4, 0.5, 0.6]
@@ -253,7 +238,6 @@ def test_from_pg_to_haystack_documents():
253
238
 
254
239
  assert haystack_docs[2].id == "3"
255
240
  assert haystack_docs[2].content is None
256
- assert haystack_docs[2].dataframe is None
257
241
  assert haystack_docs[2].blob.data == b"test"
258
242
  assert haystack_docs[2].blob.meta == {"blob_meta_key": "blob_meta_value"}
259
243
  assert haystack_docs[2].blob.mime_type == "mime_type"
@@ -2,10 +2,8 @@ from typing import List
2
2
 
3
3
  import pytest
4
4
  from haystack.dataclasses.document import Document
5
- from haystack.testing.document_store import FilterDocumentsTest, FilterDocumentsTestWithDataframe
6
- from pandas import DataFrame
5
+ from haystack.testing.document_store import FilterDocumentsTest
7
6
  from psycopg.sql import SQL
8
- from psycopg.types.json import Jsonb
9
7
 
10
8
  from haystack_integrations.document_stores.pgvector.filters import (
11
9
  FilterError,
@@ -17,7 +15,7 @@ from haystack_integrations.document_stores.pgvector.filters import (
17
15
 
18
16
 
19
17
  @pytest.mark.integration
20
- class TestFilters(FilterDocumentsTest, FilterDocumentsTestWithDataframe):
18
+ class TestFilters(FilterDocumentsTest):
21
19
  def assert_documents_are_equal(self, received: List[Document], expected: List[Document]):
22
20
  """
23
21
  This overrides the default assert_documents_are_equal from FilterDocumentsTest.
@@ -147,17 +145,6 @@ def test_treat_meta_field():
147
145
  assert _treat_meta_field(field="meta.name", value=None) == "meta->>'name'"
148
146
 
149
147
 
150
- def test_comparison_condition_dataframe_jsonb_conversion():
151
- dataframe = DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]})
152
- condition = {"field": "meta.df", "operator": "==", "value": dataframe}
153
- field, values = _parse_comparison_condition(condition)
154
- assert field == "(meta.df)::jsonb = %s"
155
-
156
- # we check each slot of the Jsonb object because it does not implement __eq__
157
- assert values[0].obj == Jsonb(dataframe.to_json()).obj
158
- assert values[0].dumps == Jsonb(dataframe.to_json()).dumps
159
-
160
-
161
148
  def test_comparison_condition_missing_operator():
162
149
  condition = {"field": "meta.type", "value": "article"}
163
150
  with pytest.raises(FilterError):