pgvector-haystack 1.3.0__tar.gz → 2.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-2.0.0}/CHANGELOG.md +11 -0
  2. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/PKG-INFO +1 -1
  3. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/pyproject.toml +1 -0
  4. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/src/haystack_integrations/document_stores/pgvector/document_store.py +22 -8
  5. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/src/haystack_integrations/document_stores/pgvector/filters.py +0 -5
  6. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/tests/test_document_store.py +7 -23
  7. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/tests/test_filters.py +2 -15
  8. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/.gitignore +0 -0
  9. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/LICENSE.txt +0 -0
  10. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/README.md +0 -0
  11. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/examples/embedding_retrieval.py +0 -0
  12. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/examples/hybrid_retrieval.py +0 -0
  13. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/pydoc/config.yml +0 -0
  14. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/src/haystack_integrations/components/retrievers/pgvector/__init__.py +0 -0
  15. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/src/haystack_integrations/components/retrievers/pgvector/embedding_retriever.py +0 -0
  16. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/src/haystack_integrations/components/retrievers/pgvector/keyword_retriever.py +0 -0
  17. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/src/haystack_integrations/document_stores/pgvector/__init__.py +0 -0
  18. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/tests/__init__.py +0 -0
  19. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/tests/conftest.py +0 -0
  20. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/tests/test_embedding_retrieval.py +0 -0
  21. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/tests/test_keyword_retrieval.py +0 -0
  22. {pgvector_haystack-1.3.0 → pgvector_haystack-2.0.0}/tests/test_retrievers.py +0 -0
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/pgvector-v1.3.0] - 2025-02-03
4
+
5
+ ### 🚀 Features
6
+
7
+ - Pgvector - add like and not like filters (#1341)
8
+
9
+ ### 🧹 Chores
10
+
11
+ - Inherit from `FilterDocumentsTestWithDataframe` in Document Stores (#1290)
12
+
13
+
3
14
  ## [integrations/pgvector-v1.2.1] - 2025-01-10
4
15
 
5
16
  ### 🐛 Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pgvector-haystack
3
- Version: 1.3.0
3
+ Version: 2.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
@@ -163,6 +163,7 @@ markers = ["integration: integration tests"]
163
163
  module = [
164
164
  "haystack.*",
165
165
  "haystack_integrations.*",
166
+ "numpy.*",
166
167
  "pgvector.*",
167
168
  "psycopg.*",
168
169
  "pytest.*",
@@ -28,7 +28,6 @@ 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,
31
- dataframe JSONB,
32
31
  blob_data BYTEA,
33
32
  blob_meta JSONB,
34
33
  blob_mime_type VARCHAR(255),
@@ -37,15 +36,14 @@ meta JSONB)
37
36
 
38
37
  INSERT_STATEMENT = """
39
38
  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)
39
+ (id, embedding, content, blob_data, blob_meta, blob_mime_type, meta)
40
+ VALUES (%(id)s, %(embedding)s, %(content)s, %(blob_data)s, %(blob_meta)s, %(blob_mime_type)s, %(meta)s)
42
41
  """
43
42
 
44
43
  UPDATE_STATEMENT = """
45
44
  ON CONFLICT (id) DO UPDATE SET
46
45
  embedding = EXCLUDED.embedding,
47
46
  content = EXCLUDED.content,
48
- dataframe = EXCLUDED.dataframe,
49
47
  blob_data = EXCLUDED.blob_data,
50
48
  blob_meta = EXCLUDED.blob_meta,
51
49
  blob_mime_type = EXCLUDED.blob_mime_type,
@@ -555,10 +553,18 @@ class PgvectorDocumentStore:
555
553
  db_document["blob_data"] = blob.data if blob else None
556
554
  db_document["blob_meta"] = Jsonb(blob.meta) if blob and blob.meta else None
557
555
  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
556
  db_document["meta"] = Jsonb(db_document["meta"])
561
557
 
558
+ if "dataframe" in db_document:
559
+ dataframe = db_document.pop("dataframe", None)
560
+ if dataframe:
561
+ logger.warning(
562
+ "Document %s has the `dataframe` field set. "
563
+ "PgvectorDocumentStore no longer supports dataframes and this field will be ignored. "
564
+ "The `dataframe` field will soon be removed from Haystack Document.",
565
+ db_document["id"],
566
+ )
567
+
562
568
  if "sparse_embedding" in db_document:
563
569
  sparse_embedding = db_document.pop("sparse_embedding", None)
564
570
  if sparse_embedding:
@@ -585,12 +591,20 @@ class PgvectorDocumentStore:
585
591
  blob_data = haystack_dict.pop("blob_data")
586
592
  blob_meta = haystack_dict.pop("blob_meta")
587
593
  blob_mime_type = haystack_dict.pop("blob_mime_type")
594
+ dataframe = haystack_dict.pop("dataframe", None)
588
595
 
589
- # postgresql returns the embedding as a string
590
- # so we need to convert it to a list of floats
596
+ # convert the embedding to a list of floats
591
597
  if document.get("embedding") is not None:
592
598
  haystack_dict["embedding"] = document["embedding"].tolist()
593
599
 
600
+ if dataframe:
601
+ logger.warning(
602
+ "Document %s has the `dataframe` field set. "
603
+ "PgvectorDocumentStore no longer supports dataframes and this field will be ignored. "
604
+ "The `dataframe` field will soon be removed from Haystack Document.",
605
+ haystack_dict["id"],
606
+ )
607
+
594
608
  haystack_document = Document.from_dict(haystack_dict)
595
609
 
596
610
  if blob_data:
@@ -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):