qdrant-haystack 4.1.0__py3-none-any.whl → 4.1.1__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.
Potentially problematic release.
This version of qdrant-haystack might be problematic. Click here for more details.
- haystack_integrations/components/retrievers/qdrant/retriever.py +38 -5
- haystack_integrations/document_stores/qdrant/filters.py +145 -67
- {qdrant_haystack-4.1.0.dist-info → qdrant_haystack-4.1.1.dist-info}/METADATA +2 -2
- {qdrant_haystack-4.1.0.dist-info → qdrant_haystack-4.1.1.dist-info}/RECORD +6 -6
- {qdrant_haystack-4.1.0.dist-info → qdrant_haystack-4.1.1.dist-info}/WHEEL +0 -0
- {qdrant_haystack-4.1.0.dist-info → qdrant_haystack-4.1.1.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -2,6 +2,8 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
2
2
|
|
|
3
3
|
from haystack import Document, component, default_from_dict, default_to_dict
|
|
4
4
|
from haystack.dataclasses.sparse_embedding import SparseEmbedding
|
|
5
|
+
from haystack.document_stores.types import FilterPolicy
|
|
6
|
+
from haystack.document_stores.types.filter_policy import apply_filter_policy
|
|
5
7
|
from haystack_integrations.document_stores.qdrant import QdrantDocumentStore
|
|
6
8
|
from qdrant_client.http import models
|
|
7
9
|
|
|
@@ -39,6 +41,7 @@ class QdrantEmbeddingRetriever:
|
|
|
39
41
|
top_k: int = 10,
|
|
40
42
|
scale_score: bool = False,
|
|
41
43
|
return_embedding: bool = False,
|
|
44
|
+
filter_policy: Union[str, FilterPolicy] = FilterPolicy.REPLACE,
|
|
42
45
|
score_threshold: Optional[float] = None,
|
|
43
46
|
):
|
|
44
47
|
"""
|
|
@@ -49,6 +52,7 @@ class QdrantEmbeddingRetriever:
|
|
|
49
52
|
:param top_k: The maximum number of documents to retrieve.
|
|
50
53
|
:param scale_score: Whether to scale the scores of the retrieved documents or not.
|
|
51
54
|
:param return_embedding: Whether to return the embedding of the retrieved Documents.
|
|
55
|
+
:param filter_policy: Policy to determine how filters are applied.
|
|
52
56
|
:param score_threshold: A minimal score threshold for the result.
|
|
53
57
|
Score of the returned result might be higher or smaller than the threshold
|
|
54
58
|
depending on the `similarity` function specified in the Document Store.
|
|
@@ -66,6 +70,9 @@ class QdrantEmbeddingRetriever:
|
|
|
66
70
|
self._top_k = top_k
|
|
67
71
|
self._scale_score = scale_score
|
|
68
72
|
self._return_embedding = return_embedding
|
|
73
|
+
self._filter_policy = (
|
|
74
|
+
filter_policy if isinstance(filter_policy, FilterPolicy) else FilterPolicy.from_str(filter_policy)
|
|
75
|
+
)
|
|
69
76
|
self._score_threshold = score_threshold
|
|
70
77
|
|
|
71
78
|
def to_dict(self) -> Dict[str, Any]:
|
|
@@ -80,6 +87,7 @@ class QdrantEmbeddingRetriever:
|
|
|
80
87
|
document_store=self._document_store,
|
|
81
88
|
filters=self._filters,
|
|
82
89
|
top_k=self._top_k,
|
|
90
|
+
filter_policy=self._filter_policy.value,
|
|
83
91
|
scale_score=self._scale_score,
|
|
84
92
|
return_embedding=self._return_embedding,
|
|
85
93
|
score_threshold=self._score_threshold,
|
|
@@ -100,6 +108,7 @@ class QdrantEmbeddingRetriever:
|
|
|
100
108
|
"""
|
|
101
109
|
document_store = QdrantDocumentStore.from_dict(data["init_parameters"]["document_store"])
|
|
102
110
|
data["init_parameters"]["document_store"] = document_store
|
|
111
|
+
data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(data["init_parameters"]["filter_policy"])
|
|
103
112
|
return default_from_dict(cls, data)
|
|
104
113
|
|
|
105
114
|
@component.output_types(documents=List[Document])
|
|
@@ -125,9 +134,11 @@ class QdrantEmbeddingRetriever:
|
|
|
125
134
|
The retrieved documents.
|
|
126
135
|
|
|
127
136
|
"""
|
|
137
|
+
filters = apply_filter_policy(self._filter_policy, self._filters, filters)
|
|
138
|
+
|
|
128
139
|
docs = self._document_store._query_by_embedding(
|
|
129
140
|
query_embedding=query_embedding,
|
|
130
|
-
filters=filters
|
|
141
|
+
filters=filters,
|
|
131
142
|
top_k=top_k or self._top_k,
|
|
132
143
|
scale_score=scale_score or self._scale_score,
|
|
133
144
|
return_embedding=return_embedding or self._return_embedding,
|
|
@@ -171,6 +182,7 @@ class QdrantSparseEmbeddingRetriever:
|
|
|
171
182
|
top_k: int = 10,
|
|
172
183
|
scale_score: bool = False,
|
|
173
184
|
return_embedding: bool = False,
|
|
185
|
+
filter_policy: Union[str, FilterPolicy] = FilterPolicy.REPLACE,
|
|
174
186
|
score_threshold: Optional[float] = None,
|
|
175
187
|
):
|
|
176
188
|
"""
|
|
@@ -181,6 +193,7 @@ class QdrantSparseEmbeddingRetriever:
|
|
|
181
193
|
:param top_k: The maximum number of documents to retrieve.
|
|
182
194
|
:param scale_score: Whether to scale the scores of the retrieved documents or not.
|
|
183
195
|
:param return_embedding: Whether to return the sparse embedding of the retrieved Documents.
|
|
196
|
+
:param filter_policy: Policy to determine how filters are applied. Defaults to "replace".
|
|
184
197
|
:param score_threshold: A minimal score threshold for the result.
|
|
185
198
|
Score of the returned result might be higher or smaller than the threshold
|
|
186
199
|
depending on the Distance function used.
|
|
@@ -198,6 +211,9 @@ class QdrantSparseEmbeddingRetriever:
|
|
|
198
211
|
self._top_k = top_k
|
|
199
212
|
self._scale_score = scale_score
|
|
200
213
|
self._return_embedding = return_embedding
|
|
214
|
+
self._filter_policy = (
|
|
215
|
+
filter_policy if isinstance(filter_policy, FilterPolicy) else FilterPolicy.from_str(filter_policy)
|
|
216
|
+
)
|
|
201
217
|
self._score_threshold = score_threshold
|
|
202
218
|
|
|
203
219
|
def to_dict(self) -> Dict[str, Any]:
|
|
@@ -213,6 +229,7 @@ class QdrantSparseEmbeddingRetriever:
|
|
|
213
229
|
filters=self._filters,
|
|
214
230
|
top_k=self._top_k,
|
|
215
231
|
scale_score=self._scale_score,
|
|
232
|
+
filter_policy=self._filter_policy.value,
|
|
216
233
|
return_embedding=self._return_embedding,
|
|
217
234
|
score_threshold=self._score_threshold,
|
|
218
235
|
)
|
|
@@ -232,6 +249,7 @@ class QdrantSparseEmbeddingRetriever:
|
|
|
232
249
|
"""
|
|
233
250
|
document_store = QdrantDocumentStore.from_dict(data["init_parameters"]["document_store"])
|
|
234
251
|
data["init_parameters"]["document_store"] = document_store
|
|
252
|
+
data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(data["init_parameters"]["filter_policy"])
|
|
235
253
|
return default_from_dict(cls, data)
|
|
236
254
|
|
|
237
255
|
@component.output_types(documents=List[Document])
|
|
@@ -248,7 +266,9 @@ class QdrantSparseEmbeddingRetriever:
|
|
|
248
266
|
Run the Sparse Embedding Retriever on the given input data.
|
|
249
267
|
|
|
250
268
|
:param query_sparse_embedding: Sparse Embedding of the query.
|
|
251
|
-
:param filters:
|
|
269
|
+
:param filters: Filters applied to the retrieved Documents. The way runtime filters are applied depends on
|
|
270
|
+
the `filter_policy` chosen at retriever initialization. See init method docstring for more
|
|
271
|
+
details.
|
|
252
272
|
:param top_k: The maximum number of documents to return.
|
|
253
273
|
:param scale_score: Whether to scale the scores of the retrieved documents or not.
|
|
254
274
|
:param return_embedding: Whether to return the embedding of the retrieved Documents.
|
|
@@ -260,9 +280,11 @@ class QdrantSparseEmbeddingRetriever:
|
|
|
260
280
|
The retrieved documents.
|
|
261
281
|
|
|
262
282
|
"""
|
|
283
|
+
filters = apply_filter_policy(self._filter_policy, self._filters, filters)
|
|
284
|
+
|
|
263
285
|
docs = self._document_store._query_by_sparse(
|
|
264
286
|
query_sparse_embedding=query_sparse_embedding,
|
|
265
|
-
filters=filters
|
|
287
|
+
filters=filters,
|
|
266
288
|
top_k=top_k or self._top_k,
|
|
267
289
|
scale_score=scale_score or self._scale_score,
|
|
268
290
|
return_embedding=return_embedding or self._return_embedding,
|
|
@@ -311,6 +333,7 @@ class QdrantHybridRetriever:
|
|
|
311
333
|
filters: Optional[Union[Dict[str, Any], models.Filter]] = None,
|
|
312
334
|
top_k: int = 10,
|
|
313
335
|
return_embedding: bool = False,
|
|
336
|
+
filter_policy: Union[str, FilterPolicy] = FilterPolicy.REPLACE,
|
|
314
337
|
score_threshold: Optional[float] = None,
|
|
315
338
|
):
|
|
316
339
|
"""
|
|
@@ -320,6 +343,7 @@ class QdrantHybridRetriever:
|
|
|
320
343
|
:param filters: A dictionary with filters to narrow down the search space.
|
|
321
344
|
:param top_k: The maximum number of documents to retrieve.
|
|
322
345
|
:param return_embedding: Whether to return the embeddings of the retrieved Documents.
|
|
346
|
+
:param filter_policy: Policy to determine how filters are applied.
|
|
323
347
|
:param score_threshold: A minimal score threshold for the result.
|
|
324
348
|
Score of the returned result might be higher or smaller than the threshold
|
|
325
349
|
depending on the Distance function used.
|
|
@@ -336,6 +360,9 @@ class QdrantHybridRetriever:
|
|
|
336
360
|
self._filters = filters
|
|
337
361
|
self._top_k = top_k
|
|
338
362
|
self._return_embedding = return_embedding
|
|
363
|
+
self._filter_policy = (
|
|
364
|
+
filter_policy if isinstance(filter_policy, FilterPolicy) else FilterPolicy.from_str(filter_policy)
|
|
365
|
+
)
|
|
339
366
|
self._score_threshold = score_threshold
|
|
340
367
|
|
|
341
368
|
def to_dict(self) -> Dict[str, Any]:
|
|
@@ -350,6 +377,7 @@ class QdrantHybridRetriever:
|
|
|
350
377
|
document_store=self._document_store.to_dict(),
|
|
351
378
|
filters=self._filters,
|
|
352
379
|
top_k=self._top_k,
|
|
380
|
+
filter_policy=self._filter_policy.value,
|
|
353
381
|
return_embedding=self._return_embedding,
|
|
354
382
|
score_threshold=self._score_threshold,
|
|
355
383
|
)
|
|
@@ -366,6 +394,7 @@ class QdrantHybridRetriever:
|
|
|
366
394
|
"""
|
|
367
395
|
document_store = QdrantDocumentStore.from_dict(data["init_parameters"]["document_store"])
|
|
368
396
|
data["init_parameters"]["document_store"] = document_store
|
|
397
|
+
data["init_parameters"]["filter_policy"] = FilterPolicy.from_str(data["init_parameters"]["filter_policy"])
|
|
369
398
|
return default_from_dict(cls, data)
|
|
370
399
|
|
|
371
400
|
@component.output_types(documents=List[Document])
|
|
@@ -383,7 +412,9 @@ class QdrantHybridRetriever:
|
|
|
383
412
|
|
|
384
413
|
:param query_embedding: Dense embedding of the query.
|
|
385
414
|
:param query_sparse_embedding: Sparse embedding of the query.
|
|
386
|
-
:param filters:
|
|
415
|
+
:param filters: Filters applied to the retrieved Documents. The way runtime filters are applied depends on
|
|
416
|
+
the `filter_policy` chosen at retriever initialization. See init method docstring for more
|
|
417
|
+
details.
|
|
387
418
|
:param top_k: The maximum number of documents to return.
|
|
388
419
|
:param return_embedding: Whether to return the embedding of the retrieved Documents.
|
|
389
420
|
:param score_threshold: A minimal score threshold for the result.
|
|
@@ -394,10 +425,12 @@ class QdrantHybridRetriever:
|
|
|
394
425
|
The retrieved documents.
|
|
395
426
|
|
|
396
427
|
"""
|
|
428
|
+
filters = apply_filter_policy(self._filter_policy, self._filters, filters)
|
|
429
|
+
|
|
397
430
|
docs = self._document_store._query_hybrid(
|
|
398
431
|
query_embedding=query_embedding,
|
|
399
432
|
query_sparse_embedding=query_sparse_embedding,
|
|
400
|
-
filters=filters
|
|
433
|
+
filters=filters,
|
|
401
434
|
top_k=top_k or self._top_k,
|
|
402
435
|
return_embedding=return_embedding or self._return_embedding,
|
|
403
436
|
score_threshold=score_threshold or self._score_threshold,
|
|
@@ -4,28 +4,55 @@ from typing import List, Optional, Union
|
|
|
4
4
|
from haystack.utils.filters import COMPARISON_OPERATORS, LOGICAL_OPERATORS, FilterError
|
|
5
5
|
from qdrant_client.http import models
|
|
6
6
|
|
|
7
|
-
from .converters import convert_id
|
|
8
|
-
|
|
9
7
|
COMPARISON_OPERATORS = COMPARISON_OPERATORS.keys()
|
|
10
8
|
LOGICAL_OPERATORS = LOGICAL_OPERATORS.keys()
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
def convert_filters_to_qdrant(
|
|
14
|
-
filter_term: Optional[Union[List[dict], dict, models.Filter]] = None,
|
|
15
|
-
) -> Optional[models.Filter]:
|
|
16
|
-
"""Converts Haystack filters to the format used by Qdrant.
|
|
12
|
+
filter_term: Optional[Union[List[dict], dict, models.Filter]] = None, is_parent_call: bool = True
|
|
13
|
+
) -> Optional[Union[models.Filter, List[models.Filter], List[models.Condition]]]:
|
|
14
|
+
"""Converts Haystack filters to the format used by Qdrant.
|
|
15
|
+
|
|
16
|
+
:param filter_term: the haystack filter to be converted to qdrant.
|
|
17
|
+
:param is_parent_call: indicates if this is the top-level call to the function. If True, the function returns
|
|
18
|
+
a single models.Filter object; if False, it may return a list of filters or conditions for further processing.
|
|
19
|
+
|
|
20
|
+
:returns: a single Qdrant Filter in the parent call or a list of such Filters in recursive calls.
|
|
21
|
+
|
|
22
|
+
:raises FilterError: If the invalid filter criteria is provided or if an unknown operator is encountered.
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
|
|
17
26
|
if isinstance(filter_term, models.Filter):
|
|
18
27
|
return filter_term
|
|
19
28
|
if not filter_term:
|
|
20
29
|
return None
|
|
21
30
|
|
|
22
|
-
must_clauses
|
|
31
|
+
must_clauses: List[models.Filter] = []
|
|
32
|
+
should_clauses: List[models.Filter] = []
|
|
33
|
+
must_not_clauses: List[models.Filter] = []
|
|
34
|
+
# Indicates if there are multiple same LOGICAL OPERATORS on each level
|
|
35
|
+
# and prevents them from being combined
|
|
36
|
+
same_operator_flag = False
|
|
37
|
+
conditions, qdrant_filter, current_level_operators = (
|
|
38
|
+
[],
|
|
39
|
+
[],
|
|
40
|
+
[],
|
|
41
|
+
)
|
|
23
42
|
|
|
24
43
|
if isinstance(filter_term, dict):
|
|
25
44
|
filter_term = [filter_term]
|
|
26
45
|
|
|
46
|
+
# ======== IDENTIFY FILTER ITEMS ON EACH LEVEL ========
|
|
47
|
+
|
|
27
48
|
for item in filter_term:
|
|
28
49
|
operator = item.get("operator")
|
|
50
|
+
|
|
51
|
+
# Check for repeated similar operators on each level
|
|
52
|
+
same_operator_flag = operator in current_level_operators and operator in LOGICAL_OPERATORS
|
|
53
|
+
if not same_operator_flag:
|
|
54
|
+
current_level_operators.append(operator)
|
|
55
|
+
|
|
29
56
|
if operator is None:
|
|
30
57
|
msg = "Operator not found in filters"
|
|
31
58
|
raise FilterError(msg)
|
|
@@ -34,12 +61,23 @@ def convert_filters_to_qdrant(
|
|
|
34
61
|
msg = f"'conditions' not found for '{operator}'"
|
|
35
62
|
raise FilterError(msg)
|
|
36
63
|
|
|
37
|
-
if operator
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
64
|
+
if operator in LOGICAL_OPERATORS:
|
|
65
|
+
# Recursively process nested conditions
|
|
66
|
+
current_filter = convert_filters_to_qdrant(item.get("conditions", []), is_parent_call=False) or []
|
|
67
|
+
|
|
68
|
+
# When same_operator_flag is set to True,
|
|
69
|
+
# ensure each clause is appended as an independent list to avoid merging distinct clauses.
|
|
70
|
+
if operator == "AND":
|
|
71
|
+
must_clauses = [must_clauses, current_filter] if same_operator_flag else must_clauses + current_filter
|
|
72
|
+
elif operator == "OR":
|
|
73
|
+
should_clauses = (
|
|
74
|
+
[should_clauses, current_filter] if same_operator_flag else should_clauses + current_filter
|
|
75
|
+
)
|
|
76
|
+
elif operator == "NOT":
|
|
77
|
+
must_not_clauses = (
|
|
78
|
+
[must_not_clauses, current_filter] if same_operator_flag else must_not_clauses + current_filter
|
|
79
|
+
)
|
|
80
|
+
|
|
43
81
|
elif operator in COMPARISON_OPERATORS:
|
|
44
82
|
field = item.get("field")
|
|
45
83
|
value = item.get("value")
|
|
@@ -47,20 +85,106 @@ def convert_filters_to_qdrant(
|
|
|
47
85
|
msg = f"'field' or 'value' not found for '{operator}'"
|
|
48
86
|
raise FilterError(msg)
|
|
49
87
|
|
|
50
|
-
|
|
88
|
+
parsed_conditions = _parse_comparison_operation(comparison_operation=operator, key=field, value=value)
|
|
89
|
+
|
|
90
|
+
# check if the parsed_conditions are models.Filter or models.Condition
|
|
91
|
+
for condition in parsed_conditions:
|
|
92
|
+
if isinstance(condition, models.Filter):
|
|
93
|
+
qdrant_filter.append(condition)
|
|
94
|
+
else:
|
|
95
|
+
conditions.append(condition)
|
|
96
|
+
|
|
51
97
|
else:
|
|
52
98
|
msg = f"Unknown operator {operator} used in filters"
|
|
53
99
|
raise FilterError(msg)
|
|
54
100
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
101
|
+
# ======== PROCESS FILTER ITEMS ON EACH LEVEL ========
|
|
102
|
+
|
|
103
|
+
# If same logical operators have separate clauses, create separate filters
|
|
104
|
+
if same_operator_flag:
|
|
105
|
+
qdrant_filter = build_filters_for_repeated_operators(
|
|
106
|
+
must_clauses, should_clauses, must_not_clauses, qdrant_filter
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# else append a single Filter for existing clauses
|
|
110
|
+
elif must_clauses or should_clauses or must_not_clauses:
|
|
111
|
+
qdrant_filter.append(
|
|
112
|
+
models.Filter(
|
|
113
|
+
must=must_clauses or None,
|
|
114
|
+
should=should_clauses or None,
|
|
115
|
+
must_not=must_not_clauses or None,
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# In case of parent call, a single Filter is returned
|
|
120
|
+
if is_parent_call:
|
|
121
|
+
# If qdrant_filter has just a single Filter in parent call,
|
|
122
|
+
# then it might be returned instead.
|
|
123
|
+
if len(qdrant_filter) == 1 and isinstance(qdrant_filter[0], models.Filter):
|
|
124
|
+
return qdrant_filter[0]
|
|
125
|
+
else:
|
|
126
|
+
must_clauses.extend(conditions)
|
|
127
|
+
return models.Filter(
|
|
128
|
+
must=must_clauses or None,
|
|
129
|
+
should=should_clauses or None,
|
|
130
|
+
must_not=must_not_clauses or None,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Store conditions of each level in output of the loop
|
|
134
|
+
elif conditions:
|
|
135
|
+
qdrant_filter.extend(conditions)
|
|
136
|
+
|
|
137
|
+
return qdrant_filter
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def build_filters_for_repeated_operators(
|
|
141
|
+
must_clauses,
|
|
142
|
+
should_clauses,
|
|
143
|
+
must_not_clauses,
|
|
144
|
+
qdrant_filter,
|
|
145
|
+
) -> List[models.Filter]:
|
|
146
|
+
"""
|
|
147
|
+
Flattens the nested lists of clauses by creating separate Filters for each clause of a logical operator.
|
|
148
|
+
|
|
149
|
+
:param must_clauses: a nested list of must clauses or an empty list.
|
|
150
|
+
:param should_clauses: a nested list of should clauses or an empty list.
|
|
151
|
+
:param must_not_clauses: a nested list of must_not clauses or an empty list.
|
|
152
|
+
:param qdrant_filter: a list where the generated Filter objects will be appended.
|
|
153
|
+
This list will be modified in-place.
|
|
60
154
|
|
|
61
|
-
filter_result = _squeeze_filter(payload_filter)
|
|
62
155
|
|
|
63
|
-
|
|
156
|
+
:returns: the modified `qdrant_filter` list with appended generated Filter objects.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
if any(isinstance(i, list) for i in must_clauses):
|
|
160
|
+
for i in must_clauses:
|
|
161
|
+
qdrant_filter.append(
|
|
162
|
+
models.Filter(
|
|
163
|
+
must=i or None,
|
|
164
|
+
should=should_clauses or None,
|
|
165
|
+
must_not=must_not_clauses or None,
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
if any(isinstance(i, list) for i in should_clauses):
|
|
169
|
+
for i in should_clauses:
|
|
170
|
+
qdrant_filter.append(
|
|
171
|
+
models.Filter(
|
|
172
|
+
must=must_clauses or None,
|
|
173
|
+
should=i or None,
|
|
174
|
+
must_not=must_not_clauses or None,
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
if any(isinstance(i, list) for i in must_not_clauses):
|
|
178
|
+
for i in must_clauses:
|
|
179
|
+
qdrant_filter.append(
|
|
180
|
+
models.Filter(
|
|
181
|
+
must=must_clauses or None,
|
|
182
|
+
should=should_clauses or None,
|
|
183
|
+
must_not=i or None,
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
return qdrant_filter
|
|
64
188
|
|
|
65
189
|
|
|
66
190
|
def _parse_comparison_operation(
|
|
@@ -92,7 +216,7 @@ def _parse_comparison_operation(
|
|
|
92
216
|
|
|
93
217
|
def _build_eq_condition(key: str, value: models.ValueVariants) -> models.Condition:
|
|
94
218
|
if isinstance(value, str) and " " in value:
|
|
95
|
-
models.FieldCondition(key=key, match=models.MatchText(text=value))
|
|
219
|
+
return models.FieldCondition(key=key, match=models.MatchText(text=value))
|
|
96
220
|
return models.FieldCondition(key=key, match=models.MatchValue(value=value))
|
|
97
221
|
|
|
98
222
|
|
|
@@ -184,52 +308,6 @@ def _build_gte_condition(key: str, value: Union[str, float, int]) -> models.Cond
|
|
|
184
308
|
raise FilterError(msg)
|
|
185
309
|
|
|
186
310
|
|
|
187
|
-
def _build_has_id_condition(id_values: List[models.ExtendedPointId]) -> models.HasIdCondition:
|
|
188
|
-
return models.HasIdCondition(
|
|
189
|
-
has_id=[
|
|
190
|
-
# Ids are converted into their internal representation
|
|
191
|
-
convert_id(item)
|
|
192
|
-
for item in id_values
|
|
193
|
-
]
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def _squeeze_filter(payload_filter: models.Filter) -> models.Filter:
|
|
198
|
-
"""
|
|
199
|
-
Simplify given payload filter, if the nested structure might be unnested.
|
|
200
|
-
That happens if there is a single clause in that filter.
|
|
201
|
-
:param payload_filter:
|
|
202
|
-
:returns:
|
|
203
|
-
"""
|
|
204
|
-
filter_parts = {
|
|
205
|
-
"must": payload_filter.must,
|
|
206
|
-
"should": payload_filter.should,
|
|
207
|
-
"must_not": payload_filter.must_not,
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
total_clauses = sum(len(x) for x in filter_parts.values() if x is not None)
|
|
211
|
-
if total_clauses == 0 or total_clauses > 1:
|
|
212
|
-
return payload_filter
|
|
213
|
-
|
|
214
|
-
# Payload filter has just a single clause provided (either must, should
|
|
215
|
-
# or must_not). If that single clause is also of a models.Filter type,
|
|
216
|
-
# then it might be returned instead.
|
|
217
|
-
for part_name, filter_part in filter_parts.items():
|
|
218
|
-
if not filter_part:
|
|
219
|
-
continue
|
|
220
|
-
|
|
221
|
-
subfilter = filter_part[0]
|
|
222
|
-
if not isinstance(subfilter, models.Filter):
|
|
223
|
-
# The inner statement is a simple condition like models.FieldCondition
|
|
224
|
-
# so it cannot be simplified.
|
|
225
|
-
continue
|
|
226
|
-
|
|
227
|
-
if subfilter.must:
|
|
228
|
-
return models.Filter(**{part_name: subfilter.must})
|
|
229
|
-
|
|
230
|
-
return payload_filter
|
|
231
|
-
|
|
232
|
-
|
|
233
311
|
def is_datetime_string(value: str) -> bool:
|
|
234
312
|
try:
|
|
235
313
|
datetime.fromisoformat(value)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: qdrant-haystack
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: An integration of Qdrant ANN vector database backend 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/qdrant/README.md
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
19
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
20
20
|
Requires-Python: >=3.8
|
|
21
|
-
Requires-Dist: haystack-ai
|
|
21
|
+
Requires-Dist: haystack-ai
|
|
22
22
|
Requires-Dist: qdrant-client>=1.10.0
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
haystack_integrations/components/retrievers/qdrant/__init__.py,sha256=IRjcM4f8b5eKFEMn8tn6h6RrfslEGP3WafU7mrzNzQM,313
|
|
2
|
-
haystack_integrations/components/retrievers/qdrant/retriever.py,sha256=
|
|
2
|
+
haystack_integrations/components/retrievers/qdrant/retriever.py,sha256=55IY5bmNvFe62abNBfDOhuo1I38-ue713c8gMNgkfuY,17947
|
|
3
3
|
haystack_integrations/document_stores/qdrant/__init__.py,sha256=kUGc5uewqArhmVR-JqB_NmJ4kNkTIQIvYDNSoO2ELn0,302
|
|
4
4
|
haystack_integrations/document_stores/qdrant/converters.py,sha256=2hcuI3kty1dVHzX1WGXxEtlrnZ9E8TAG56XATCFa6Pw,2491
|
|
5
5
|
haystack_integrations/document_stores/qdrant/document_store.py,sha256=eLw4P1h8GCj40R-BIlQOvJG9MpDzvtmQ7Hpb3AZhMSo,38117
|
|
6
|
-
haystack_integrations/document_stores/qdrant/filters.py,sha256=
|
|
6
|
+
haystack_integrations/document_stores/qdrant/filters.py,sha256=Nv_eKIYKwUWvldJfa0omfFQ0kgqi6L3DUFeMuIWziOY,11751
|
|
7
7
|
haystack_integrations/document_stores/qdrant/migrate_to_sparse.py,sha256=i6wBC_9_JVzYZtqKm3dhHKTxhwNdcAdpgki8GABDp1c,4909
|
|
8
|
-
qdrant_haystack-4.1.
|
|
9
|
-
qdrant_haystack-4.1.
|
|
10
|
-
qdrant_haystack-4.1.
|
|
11
|
-
qdrant_haystack-4.1.
|
|
8
|
+
qdrant_haystack-4.1.1.dist-info/METADATA,sha256=BQcRpx4WyYIvccTCEowd4jnc43zoApKx-Ql_BnUqZwM,1863
|
|
9
|
+
qdrant_haystack-4.1.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
10
|
+
qdrant_haystack-4.1.1.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
|
|
11
|
+
qdrant_haystack-4.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|