weaviate-haystack 7.7.0__tar.gz → 7.9.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.
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/CHANGELOG.md +62 -14
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/LICENSE.txt +1 -1
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/PKG-INFO +3 -3
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/docker-compose.yml +14 -2
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/pyproject.toml +2 -1
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/document_stores/weaviate/document_store.py +132 -46
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_document_store.py +128 -46
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_document_store_async.py +49 -45
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/.gitignore +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/README.md +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/pydoc/config_docusaurus.yml +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/components/retrievers/py.typed +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/components/retrievers/weaviate/__init__.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/components/retrievers/weaviate/bm25_retriever.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/components/retrievers/weaviate/embedding_retriever.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/components/retrievers/weaviate/hybrid_retriever.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/document_stores/py.typed +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/document_stores/weaviate/__init__.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/document_stores/weaviate/_filters.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/src/haystack_integrations/document_stores/weaviate/auth.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/__init__.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/conftest.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_auth.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_bm25_retriever.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_bm25_retriever_async.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_embedding_retriever.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_embedding_retriever_async.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_files/robot1.jpg +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_filters.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_hybrid_retriever.py +0 -0
- {weaviate_haystack-7.7.0 → weaviate_haystack-7.9.0}/tests/test_hybrid_retriever_async.py +0 -0
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [integrations/weaviate-v7.8.0] - 2026-09-01
|
|
4
|
+
|
|
5
|
+
### 🐛 Bug Fixes
|
|
6
|
+
|
|
7
|
+
- `Weaviate` match `search_term` against metadata field value, not content + default size value set to 10 (#3643)
|
|
8
|
+
- Fix new issues raised by ruff 0.16.0 (#3670)
|
|
9
|
+
- `WeaviateDocumentStore` get_metadata_field_unique_values setting list of values to return to `Any` (#3708)
|
|
10
|
+
- Weaviate adding optional parameter `filters` to `get_metadata_field_unique_values()` (#3741)
|
|
11
|
+
- Standardize license files (#3771)
|
|
12
|
+
|
|
13
|
+
### 🧪 Testing
|
|
14
|
+
|
|
15
|
+
- `WeaviateDocumentStore` removing duplicated tests already covered by haystack Mixin tests (#3787)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## [integrations/weaviate-v7.7.0] - 2026-07-22
|
|
19
|
+
|
|
20
|
+
### 🚜 Refactor
|
|
21
|
+
|
|
22
|
+
- Align Weaviate closing methods to standard (#3630)
|
|
23
|
+
|
|
24
|
+
|
|
3
25
|
## [integrations/weaviate-v7.6.0] - 2026-06-22
|
|
4
26
|
|
|
5
27
|
### 🐛 Bug Fixes
|
|
@@ -200,21 +222,20 @@
|
|
|
200
222
|
|
|
201
223
|
### 🧹 Chores
|
|
202
224
|
|
|
203
|
-
- Weaviate - remove legacy filter support (#1070)
|
|
204
225
|
- Update changelog after removing legacy filters (#1083)
|
|
205
226
|
- Update ruff linting scripts and settings (#1105)
|
|
206
227
|
|
|
228
|
+
## [integrations/weaviate-v3.0.0] - 2024-09-12
|
|
207
229
|
|
|
208
|
-
|
|
230
|
+
### 🧹 Chores
|
|
209
231
|
|
|
210
|
-
|
|
232
|
+
- Weaviate - remove legacy filter support (#1070)
|
|
211
233
|
|
|
212
|
-
|
|
234
|
+
|
|
235
|
+
## [integrations/weaviate-v2.2.1] - 2024-09-07
|
|
213
236
|
|
|
214
237
|
### 🐛 Bug Fixes
|
|
215
238
|
|
|
216
|
-
- Weaviate filter error (#811)
|
|
217
|
-
- Fix connection to Weaviate Cloud Service (#624)
|
|
218
239
|
- Pin weaviate-client (#1046)
|
|
219
240
|
- Weaviate - fix connection issues with some WCS URLs (#1058)
|
|
220
241
|
|
|
@@ -222,9 +243,27 @@
|
|
|
222
243
|
|
|
223
244
|
- Do not retry tests in `hatch run test` command (#954)
|
|
224
245
|
|
|
225
|
-
###
|
|
246
|
+
### 🌀 Miscellaneous
|
|
226
247
|
|
|
227
|
-
-
|
|
248
|
+
- Chore: weaviate - ruff update, don't ruff tests (#986)
|
|
249
|
+
|
|
250
|
+
## [integrations/weaviate-v2.2.0] - 2024-08-02
|
|
251
|
+
|
|
252
|
+
### 🌀 Miscellaneous
|
|
253
|
+
|
|
254
|
+
- Fix: Weaviate: Use collections.list_all instead of collections._get_all (#921)
|
|
255
|
+
|
|
256
|
+
## [integrations/weaviate-v2.1.3] - 2024-07-17
|
|
257
|
+
|
|
258
|
+
### 🌀 Miscellaneous
|
|
259
|
+
|
|
260
|
+
- Fix: `weaviate` - Fallback to default filter policy when deserializing retrievers without the init parameter (#903)
|
|
261
|
+
|
|
262
|
+
## [integrations/weaviate-v2.1.2] - 2024-07-15
|
|
263
|
+
|
|
264
|
+
### 🚀 Features
|
|
265
|
+
|
|
266
|
+
- Add filter_policy to weaviate integration (#824)
|
|
228
267
|
|
|
229
268
|
### 🧹 Chores
|
|
230
269
|
|
|
@@ -232,15 +271,24 @@
|
|
|
232
271
|
|
|
233
272
|
### 🌀 Miscellaneous
|
|
234
273
|
|
|
274
|
+
- Chore: Minor retriever pydoc fix (#884)
|
|
275
|
+
|
|
276
|
+
## [integrations/weaviate-v2.1.1] - 2024-06-25
|
|
277
|
+
|
|
278
|
+
### 🐛 Bug Fixes
|
|
279
|
+
|
|
280
|
+
- Weaviate filter error (#811)
|
|
281
|
+
- Fix connection to Weaviate Cloud Service (#624)
|
|
282
|
+
|
|
283
|
+
### ⚙️ CI
|
|
284
|
+
|
|
285
|
+
- Retry tests to reduce flakyness (#836)
|
|
286
|
+
|
|
287
|
+
### 🌀 Miscellaneous
|
|
288
|
+
|
|
235
289
|
- Handle connection to WCS and add tests
|
|
236
290
|
- Revert "Handle connection to WCS and add tests"
|
|
237
|
-
|
|
238
|
-
This reverts commit f48802b2ce612896fd06a13cf33dffd9f77a8859.
|
|
239
291
|
- Ci: install `pytest-rerunfailures` where needed; add retry config to `test-cov` script (#845)
|
|
240
|
-
- Chore: Minor retriever pydoc fix (#884)
|
|
241
|
-
- Fix: `weaviate` - Fallback to default filter policy when deserializing retrievers without the init parameter (#903)
|
|
242
|
-
- Fix: Weaviate: Use collections.list_all instead of collections._get_all (#921)
|
|
243
|
-
- Chore: weaviate - ruff update, don't ruff tests (#986)
|
|
244
292
|
|
|
245
293
|
## [integrations/weaviate-v2.1.0] - 2024-06-10
|
|
246
294
|
|
|
@@ -58,7 +58,7 @@ APPENDIX: How to apply the Apache License to your work.
|
|
|
58
58
|
|
|
59
59
|
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
|
60
60
|
|
|
61
|
-
Copyright
|
|
61
|
+
Copyright 2023-present deepset GmbH
|
|
62
62
|
|
|
63
63
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
64
64
|
you may not use this file except in compliance with the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: weaviate-haystack
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.9.0
|
|
4
4
|
Summary: An integration of Weaviate vector database 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/weaviate/README.md
|
|
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: haystack-ai>=2.28.0
|
|
23
23
|
Requires-Dist: python-dateutil>=2.7.0
|
|
24
|
-
Requires-Dist: weaviate-client>=4.
|
|
24
|
+
Requires-Dist: weaviate-client>=4.23.0
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
|
|
27
27
|
# weaviate-haystack
|
|
@@ -7,7 +7,7 @@ services:
|
|
|
7
7
|
- '8080'
|
|
8
8
|
- --scheme
|
|
9
9
|
- http
|
|
10
|
-
image: semitechnologies/weaviate:1.
|
|
10
|
+
image: cr.weaviate.io/semitechnologies/weaviate:1.39.4
|
|
11
11
|
ports:
|
|
12
12
|
- 8080:8080
|
|
13
13
|
- 50051:50051
|
|
@@ -18,4 +18,16 @@ services:
|
|
|
18
18
|
PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
|
|
19
19
|
DEFAULT_VECTORIZER_MODULE: 'none'
|
|
20
20
|
ENABLE_MODULES: ''
|
|
21
|
-
CLUSTER_HOSTNAME: 'node1'
|
|
21
|
+
CLUSTER_HOSTNAME: 'node1'
|
|
22
|
+
healthcheck:
|
|
23
|
+
test:
|
|
24
|
+
- CMD
|
|
25
|
+
- busybox
|
|
26
|
+
- wget
|
|
27
|
+
- -q
|
|
28
|
+
- -O
|
|
29
|
+
- '-'
|
|
30
|
+
- http://127.0.0.1:8080/v1/.well-known/ready
|
|
31
|
+
interval: 5s
|
|
32
|
+
timeout: 3s
|
|
33
|
+
retries: 20
|
|
@@ -25,7 +25,7 @@ classifiers = [
|
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
27
|
"haystack-ai>=2.28.0",
|
|
28
|
-
"weaviate-client>=4.
|
|
28
|
+
"weaviate-client>=4.23.0",
|
|
29
29
|
"python-dateutil>=2.7.0",
|
|
30
30
|
]
|
|
31
31
|
|
|
@@ -136,6 +136,7 @@ ignore = [
|
|
|
136
136
|
"PLR0912",
|
|
137
137
|
"PLR0913",
|
|
138
138
|
"PLR0915",
|
|
139
|
+
"PLR0917",
|
|
139
140
|
]
|
|
140
141
|
|
|
141
142
|
[tool.ruff.lint.isort]
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import base64
|
|
6
6
|
import datetime
|
|
7
|
+
import importlib.metadata
|
|
7
8
|
import json
|
|
8
9
|
from contextlib import suppress
|
|
9
10
|
from dataclasses import asdict
|
|
@@ -38,6 +39,27 @@ from .auth import AuthCredentials
|
|
|
38
39
|
|
|
39
40
|
logger = logging.getLogger(__name__)
|
|
40
41
|
|
|
42
|
+
# Weaviate tracks which framework integrations talk to it through this header, so that adoption of
|
|
43
|
+
# weaviate-haystack shows up in Weaviate's own telemetry. See https://github.com/weaviate/weaviate/pull/10535.
|
|
44
|
+
_INTEGRATION_HEADER = "X-Weaviate-Client-Integration"
|
|
45
|
+
_INTEGRATION_NAME = "haystack-python"
|
|
46
|
+
_PACKAGE_NAME = "weaviate-haystack"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _integration_header_value() -> str:
|
|
50
|
+
"""
|
|
51
|
+
Builds the value of the Weaviate client integration header.
|
|
52
|
+
|
|
53
|
+
:returns:
|
|
54
|
+
A `<name>/<version>` string. The version falls back to `unknown` when the package metadata is
|
|
55
|
+
unavailable, for example when running from a source checkout that was never installed.
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
version = importlib.metadata.version(_PACKAGE_NAME)
|
|
59
|
+
except importlib.metadata.PackageNotFoundError:
|
|
60
|
+
version = "unknown"
|
|
61
|
+
return f"{_INTEGRATION_NAME}/{version}"
|
|
62
|
+
|
|
41
63
|
|
|
42
64
|
# This is the default collection properties for Weaviate.
|
|
43
65
|
# It's a list of properties that will be created on the collection.
|
|
@@ -144,6 +166,9 @@ class WeaviateDocumentStore:
|
|
|
144
166
|
```
|
|
145
167
|
{"X-OpenAI-Api-Key": "<THE-KEY>"}, {"X-HuggingFace-Api-Key": "<THE-KEY>"}
|
|
146
168
|
```
|
|
169
|
+
Every connection also carries `X-Weaviate-Client-Integration: haystack-python/<version>`, which
|
|
170
|
+
identifies this integration in Weaviate's telemetry. Supplying that header here, in any casing,
|
|
171
|
+
overrides it.
|
|
147
172
|
:param embedded_options:
|
|
148
173
|
If set, create an embedded Weaviate cluster inside the client. For a full list of options see
|
|
149
174
|
`weaviate.embedded.EmbeddedOptions`.
|
|
@@ -183,6 +208,26 @@ class WeaviateDocumentStore:
|
|
|
183
208
|
"properties", DOCUMENT_COLLECTION_PROPERTIES
|
|
184
209
|
)
|
|
185
210
|
|
|
211
|
+
def _connection_headers(self) -> dict[str, Any]:
|
|
212
|
+
"""
|
|
213
|
+
Builds the headers passed to the Weaviate client.
|
|
214
|
+
|
|
215
|
+
Merges the integration identification header into the user supplied `additional_headers`. A user
|
|
216
|
+
supplied value for the same header wins, so the header can be overridden or suppressed. The merged
|
|
217
|
+
headers are deliberately not stored on the instance: `to_dict()` must keep serializing only the
|
|
218
|
+
headers the user actually passed, so that a serialized pipeline does not bake in the version of
|
|
219
|
+
`weaviate-haystack` that happened to write it.
|
|
220
|
+
|
|
221
|
+
:returns:
|
|
222
|
+
The headers to pass to the Weaviate client.
|
|
223
|
+
"""
|
|
224
|
+
headers: dict[str, Any] = dict(self._additional_headers) if self._additional_headers else {}
|
|
225
|
+
# The comparison is case insensitive because gRPC metadata keys are case sensitive: a user supplied
|
|
226
|
+
# "x-weaviate-client-integration" would otherwise end up alongside our own spelling of the header.
|
|
227
|
+
if not any(key.lower() == _INTEGRATION_HEADER.lower() for key in headers):
|
|
228
|
+
headers[_INTEGRATION_HEADER] = _integration_header_value()
|
|
229
|
+
return headers
|
|
230
|
+
|
|
186
231
|
@property
|
|
187
232
|
def client(self) -> weaviate.WeaviateClient:
|
|
188
233
|
"""Return the synchronous Weaviate client, creating and connecting it if necessary."""
|
|
@@ -199,7 +244,7 @@ class WeaviateDocumentStore:
|
|
|
199
244
|
self._client = weaviate.connect_to_weaviate_cloud(
|
|
200
245
|
self._url,
|
|
201
246
|
auth_credentials=self._auth_client_secret.resolve_value(),
|
|
202
|
-
headers=self.
|
|
247
|
+
headers=self._connection_headers(),
|
|
203
248
|
additional_config=self._additional_config,
|
|
204
249
|
)
|
|
205
250
|
else:
|
|
@@ -216,7 +261,7 @@ class WeaviateDocumentStore:
|
|
|
216
261
|
),
|
|
217
262
|
auth_client_secret=self._auth_client_secret.resolve_value() if self._auth_client_secret else None,
|
|
218
263
|
additional_config=self._additional_config,
|
|
219
|
-
additional_headers=self.
|
|
264
|
+
additional_headers=self._connection_headers(),
|
|
220
265
|
embedded_options=self._embedded_options,
|
|
221
266
|
skip_init_checks=False,
|
|
222
267
|
)
|
|
@@ -244,7 +289,7 @@ class WeaviateDocumentStore:
|
|
|
244
289
|
self._async_client = weaviate.use_async_with_weaviate_cloud(
|
|
245
290
|
self._url,
|
|
246
291
|
auth_credentials=self._auth_client_secret.resolve_value(),
|
|
247
|
-
headers=self.
|
|
292
|
+
headers=self._connection_headers(),
|
|
248
293
|
additional_config=self._additional_config,
|
|
249
294
|
)
|
|
250
295
|
else:
|
|
@@ -261,7 +306,7 @@ class WeaviateDocumentStore:
|
|
|
261
306
|
),
|
|
262
307
|
auth_client_secret=self._auth_client_secret.resolve_value() if self._auth_client_secret else None,
|
|
263
308
|
additional_config=self._additional_config,
|
|
264
|
-
additional_headers=self.
|
|
309
|
+
additional_headers=self._connection_headers(),
|
|
265
310
|
embedded_options=self._embedded_options,
|
|
266
311
|
skip_init_checks=False,
|
|
267
312
|
)
|
|
@@ -632,76 +677,121 @@ class WeaviateDocumentStore:
|
|
|
632
677
|
|
|
633
678
|
return result
|
|
634
679
|
|
|
680
|
+
@staticmethod
|
|
681
|
+
def _compute_field_unique_values(
|
|
682
|
+
agg_result: Any, search_term: str | None, from_: int, size: int
|
|
683
|
+
) -> tuple[list[Any], int]:
|
|
684
|
+
"""
|
|
685
|
+
Computes paginated unique values from a Weaviate group-by aggregation result.
|
|
686
|
+
|
|
687
|
+
Weaviate's `like` filter only supports case-sensitive substring matching, so instead of
|
|
688
|
+
filtering server-side (which would be case-sensitive), all groups are fetched unconditionally
|
|
689
|
+
and `search_term` is applied here as a case-insensitive substring match in Python.
|
|
690
|
+
|
|
691
|
+
:param agg_result: The group-by aggregation result, with one group per unique field value.
|
|
692
|
+
:param search_term: Optional term to filter the values by, matched as a case-insensitive
|
|
693
|
+
substring against each value.
|
|
694
|
+
:param from_: The starting offset for pagination (0-indexed).
|
|
695
|
+
:param size: The maximum number of unique values to return.
|
|
696
|
+
:returns: A tuple of (paginated list of unique values in their original type, total count of
|
|
697
|
+
unique values). See `get_metadata_field_unique_values`'s docstring for a caveat on scalar
|
|
698
|
+
int fields - `agg_result`'s numeric group keys always decode as `float`.
|
|
699
|
+
"""
|
|
700
|
+
all_values = [g.grouped_by.value for g in agg_result.groups] if agg_result.groups else []
|
|
701
|
+
if search_term:
|
|
702
|
+
search_term_lower = search_term.lower()
|
|
703
|
+
all_values = [value for value in all_values if search_term_lower in str(value).lower()]
|
|
704
|
+
all_values.sort(key=str) # Sort for consistent pagination
|
|
705
|
+
total_count = len(all_values)
|
|
706
|
+
|
|
707
|
+
paginated_values = all_values[from_ : from_ + size]
|
|
708
|
+
|
|
709
|
+
return paginated_values, total_count
|
|
710
|
+
|
|
635
711
|
def get_metadata_field_unique_values(
|
|
636
712
|
self,
|
|
637
713
|
metadata_field: str,
|
|
638
714
|
search_term: str | None = None,
|
|
639
715
|
from_: int = 0,
|
|
640
|
-
size: int =
|
|
641
|
-
|
|
716
|
+
size: int = 10,
|
|
717
|
+
filters: dict[str, Any] | None = None,
|
|
718
|
+
) -> tuple[list[Any], int]:
|
|
642
719
|
"""
|
|
643
720
|
Returns unique values for a metadata field with pagination support.
|
|
644
721
|
|
|
645
722
|
:param metadata_field: The metadata field name to get unique values for.
|
|
646
723
|
Can be prefixed with 'meta.' (e.g., 'meta.category' or 'category').
|
|
647
|
-
:param search_term: Optional term to filter
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
Note: Uses substring matching (
|
|
724
|
+
:param search_term: Optional term to filter the metadata field's values by
|
|
725
|
+
before returning them. If provided, only values of `metadata_field` that
|
|
726
|
+
contain this term will be considered.
|
|
727
|
+
Note: Uses case-insensitive substring matching (no stemming).
|
|
651
728
|
:param from_: The starting offset for pagination (0-indexed). Defaults to 0.
|
|
652
|
-
:param size: The maximum number of unique values to return. Defaults to
|
|
653
|
-
:
|
|
654
|
-
:
|
|
729
|
+
:param size: The maximum number of unique values to return. Defaults to 10.
|
|
730
|
+
:param filters: Optional filters to restrict the documents considered.
|
|
731
|
+
:returns: A tuple of (list of unique values in their original type, total count of unique values).
|
|
732
|
+
|
|
733
|
+
**Note**: a scalar `int` metadata value comes back as `float`, not `int`. weaviate-client has no
|
|
734
|
+
wire-protocol field for a scalar int - non-list properties are packed into a
|
|
735
|
+
`google.protobuf.Struct`, whose `Value` type only has `number_value` (a double), so the int/float
|
|
736
|
+
distinction is lost before the value reaches Weaviate. `GroupByAggregate` decodes numeric group
|
|
737
|
+
keys the same way, so this holds even when the field's schema type is explicitly `DataType.INT`.
|
|
738
|
+
List-valued int fields (e.g. `meta={"tags": [1, 2]}`) are unaffected - those go through a
|
|
739
|
+
dedicated `IntArrayProperties` wire type instead.
|
|
655
740
|
"""
|
|
656
741
|
field_name = _normalize_metadata_field_name(metadata_field)
|
|
657
742
|
|
|
658
743
|
config = self.collection.config.get()
|
|
659
744
|
schema_fields = {prop.name for prop in config.properties}
|
|
660
745
|
if field_name not in schema_fields:
|
|
661
|
-
|
|
662
|
-
|
|
746
|
+
# No document has ever written this field, so Weaviate's auto-schema never created a
|
|
747
|
+
# property for it - there are no values to aggregate.
|
|
748
|
+
return [], 0
|
|
663
749
|
|
|
664
750
|
weaviate_filter = None
|
|
665
|
-
if
|
|
666
|
-
|
|
751
|
+
if filters:
|
|
752
|
+
validate_filters(filters)
|
|
753
|
+
weaviate_filter = convert_filters(filters)
|
|
667
754
|
|
|
668
755
|
# Weaviate's GroupByAggregate has a default limit of 100 groups.
|
|
669
756
|
# We use the document count as the limit to ensure all unique values are retrieved,
|
|
670
757
|
# since the number of unique values cannot exceed the number of documents.
|
|
671
|
-
doc_count = self.collection.aggregate.over_all(total_count=True).total_count or 0
|
|
758
|
+
doc_count = self.collection.aggregate.over_all(filters=weaviate_filter, total_count=True).total_count or 0
|
|
672
759
|
|
|
673
760
|
agg_result = self.collection.aggregate.over_all(
|
|
674
761
|
filters=weaviate_filter, group_by=GroupByAggregate(prop=field_name, limit=doc_count)
|
|
675
762
|
)
|
|
676
763
|
|
|
677
|
-
|
|
678
|
-
all_values.sort() # Sort for consistent pagination
|
|
679
|
-
total_count = len(all_values)
|
|
680
|
-
|
|
681
|
-
paginated_values = all_values[from_ : from_ + size]
|
|
682
|
-
|
|
683
|
-
return paginated_values, total_count
|
|
764
|
+
return self._compute_field_unique_values(agg_result, search_term, from_, size)
|
|
684
765
|
|
|
685
766
|
async def get_metadata_field_unique_values_async(
|
|
686
767
|
self,
|
|
687
768
|
metadata_field: str,
|
|
688
769
|
search_term: str | None = None,
|
|
689
770
|
from_: int = 0,
|
|
690
|
-
size: int =
|
|
691
|
-
|
|
771
|
+
size: int = 10,
|
|
772
|
+
filters: dict[str, Any] | None = None,
|
|
773
|
+
) -> tuple[list[Any], int]:
|
|
692
774
|
"""
|
|
693
775
|
Asynchronously returns unique values for a metadata field with pagination support.
|
|
694
776
|
|
|
695
777
|
:param metadata_field: The metadata field name to get unique values for.
|
|
696
778
|
Can be prefixed with 'meta.' (e.g., 'meta.category' or 'category').
|
|
697
|
-
:param search_term: Optional term to filter
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
Note: Uses substring matching (
|
|
779
|
+
:param search_term: Optional term to filter the metadata field's values by
|
|
780
|
+
before returning them. If provided, only values of `metadata_field` that
|
|
781
|
+
contain this term will be considered.
|
|
782
|
+
Note: Uses case-insensitive substring matching (no stemming).
|
|
701
783
|
:param from_: The starting offset for pagination (0-indexed). Defaults to 0.
|
|
702
|
-
:param size: The maximum number of unique values to return. Defaults to
|
|
703
|
-
:
|
|
704
|
-
:
|
|
784
|
+
:param size: The maximum number of unique values to return. Defaults to 10.
|
|
785
|
+
:param filters: Optional filters to restrict the documents considered.
|
|
786
|
+
:returns: A tuple of (list of unique values in their original type, total count of unique values).
|
|
787
|
+
|
|
788
|
+
**Note**: a scalar `int` metadata value comes back as `float`, not `int`. weaviate-client has no
|
|
789
|
+
wire-protocol field for a scalar int - non-list properties are packed into a
|
|
790
|
+
`google.protobuf.Struct`, whose `Value` type only has `number_value` (a double), so the int/float
|
|
791
|
+
distinction is lost before the value reaches Weaviate. `GroupByAggregate` decodes numeric group
|
|
792
|
+
keys the same way, so this holds even when the field's schema type is explicitly `DataType.INT`.
|
|
793
|
+
List-valued int fields (e.g. `meta={"tags": [1, 2]}`) are unaffected - those go through a
|
|
794
|
+
dedicated `IntArrayProperties` wire type instead.
|
|
705
795
|
"""
|
|
706
796
|
field_name = _normalize_metadata_field_name(metadata_field)
|
|
707
797
|
|
|
@@ -709,30 +799,26 @@ class WeaviateDocumentStore:
|
|
|
709
799
|
config = await collection.config.get()
|
|
710
800
|
schema_fields = {prop.name for prop in config.properties}
|
|
711
801
|
if field_name not in schema_fields:
|
|
712
|
-
|
|
713
|
-
|
|
802
|
+
# No document has ever written this field, so Weaviate's auto-schema never created a
|
|
803
|
+
# property for it - there are no values to aggregate.
|
|
804
|
+
return [], 0
|
|
714
805
|
|
|
715
806
|
weaviate_filter = None
|
|
716
|
-
if
|
|
717
|
-
|
|
807
|
+
if filters:
|
|
808
|
+
validate_filters(filters)
|
|
809
|
+
weaviate_filter = convert_filters(filters)
|
|
718
810
|
|
|
719
811
|
# Weaviate's GroupByAggregate has a default limit of 100 groups.
|
|
720
812
|
# We use the document count as the limit to ensure all unique values are retrieved,
|
|
721
813
|
# since the number of unique values cannot exceed the number of documents.
|
|
722
|
-
doc_count_result = await collection.aggregate.over_all(total_count=True)
|
|
814
|
+
doc_count_result = await collection.aggregate.over_all(filters=weaviate_filter, total_count=True)
|
|
723
815
|
doc_count = doc_count_result.total_count or 0
|
|
724
816
|
|
|
725
817
|
agg_result = await collection.aggregate.over_all(
|
|
726
818
|
filters=weaviate_filter, group_by=GroupByAggregate(prop=field_name, limit=doc_count)
|
|
727
819
|
)
|
|
728
820
|
|
|
729
|
-
|
|
730
|
-
all_values.sort() # Sort for consistent pagination
|
|
731
|
-
total_count = len(all_values)
|
|
732
|
-
|
|
733
|
-
paginated_values = all_values[from_ : from_ + size]
|
|
734
|
-
|
|
735
|
-
return paginated_values, total_count
|
|
821
|
+
return self._compute_field_unique_values(agg_result, search_term, from_, size)
|
|
736
822
|
|
|
737
823
|
@staticmethod
|
|
738
824
|
def _to_data_object(document: Document) -> dict[str, Any]:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
5
|
import base64
|
|
6
|
+
import importlib.metadata
|
|
6
7
|
import logging
|
|
7
8
|
import os
|
|
8
9
|
from collections.abc import Generator
|
|
@@ -43,6 +44,7 @@ from haystack_integrations.document_stores.weaviate.auth import AuthApiKey
|
|
|
43
44
|
from haystack_integrations.document_stores.weaviate.document_store import (
|
|
44
45
|
DOCUMENT_COLLECTION_PROPERTIES,
|
|
45
46
|
WeaviateDocumentStore,
|
|
47
|
+
_integration_header_value,
|
|
46
48
|
)
|
|
47
49
|
|
|
48
50
|
|
|
@@ -81,7 +83,10 @@ def test_client_connects_to_weaviate_cloud(mock_connect, monkeypatch):
|
|
|
81
83
|
|
|
82
84
|
mock_connect.assert_called_once()
|
|
83
85
|
_args, kwargs = mock_connect.call_args
|
|
84
|
-
assert kwargs["headers"] == {
|
|
86
|
+
assert kwargs["headers"] == {
|
|
87
|
+
"X-HuggingFace-Api-Key": "k",
|
|
88
|
+
"X-Weaviate-Client-Integration": _integration_header_value(),
|
|
89
|
+
}
|
|
85
90
|
|
|
86
91
|
|
|
87
92
|
@pytest.mark.asyncio
|
|
@@ -100,9 +105,84 @@ async def test_async_client_connects_to_weaviate_cloud(mock_connect, monkeypatch
|
|
|
100
105
|
mock_client.collections.exists = exists
|
|
101
106
|
mock_connect.return_value = mock_client
|
|
102
107
|
|
|
103
|
-
ds = WeaviateDocumentStore(
|
|
108
|
+
ds = WeaviateDocumentStore(
|
|
109
|
+
url="rAnD0m.something.weaviate.cloud",
|
|
110
|
+
auth_client_secret=AuthApiKey(),
|
|
111
|
+
additional_headers={"X-HuggingFace-Api-Key": "k"},
|
|
112
|
+
)
|
|
104
113
|
assert await ds.async_client is mock_client
|
|
114
|
+
|
|
105
115
|
mock_connect.assert_called_once()
|
|
116
|
+
_args, kwargs = mock_connect.call_args
|
|
117
|
+
assert kwargs["headers"] == {
|
|
118
|
+
"X-HuggingFace-Api-Key": "k",
|
|
119
|
+
"X-Weaviate-Client-Integration": _integration_header_value(),
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@patch("haystack_integrations.document_stores.weaviate.document_store.weaviate.WeaviateClient")
|
|
124
|
+
def test_client_sends_integration_header(mock_weaviate_client_class):
|
|
125
|
+
mock_client = MagicMock()
|
|
126
|
+
mock_client.collections.exists.return_value = True
|
|
127
|
+
mock_weaviate_client_class.return_value = mock_client
|
|
128
|
+
|
|
129
|
+
ds = WeaviateDocumentStore(url="http://localhost:8080")
|
|
130
|
+
ds.client # noqa: B018
|
|
131
|
+
|
|
132
|
+
headers = mock_weaviate_client_class.call_args.kwargs["additional_headers"]
|
|
133
|
+
assert headers["X-Weaviate-Client-Integration"].startswith("haystack-python/")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@pytest.mark.asyncio
|
|
137
|
+
@patch("haystack_integrations.document_stores.weaviate.document_store.weaviate.WeaviateAsyncClient")
|
|
138
|
+
async def test_async_client_sends_integration_header(mock_weaviate_async_client_class):
|
|
139
|
+
mock_client = MagicMock()
|
|
140
|
+
|
|
141
|
+
async def connect() -> None:
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
async def exists(_name: str) -> bool:
|
|
145
|
+
return True
|
|
146
|
+
|
|
147
|
+
mock_client.connect = connect
|
|
148
|
+
mock_client.collections.exists = exists
|
|
149
|
+
mock_weaviate_async_client_class.return_value = mock_client
|
|
150
|
+
|
|
151
|
+
ds = WeaviateDocumentStore(url="http://localhost:8080")
|
|
152
|
+
await ds.async_client
|
|
153
|
+
|
|
154
|
+
headers = mock_weaviate_async_client_class.call_args.kwargs["additional_headers"]
|
|
155
|
+
assert headers["X-Weaviate-Client-Integration"].startswith("haystack-python/")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@pytest.mark.parametrize("header_name", ["X-Weaviate-Client-Integration", "x-weaviate-client-integration"])
|
|
159
|
+
@patch("haystack_integrations.document_stores.weaviate.document_store.weaviate.WeaviateClient")
|
|
160
|
+
def test_user_supplied_integration_header_wins(mock_weaviate_client_class, header_name):
|
|
161
|
+
mock_client = MagicMock()
|
|
162
|
+
mock_client.collections.exists.return_value = True
|
|
163
|
+
mock_weaviate_client_class.return_value = mock_client
|
|
164
|
+
|
|
165
|
+
ds = WeaviateDocumentStore(url="http://localhost:8080", additional_headers={header_name: "custom/1.0"})
|
|
166
|
+
ds.client # noqa: B018
|
|
167
|
+
|
|
168
|
+
headers = mock_weaviate_client_class.call_args.kwargs["additional_headers"]
|
|
169
|
+
assert headers == {header_name: "custom/1.0"}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_integration_header_value_falls_back_to_unknown_version(monkeypatch):
|
|
173
|
+
def raise_package_not_found(_package_name: str) -> str:
|
|
174
|
+
raise importlib.metadata.PackageNotFoundError
|
|
175
|
+
|
|
176
|
+
monkeypatch.setattr(importlib.metadata, "version", raise_package_not_found)
|
|
177
|
+
|
|
178
|
+
assert _integration_header_value() == "haystack-python/unknown"
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_integration_header_is_not_serialized():
|
|
182
|
+
assert WeaviateDocumentStore().to_dict()["init_parameters"]["additional_headers"] is None
|
|
183
|
+
|
|
184
|
+
ds = WeaviateDocumentStore(additional_headers={"X-HuggingFace-Api-Key": "k"})
|
|
185
|
+
assert ds.to_dict()["init_parameters"]["additional_headers"] == {"X-HuggingFace-Api-Key": "k"}
|
|
106
186
|
|
|
107
187
|
|
|
108
188
|
def test_to_data_object_with_sparse_embedding_logs_warning(caplog):
|
|
@@ -325,7 +405,10 @@ class TestWeaviateDocumentStore(
|
|
|
325
405
|
mock_weaviate_client_class.assert_called_once_with(
|
|
326
406
|
auth_client_secret=AuthApiKey().resolve_value(),
|
|
327
407
|
connection_params=None,
|
|
328
|
-
additional_headers={
|
|
408
|
+
additional_headers={
|
|
409
|
+
"X-HuggingFace-Api-Key": "MY_HUGGINGFACE_KEY",
|
|
410
|
+
"X-Weaviate-Client-Integration": _integration_header_value(),
|
|
411
|
+
},
|
|
329
412
|
embedded_options=EmbeddedOptions(
|
|
330
413
|
persistence_data_path=DEFAULT_PERSISTENCE_DATA_PATH,
|
|
331
414
|
binary_path=DEFAULT_BINARY_PATH,
|
|
@@ -1144,60 +1227,59 @@ class TestWeaviateDocumentStore(
|
|
|
1144
1227
|
metadata_fields=["nonexistent_field"],
|
|
1145
1228
|
)
|
|
1146
1229
|
|
|
1147
|
-
|
|
1148
|
-
docs = [
|
|
1149
|
-
Document(content="Doc 1", meta={"category": "TypeA"}),
|
|
1150
|
-
Document(content="Doc 2", meta={"category": "TypeB"}),
|
|
1151
|
-
]
|
|
1152
|
-
document_store.write_documents(docs)
|
|
1153
|
-
|
|
1154
|
-
values, total_count = document_store.get_metadata_field_unique_values("meta.category")
|
|
1155
|
-
assert total_count == 2
|
|
1156
|
-
assert set(values) == {"TypeA", "TypeB"}
|
|
1230
|
+
# --- Overrides of mixin tests to account for Weaviate-specific behaviour ---
|
|
1157
1231
|
|
|
1158
|
-
def
|
|
1232
|
+
def test_get_metadata_field_unique_values_preserves_type(self, document_store):
|
|
1233
|
+
"""
|
|
1234
|
+
Override: weaviate-client has no wire-protocol field for a scalar int - google.protobuf.Struct's
|
|
1235
|
+
Value type only has `number_value` (a double), so a metadata int is indistinguishable from a
|
|
1236
|
+
float by the time it reaches Weaviate. GroupByAggregate consequently always returns numeric
|
|
1237
|
+
group keys as float, regardless of the field's declared schema type.
|
|
1238
|
+
"""
|
|
1159
1239
|
docs = [
|
|
1160
|
-
Document(content="
|
|
1161
|
-
Document(content="
|
|
1162
|
-
Document(content="
|
|
1163
|
-
Document(content="JavaScript tutorial", meta={"category": "TypeD"}),
|
|
1240
|
+
Document(content="Doc 1", meta={"priority": 1}),
|
|
1241
|
+
Document(content="Doc 2", meta={"priority": 2}),
|
|
1242
|
+
Document(content="Doc 3", meta={"priority": 1}),
|
|
1164
1243
|
]
|
|
1165
1244
|
document_store.write_documents(docs)
|
|
1166
1245
|
|
|
1167
|
-
values, total_count = document_store.get_metadata_field_unique_values(
|
|
1246
|
+
values, total_count = document_store.get_metadata_field_unique_values(metadata_field="priority")
|
|
1247
|
+
|
|
1248
|
+
assert set(values) == {1, 2}
|
|
1249
|
+
assert all(isinstance(value, float) for value in values) # not int - see docstring
|
|
1168
1250
|
assert total_count == 2
|
|
1169
|
-
assert set(values) == {"TypeA", "TypeC"}
|
|
1170
1251
|
|
|
1171
|
-
def
|
|
1252
|
+
def test_get_metadata_field_unique_values_distinct_types(self, document_store):
|
|
1253
|
+
"""
|
|
1254
|
+
Override: the base mixin test stores int, float, str and bool under the *same* metadata field
|
|
1255
|
+
name and expects all four back as distinct values. Weaviate's collection schema fields are
|
|
1256
|
+
strongly single-typed, so a field can't simultaneously hold int/str/float/bool values.
|
|
1257
|
+
|
|
1258
|
+
This adapts the same intent - int, float, str and bool must come back as distinct, unmangled
|
|
1259
|
+
types via get_metadata_field_unique_values() - using one field per type instead of one shared
|
|
1260
|
+
field, which is what Weaviate can actually support.
|
|
1261
|
+
"""
|
|
1172
1262
|
docs = [
|
|
1173
|
-
Document(content="Doc 1", meta={"
|
|
1174
|
-
Document(content="Doc 2", meta={"
|
|
1175
|
-
Document(content="Doc 3", meta={"
|
|
1176
|
-
Document(content="Doc 4", meta={"
|
|
1177
|
-
Document(content="Doc 5", meta={"category": "TypeE"}),
|
|
1263
|
+
Document(content="Doc 1", meta={"priority_int": 1}),
|
|
1264
|
+
Document(content="Doc 2", meta={"priority_str": "1"}),
|
|
1265
|
+
Document(content="Doc 3", meta={"priority_float": 1.5}),
|
|
1266
|
+
Document(content="Doc 4", meta={"priority_bool": True}),
|
|
1178
1267
|
]
|
|
1179
1268
|
document_store.write_documents(docs)
|
|
1180
1269
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
assert
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
def test_get_metadata_field_unique_values_empty_result(self, document_store):
|
|
1196
|
-
values, total_count = document_store.get_metadata_field_unique_values("category")
|
|
1197
|
-
assert total_count == 0
|
|
1198
|
-
assert values == []
|
|
1199
|
-
|
|
1200
|
-
# --- Overrides of mixin tests to account for Weaviate-specific behaviour ---
|
|
1270
|
+
int_values, int_count = document_store.get_metadata_field_unique_values(metadata_field="priority_int")
|
|
1271
|
+
str_values, str_count = document_store.get_metadata_field_unique_values(metadata_field="priority_str")
|
|
1272
|
+
float_values, float_count = document_store.get_metadata_field_unique_values(metadata_field="priority_float")
|
|
1273
|
+
bool_values, bool_count = document_store.get_metadata_field_unique_values(metadata_field="priority_bool")
|
|
1274
|
+
|
|
1275
|
+
assert (int_count, str_count, float_count, bool_count) == (1, 1, 1, 1)
|
|
1276
|
+
# int type fidelity isn't asserted here: GroupByAggregate returns numeric group keys as float
|
|
1277
|
+
# regardless of the field's declared schema type, the same known gap covered by
|
|
1278
|
+
# test_get_metadata_field_unique_values_preserves_type - not specific to field-sharing.
|
|
1279
|
+
assert int_values == [1]
|
|
1280
|
+
assert str_values == ["1"] and type(str_values[0]) is str
|
|
1281
|
+
assert float_values == [1.5] and type(float_values[0]) is float
|
|
1282
|
+
assert bool_values == [True] and type(bool_values[0]) is bool
|
|
1201
1283
|
|
|
1202
1284
|
def test_count_documents_by_filter_simple(self, document_store):
|
|
1203
1285
|
docs = [
|
|
@@ -440,65 +440,69 @@ class TestWeaviateDocumentStoreAsync(
|
|
|
440
440
|
metadata_fields=["nonexistent_field"],
|
|
441
441
|
)
|
|
442
442
|
|
|
443
|
-
|
|
444
|
-
async def test_get_metadata_field_unique_values_async_with_meta_prefix(self, document_store):
|
|
445
|
-
docs = [
|
|
446
|
-
Document(content="Doc 1", meta={"category": "TypeA"}),
|
|
447
|
-
Document(content="Doc 2", meta={"category": "TypeB"}),
|
|
448
|
-
]
|
|
449
|
-
await document_store.write_documents_async(docs)
|
|
450
|
-
|
|
451
|
-
values, total_count = await document_store.get_metadata_field_unique_values_async("meta.category")
|
|
452
|
-
assert total_count == 2
|
|
453
|
-
assert set(values) == {"TypeA", "TypeB"}
|
|
443
|
+
# --- Overrides of mixin tests to account for Weaviate-specific behaviour ---
|
|
454
444
|
|
|
455
445
|
@pytest.mark.asyncio
|
|
456
|
-
async def
|
|
446
|
+
async def test_get_metadata_field_unique_values_preserves_type_async(self, document_store):
|
|
447
|
+
"""
|
|
448
|
+
Override: weaviate-client has no wire-protocol field for a scalar int - google.protobuf.Struct's
|
|
449
|
+
Value type only has `number_value` (a double), so a metadata int is indistinguishable from a
|
|
450
|
+
float by the time it reaches Weaviate. GroupByAggregate consequently always returns numeric
|
|
451
|
+
group keys as float, regardless of the field's declared schema type.
|
|
452
|
+
"""
|
|
457
453
|
docs = [
|
|
458
|
-
Document(content="
|
|
459
|
-
Document(content="
|
|
460
|
-
Document(content="
|
|
461
|
-
Document(content="JavaScript tutorial", meta={"category": "TypeD"}),
|
|
454
|
+
Document(content="Doc 1", meta={"priority": 1}),
|
|
455
|
+
Document(content="Doc 2", meta={"priority": 2}),
|
|
456
|
+
Document(content="Doc 3", meta={"priority": 1}),
|
|
462
457
|
]
|
|
463
458
|
await document_store.write_documents_async(docs)
|
|
464
459
|
|
|
465
|
-
values, total_count = await document_store.get_metadata_field_unique_values_async(
|
|
466
|
-
|
|
467
|
-
)
|
|
460
|
+
values, total_count = await document_store.get_metadata_field_unique_values_async(metadata_field="priority")
|
|
461
|
+
|
|
462
|
+
assert set(values) == {1, 2}
|
|
463
|
+
assert all(isinstance(value, float) for value in values) # not int - see docstring
|
|
468
464
|
assert total_count == 2
|
|
469
|
-
assert set(values) == {"TypeA", "TypeC"}
|
|
470
465
|
|
|
471
466
|
@pytest.mark.asyncio
|
|
472
|
-
async def
|
|
467
|
+
async def test_get_metadata_field_unique_values_distinct_types_async(self, document_store):
|
|
468
|
+
"""
|
|
469
|
+
Override: the base mixin test stores int, float, str and bool under the *same* metadata field
|
|
470
|
+
name and expects all four back as distinct values. Weaviate's collection schema fields are
|
|
471
|
+
strongly single-typed, so a field can't simultaneously hold int/str/float/bool values.
|
|
472
|
+
|
|
473
|
+
This adapts the same intent - int, float, str and bool must come back as distinct, unmangled
|
|
474
|
+
types via get_metadata_field_unique_values_async() - using one field per type instead of one
|
|
475
|
+
shared field, which is what Weaviate can actually support.
|
|
476
|
+
"""
|
|
473
477
|
docs = [
|
|
474
|
-
Document(content="Doc 1", meta={"
|
|
475
|
-
Document(content="Doc 2", meta={"
|
|
476
|
-
Document(content="Doc 3", meta={"
|
|
477
|
-
Document(content="Doc 4", meta={"
|
|
478
|
-
Document(content="Doc 5", meta={"category": "TypeE"}),
|
|
478
|
+
Document(content="Doc 1", meta={"priority_int": 1}),
|
|
479
|
+
Document(content="Doc 2", meta={"priority_str": "1"}),
|
|
480
|
+
Document(content="Doc 3", meta={"priority_float": 1.5}),
|
|
481
|
+
Document(content="Doc 4", meta={"priority_bool": True}),
|
|
479
482
|
]
|
|
480
483
|
await document_store.write_documents_async(docs)
|
|
481
484
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
with pytest.raises(ValueError, match="not found in collection schema"):
|
|
495
|
-
await document_store.get_metadata_field_unique_values_async("nonexistent_field")
|
|
485
|
+
int_values, int_count = await document_store.get_metadata_field_unique_values_async(
|
|
486
|
+
metadata_field="priority_int"
|
|
487
|
+
)
|
|
488
|
+
str_values, str_count = await document_store.get_metadata_field_unique_values_async(
|
|
489
|
+
metadata_field="priority_str"
|
|
490
|
+
)
|
|
491
|
+
float_values, float_count = await document_store.get_metadata_field_unique_values_async(
|
|
492
|
+
metadata_field="priority_float"
|
|
493
|
+
)
|
|
494
|
+
bool_values, bool_count = await document_store.get_metadata_field_unique_values_async(
|
|
495
|
+
metadata_field="priority_bool"
|
|
496
|
+
)
|
|
496
497
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
assert
|
|
498
|
+
assert (int_count, str_count, float_count, bool_count) == (1, 1, 1, 1)
|
|
499
|
+
# int type fidelity isn't asserted here: GroupByAggregate returns numeric group keys as float
|
|
500
|
+
# regardless of the field's declared schema type, the same known gap covered by
|
|
501
|
+
# test_get_metadata_field_unique_values_preserves_type_async - not specific to field-sharing.
|
|
502
|
+
assert int_values == [1]
|
|
503
|
+
assert str_values == ["1"] and type(str_values[0]) is str
|
|
504
|
+
assert float_values == [1.5] and type(float_values[0]) is float
|
|
505
|
+
assert bool_values == [True] and type(bool_values[0]) is bool
|
|
502
506
|
|
|
503
507
|
@pytest.mark.asyncio
|
|
504
508
|
async def test_delete_all_documents_excessive_batch_size_async(
|
|
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
|
|
File without changes
|