pgvector-haystack 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl

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.
@@ -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
- connection = connect(self.connection_string.resolve_value())
150
- connection.autocommit = True
151
- self._connection = connection
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
- # we create a generic cursor and another one that returns dictionaries
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
- if recreate_table:
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._cursor.
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._cursor
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._connection.rollback()
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, KEYWORD_INDEX_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(KEYWORD_INDEX_NAME),
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, HNSW_INDEX_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(HNSW_INDEX_NAME))
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(HNSW_INDEX_NAME), table_name=Identifier(self.table_name), ops=SQL(pg_ops)
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._dict_cursor,
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._cursor) if not isinstance(sql_insert, str) else sql_insert
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._cursor.executemany(sql_insert, db_documents, returning=True)
446
+ self.cursor.executemany(sql_insert, db_documents, returning=True)
416
447
  except IntegrityError as ie:
417
- self._connection.rollback()
448
+ self.connection.rollback()
418
449
  raise DuplicateDocumentError from ie
419
450
  except Error as e:
420
- self._connection.rollback()
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._cursor.fetchone():
462
+ if self.cursor.fetchone():
432
463
  written_docs += 1
433
- if not self._cursor.nextset():
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"] = [float(el) for el in document["embedding"].strip("[]").split(",")]
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._dict_cursor,
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._dict_cursor,
667
+ cursor=self.dict_cursor,
637
668
  )
638
669
 
639
670
  records = result.fetchall()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pgvector-haystack
3
- Version: 0.2.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
@@ -2,9 +2,9 @@ haystack_integrations/components/retrievers/pgvector/__init__.py,sha256=n4q4pUAp
2
2
  haystack_integrations/components/retrievers/pgvector/embedding_retriever.py,sha256=6LuQmF7JmPOCU_I10ksPcbDC6QMu-2SOq0--BQxBxpg,6523
3
3
  haystack_integrations/components/retrievers/pgvector/keyword_retriever.py,sha256=fAJ3M2l_Pt8zKqHES1l1L4gM16BOHeZWq8xc18Bhq7A,4624
4
4
  haystack_integrations/document_stores/pgvector/__init__.py,sha256=Ge-7ggarfr3x8UqNdFHUuFGiu52u49sOVe3l16WEwZU,197
5
- haystack_integrations/document_stores/pgvector/document_store.py,sha256=KY0FZGTjz4ejxKNqQ9UHDJUArsghwf7Iag45YfXgm2g,27011
5
+ haystack_integrations/document_stores/pgvector/document_store.py,sha256=bvN4tYQeNNZ0s07Ur8VtJNDL_XtFQqHq0IYAUbkmTho,28018
6
6
  haystack_integrations/document_stores/pgvector/filters.py,sha256=751_Z1QnXJCG0GWC2jcfeZ25NNYfJewQEIUCeJS70PA,8212
7
- pgvector_haystack-0.2.0.dist-info/METADATA,sha256=fD4i8wnlapdeHvJMpj11zlmGS_MxN_d6GDFvc3W_i1A,2219
8
- pgvector_haystack-0.2.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
9
- pgvector_haystack-0.2.0.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
10
- pgvector_haystack-0.2.0.dist-info/RECORD,,
7
+ pgvector_haystack-0.4.0.dist-info/METADATA,sha256=dNAGH9BMjyn5qlnaAdQqBpMUElmt173XoS9PEoiuCVY,2219
8
+ pgvector_haystack-0.4.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
9
+ pgvector_haystack-0.4.0.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
10
+ pgvector_haystack-0.4.0.dist-info/RECORD,,