matrixone-python-sdk 0.1.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.
- matrixone/__init__.py +155 -0
- matrixone/account.py +723 -0
- matrixone/async_client.py +3913 -0
- matrixone/async_metadata_manager.py +311 -0
- matrixone/async_orm.py +123 -0
- matrixone/async_vector_index_manager.py +633 -0
- matrixone/base_client.py +208 -0
- matrixone/client.py +4672 -0
- matrixone/config.py +452 -0
- matrixone/connection_hooks.py +286 -0
- matrixone/exceptions.py +89 -0
- matrixone/logger.py +782 -0
- matrixone/metadata.py +820 -0
- matrixone/moctl.py +219 -0
- matrixone/orm.py +2277 -0
- matrixone/pitr.py +646 -0
- matrixone/pubsub.py +771 -0
- matrixone/restore.py +411 -0
- matrixone/search_vector_index.py +1176 -0
- matrixone/snapshot.py +550 -0
- matrixone/sql_builder.py +844 -0
- matrixone/sqlalchemy_ext/__init__.py +161 -0
- matrixone/sqlalchemy_ext/adapters.py +163 -0
- matrixone/sqlalchemy_ext/dialect.py +534 -0
- matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
- matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
- matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
- matrixone/sqlalchemy_ext/ivf_config.py +252 -0
- matrixone/sqlalchemy_ext/table_builder.py +351 -0
- matrixone/sqlalchemy_ext/vector_index.py +1721 -0
- matrixone/sqlalchemy_ext/vector_type.py +948 -0
- matrixone/version.py +580 -0
- matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
- matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
- matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
- matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
- matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
- matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +19 -0
- tests/offline/__init__.py +20 -0
- tests/offline/conftest.py +77 -0
- tests/offline/test_account.py +703 -0
- tests/offline/test_async_client_query_comprehensive.py +1218 -0
- tests/offline/test_basic.py +54 -0
- tests/offline/test_case_sensitivity.py +227 -0
- tests/offline/test_connection_hooks_offline.py +287 -0
- tests/offline/test_dialect_schema_handling.py +609 -0
- tests/offline/test_explain_methods.py +346 -0
- tests/offline/test_filter_logical_in.py +237 -0
- tests/offline/test_fulltext_search_comprehensive.py +795 -0
- tests/offline/test_ivf_config.py +249 -0
- tests/offline/test_join_methods.py +281 -0
- tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
- tests/offline/test_logical_in_method.py +237 -0
- tests/offline/test_matrixone_version_parsing.py +264 -0
- tests/offline/test_metadata_offline.py +557 -0
- tests/offline/test_moctl.py +300 -0
- tests/offline/test_moctl_simple.py +251 -0
- tests/offline/test_model_support_offline.py +359 -0
- tests/offline/test_model_support_simple.py +225 -0
- tests/offline/test_pinecone_filter_offline.py +377 -0
- tests/offline/test_pitr.py +585 -0
- tests/offline/test_pubsub.py +712 -0
- tests/offline/test_query_update.py +283 -0
- tests/offline/test_restore.py +445 -0
- tests/offline/test_snapshot_comprehensive.py +384 -0
- tests/offline/test_sql_escaping_edge_cases.py +551 -0
- tests/offline/test_sqlalchemy_integration.py +382 -0
- tests/offline/test_sqlalchemy_vector_integration.py +434 -0
- tests/offline/test_table_builder.py +198 -0
- tests/offline/test_unified_filter.py +398 -0
- tests/offline/test_unified_transaction.py +495 -0
- tests/offline/test_vector_index.py +238 -0
- tests/offline/test_vector_operations.py +688 -0
- tests/offline/test_vector_type.py +174 -0
- tests/offline/test_version_core.py +328 -0
- tests/offline/test_version_management.py +372 -0
- tests/offline/test_version_standalone.py +652 -0
- tests/online/__init__.py +20 -0
- tests/online/conftest.py +216 -0
- tests/online/test_account_management.py +194 -0
- tests/online/test_advanced_features.py +344 -0
- tests/online/test_async_client_interfaces.py +330 -0
- tests/online/test_async_client_online.py +285 -0
- tests/online/test_async_model_insert_online.py +293 -0
- tests/online/test_async_orm_online.py +300 -0
- tests/online/test_async_simple_query_online.py +802 -0
- tests/online/test_async_transaction_simple_query.py +300 -0
- tests/online/test_basic_connection.py +130 -0
- tests/online/test_client_online.py +238 -0
- tests/online/test_config.py +90 -0
- tests/online/test_config_validation.py +123 -0
- tests/online/test_connection_hooks_new_online.py +217 -0
- tests/online/test_dialect_schema_handling_online.py +331 -0
- tests/online/test_filter_logical_in_online.py +374 -0
- tests/online/test_fulltext_comprehensive.py +1773 -0
- tests/online/test_fulltext_label_online.py +433 -0
- tests/online/test_fulltext_search_online.py +842 -0
- tests/online/test_ivf_stats_online.py +506 -0
- tests/online/test_logger_integration.py +311 -0
- tests/online/test_matrixone_query_orm.py +540 -0
- tests/online/test_metadata_online.py +579 -0
- tests/online/test_model_insert_online.py +255 -0
- tests/online/test_mysql_driver_validation.py +213 -0
- tests/online/test_orm_advanced_features.py +2022 -0
- tests/online/test_orm_cte_integration.py +269 -0
- tests/online/test_orm_online.py +270 -0
- tests/online/test_pinecone_filter.py +708 -0
- tests/online/test_pubsub_operations.py +352 -0
- tests/online/test_query_methods.py +225 -0
- tests/online/test_query_update_online.py +433 -0
- tests/online/test_search_vector_index.py +557 -0
- tests/online/test_simple_fulltext_online.py +915 -0
- tests/online/test_snapshot_comprehensive.py +998 -0
- tests/online/test_sqlalchemy_engine_integration.py +336 -0
- tests/online/test_sqlalchemy_integration.py +425 -0
- tests/online/test_transaction_contexts.py +1219 -0
- tests/online/test_transaction_insert_methods.py +356 -0
- tests/online/test_transaction_query_methods.py +288 -0
- tests/online/test_unified_filter_online.py +529 -0
- tests/online/test_vector_comprehensive.py +706 -0
- tests/online/test_version_management.py +291 -0
@@ -0,0 +1,377 @@
|
|
1
|
+
# Copyright 2021 - 2022 Matrix Origin
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""
|
16
|
+
Offline tests for Pinecone-compatible filter functionality.
|
17
|
+
These tests focus on logic validation, parameter handling, and result structure
|
18
|
+
without requiring actual database connections.
|
19
|
+
"""
|
20
|
+
|
21
|
+
import pytest
|
22
|
+
from unittest.mock import Mock, MagicMock, patch
|
23
|
+
from typing import List, Dict, Any
|
24
|
+
from matrixone.search_vector_index import PineconeCompatibleIndex, QueryResponse, VectorMatch
|
25
|
+
|
26
|
+
|
27
|
+
class TestPineconeFilterOffline:
|
28
|
+
"""Offline tests for Pinecone-compatible filter functionality"""
|
29
|
+
|
30
|
+
@pytest.fixture
|
31
|
+
def mock_client(self):
|
32
|
+
"""Create mock client for testing"""
|
33
|
+
client = Mock()
|
34
|
+
client.execute = Mock()
|
35
|
+
client.vector_query = Mock()
|
36
|
+
client.vector_query.similarity_search = Mock()
|
37
|
+
return client
|
38
|
+
|
39
|
+
@pytest.fixture
|
40
|
+
def mock_index(self, mock_client):
|
41
|
+
"""Create mock Pinecone-compatible index"""
|
42
|
+
index = PineconeCompatibleIndex(mock_client, "test_table", "embedding")
|
43
|
+
return index
|
44
|
+
|
45
|
+
def test_filter_parsing_empty_dict(self, mock_index):
|
46
|
+
"""Test filter parsing with empty dictionary"""
|
47
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter({})
|
48
|
+
assert where_conditions == []
|
49
|
+
assert where_params == []
|
50
|
+
|
51
|
+
def test_filter_parsing_none_value(self, mock_index):
|
52
|
+
"""Test filter parsing with None value"""
|
53
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(None)
|
54
|
+
assert where_conditions == []
|
55
|
+
assert where_params == []
|
56
|
+
|
57
|
+
def test_filter_parsing_basic_equality(self, mock_index):
|
58
|
+
"""Test filter parsing with basic equality"""
|
59
|
+
filter_dict = {"genre": "action"}
|
60
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
61
|
+
assert where_conditions == ["genre = ?"]
|
62
|
+
assert where_params == ["action"]
|
63
|
+
|
64
|
+
def test_filter_parsing_operators(self, mock_index):
|
65
|
+
"""Test filter parsing with various operators"""
|
66
|
+
# Test $eq
|
67
|
+
filter_dict = {"rating": {"$eq": 8.5}}
|
68
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
69
|
+
assert where_conditions == ["rating = ?"]
|
70
|
+
assert where_params == [8.5]
|
71
|
+
|
72
|
+
# Test $ne
|
73
|
+
filter_dict = {"genre": {"$ne": "horror"}}
|
74
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
75
|
+
assert where_conditions == ["genre != ?"]
|
76
|
+
assert where_params == ["horror"]
|
77
|
+
|
78
|
+
# Test $gt
|
79
|
+
filter_dict = {"year": {"$gt": 2000}}
|
80
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
81
|
+
assert where_conditions == ["year > ?"]
|
82
|
+
assert where_params == [2000]
|
83
|
+
|
84
|
+
# Test $gte
|
85
|
+
filter_dict = {"rating": {"$gte": 8.0}}
|
86
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
87
|
+
assert where_conditions == ["rating >= ?"]
|
88
|
+
assert where_params == [8.0]
|
89
|
+
|
90
|
+
# Test $lt
|
91
|
+
filter_dict = {"year": {"$lt": 2010}}
|
92
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
93
|
+
assert where_conditions == ["year < ?"]
|
94
|
+
assert where_params == [2010]
|
95
|
+
|
96
|
+
# Test $lte
|
97
|
+
filter_dict = {"rating": {"$lte": 9.0}}
|
98
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
99
|
+
assert where_conditions == ["rating <= ?"]
|
100
|
+
assert where_params == [9.0]
|
101
|
+
|
102
|
+
def test_filter_parsing_in_operator(self, mock_index):
|
103
|
+
"""Test filter parsing with $in operator"""
|
104
|
+
filter_dict = {"genre": {"$in": ["action", "sci-fi", "drama"]}}
|
105
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
106
|
+
assert where_conditions == ["genre IN (?,?,?)"]
|
107
|
+
assert where_params == ["action", "sci-fi", "drama"]
|
108
|
+
|
109
|
+
def test_filter_parsing_nin_operator(self, mock_index):
|
110
|
+
"""Test filter parsing with $nin operator"""
|
111
|
+
filter_dict = {"genre": {"$nin": ["horror", "thriller"]}}
|
112
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
113
|
+
assert where_conditions == ["genre NOT IN (?,?)"]
|
114
|
+
assert where_params == ["horror", "thriller"]
|
115
|
+
|
116
|
+
def test_filter_parsing_empty_in_list(self, mock_index):
|
117
|
+
"""Test filter parsing with empty $in list"""
|
118
|
+
filter_dict = {"genre": {"$in": []}}
|
119
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
120
|
+
assert where_conditions == ["1=0"] # Always false condition
|
121
|
+
assert where_params == []
|
122
|
+
|
123
|
+
def test_filter_parsing_empty_nin_list(self, mock_index):
|
124
|
+
"""Test filter parsing with empty $nin list"""
|
125
|
+
filter_dict = {"genre": {"$nin": []}}
|
126
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
127
|
+
assert where_conditions == ["1=1"] # Always true condition
|
128
|
+
assert where_params == []
|
129
|
+
|
130
|
+
def test_filter_parsing_and_operator(self, mock_index):
|
131
|
+
"""Test filter parsing with $and operator"""
|
132
|
+
filter_dict = {"$and": [{"genre": "action"}, {"year": {"$gte": 2000}}]}
|
133
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
134
|
+
assert where_conditions == ["(genre = ? AND year >= ?)"]
|
135
|
+
assert where_params == ["action", 2000]
|
136
|
+
|
137
|
+
def test_filter_parsing_or_operator(self, mock_index):
|
138
|
+
"""Test filter parsing with $or operator"""
|
139
|
+
filter_dict = {"$or": [{"genre": "action"}, {"genre": "sci-fi"}]}
|
140
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
141
|
+
assert where_conditions == ["(genre = ? OR genre = ?)"]
|
142
|
+
assert where_params == ["action", "sci-fi"]
|
143
|
+
|
144
|
+
def test_filter_parsing_nested_conditions(self, mock_index):
|
145
|
+
"""Test filter parsing with nested $and and $or conditions"""
|
146
|
+
filter_dict = {
|
147
|
+
"$and": [
|
148
|
+
{"$or": [{"genre": "action"}, {"genre": "sci-fi"}]},
|
149
|
+
{"$and": [{"year": {"$gte": 2000}}, {"rating": {"$gte": 8.0}}]},
|
150
|
+
]
|
151
|
+
}
|
152
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
153
|
+
expected_condition = "((genre = ? OR genre = ?) AND (year >= ? AND rating >= ?))"
|
154
|
+
assert where_conditions == [expected_condition]
|
155
|
+
assert where_params == ["action", "sci-fi", 2000, 8.0]
|
156
|
+
|
157
|
+
def test_filter_parsing_special_characters(self, mock_index):
|
158
|
+
"""Test filter parsing with special characters"""
|
159
|
+
filter_dict = {"director": "Director with apostrophe's name"}
|
160
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
161
|
+
assert where_conditions == ["director = ?"]
|
162
|
+
assert where_params == ["Director with apostrophe's name"]
|
163
|
+
|
164
|
+
def test_filter_parsing_mixed_data_types(self, mock_index):
|
165
|
+
"""Test filter parsing with mixed data types"""
|
166
|
+
filter_dict = {"year": {"$in": [1999, "2010", 2008]}}
|
167
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
168
|
+
assert where_conditions == ["year IN (?,?,?)"]
|
169
|
+
assert where_params == [1999, "2010", 2008]
|
170
|
+
|
171
|
+
def test_filter_parsing_large_in_list(self, mock_index):
|
172
|
+
"""Test filter parsing with large $in list"""
|
173
|
+
large_list = [str(i) for i in range(1000)]
|
174
|
+
large_list.extend(["action", "sci-fi"])
|
175
|
+
filter_dict = {"genre": {"$in": large_list}}
|
176
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
177
|
+
|
178
|
+
# Check that placeholders are created correctly
|
179
|
+
expected_placeholders = ",".join(["?" for _ in range(1002)])
|
180
|
+
assert where_conditions == [f"genre IN ({expected_placeholders})"]
|
181
|
+
assert len(where_params) == 1002
|
182
|
+
|
183
|
+
def test_vector_dimension_validation(self, mock_index):
|
184
|
+
"""Test vector dimension validation logic"""
|
185
|
+
# Test with valid vector
|
186
|
+
valid_vector = [0.1] * 64
|
187
|
+
# This should not raise an exception during parsing
|
188
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter({"genre": "action"})
|
189
|
+
assert where_conditions == ["genre = ?"]
|
190
|
+
assert where_params == ["action"]
|
191
|
+
|
192
|
+
def test_top_k_validation_logic(self, mock_index):
|
193
|
+
"""Test top_k validation logic"""
|
194
|
+
# Test various top_k values
|
195
|
+
test_cases = [0, 1, 10, 100, 10000]
|
196
|
+
for top_k in test_cases:
|
197
|
+
# The logic should handle any positive integer
|
198
|
+
assert isinstance(top_k, int)
|
199
|
+
assert top_k >= 0
|
200
|
+
|
201
|
+
def test_query_response_structure(self):
|
202
|
+
"""Test QueryResponse structure validation"""
|
203
|
+
# Create mock matches
|
204
|
+
matches = [
|
205
|
+
VectorMatch(
|
206
|
+
id="1",
|
207
|
+
score=0.1,
|
208
|
+
metadata={"title": "Movie 1", "genre": "action"},
|
209
|
+
values=[0.1] * 64,
|
210
|
+
),
|
211
|
+
VectorMatch(
|
212
|
+
id="2",
|
213
|
+
score=0.2,
|
214
|
+
metadata={"title": "Movie 2", "genre": "sci-fi"},
|
215
|
+
values=[0.2] * 64,
|
216
|
+
),
|
217
|
+
]
|
218
|
+
|
219
|
+
response = QueryResponse(matches=matches, namespace="test", usage={"read_units": 2})
|
220
|
+
|
221
|
+
# Validate structure
|
222
|
+
assert len(response.matches) == 2
|
223
|
+
assert response.namespace == "test"
|
224
|
+
assert response.usage["read_units"] == 2
|
225
|
+
|
226
|
+
# Validate match structure
|
227
|
+
for match in response.matches:
|
228
|
+
assert hasattr(match, 'id')
|
229
|
+
assert hasattr(match, 'score')
|
230
|
+
assert hasattr(match, 'metadata')
|
231
|
+
assert hasattr(match, 'values')
|
232
|
+
assert isinstance(match.score, (int, float))
|
233
|
+
assert match.score >= 0
|
234
|
+
|
235
|
+
def test_vector_match_structure(self):
|
236
|
+
"""Test VectorMatch structure validation"""
|
237
|
+
match = VectorMatch(id="test_id", score=0.5, metadata={"key": "value"}, values=[0.1, 0.2, 0.3])
|
238
|
+
|
239
|
+
assert match.id == "test_id"
|
240
|
+
assert match.score == 0.5
|
241
|
+
assert match.metadata == {"key": "value"}
|
242
|
+
assert match.values == [0.1, 0.2, 0.3]
|
243
|
+
|
244
|
+
def test_result_ordering_logic(self):
|
245
|
+
"""Test result ordering logic validation"""
|
246
|
+
# Create matches with different scores
|
247
|
+
matches = [
|
248
|
+
VectorMatch(id="1", score=0.3, metadata={}, values=None),
|
249
|
+
VectorMatch(id="2", score=0.1, metadata={}, values=None),
|
250
|
+
VectorMatch(id="3", score=0.2, metadata={}, values=None),
|
251
|
+
]
|
252
|
+
|
253
|
+
# Sort by score (ascending for distance)
|
254
|
+
sorted_matches = sorted(matches, key=lambda x: x.score)
|
255
|
+
|
256
|
+
# Validate ordering
|
257
|
+
assert sorted_matches[0].score == 0.1
|
258
|
+
assert sorted_matches[1].score == 0.2
|
259
|
+
assert sorted_matches[2].score == 0.3
|
260
|
+
|
261
|
+
def test_usage_statistics_logic(self):
|
262
|
+
"""Test usage statistics logic validation"""
|
263
|
+
# Test various scenarios
|
264
|
+
test_cases = [
|
265
|
+
(0, 0), # top_k=0, expected_read_units=0
|
266
|
+
(1, 1), # top_k=1, expected_read_units=1
|
267
|
+
(5, 5), # top_k=5, expected_read_units=5
|
268
|
+
(100, 5), # top_k=100, but only 5 records available
|
269
|
+
]
|
270
|
+
|
271
|
+
for top_k, expected_read_units in test_cases:
|
272
|
+
# Simulate the logic
|
273
|
+
actual_read_units = min(top_k, 5) # Assuming 5 records available
|
274
|
+
assert actual_read_units == expected_read_units
|
275
|
+
|
276
|
+
def test_namespace_consistency_logic(self):
|
277
|
+
"""Test namespace consistency logic validation"""
|
278
|
+
test_namespaces = ["", "default", "test_namespace", "namespace_with_underscores"]
|
279
|
+
|
280
|
+
for namespace in test_namespaces:
|
281
|
+
# Namespace should be preserved as-is
|
282
|
+
assert isinstance(namespace, str)
|
283
|
+
# Should be able to handle any string value
|
284
|
+
processed_namespace = namespace
|
285
|
+
assert processed_namespace == namespace
|
286
|
+
|
287
|
+
def test_include_parameters_logic(self):
|
288
|
+
"""Test include_metadata and include_values parameter logic"""
|
289
|
+
# Test all combinations
|
290
|
+
test_cases = [
|
291
|
+
(True, True), # include both
|
292
|
+
(True, False), # include metadata only
|
293
|
+
(False, True), # include values only
|
294
|
+
(False, False), # include neither
|
295
|
+
]
|
296
|
+
|
297
|
+
for include_metadata, include_values in test_cases:
|
298
|
+
# Validate parameter types
|
299
|
+
assert isinstance(include_metadata, bool)
|
300
|
+
assert isinstance(include_values, bool)
|
301
|
+
|
302
|
+
# Test logic combinations
|
303
|
+
if include_metadata and include_values:
|
304
|
+
# Both included
|
305
|
+
pass
|
306
|
+
elif include_metadata and not include_values:
|
307
|
+
# Metadata only
|
308
|
+
pass
|
309
|
+
elif not include_metadata and include_values:
|
310
|
+
# Values only
|
311
|
+
pass
|
312
|
+
else:
|
313
|
+
# Neither included
|
314
|
+
pass
|
315
|
+
|
316
|
+
def test_vector_value_handling_logic(self):
|
317
|
+
"""Test vector value handling logic"""
|
318
|
+
# Test different vector representations
|
319
|
+
test_vectors = [
|
320
|
+
[0.1] * 64, # List of floats
|
321
|
+
[0.0] * 64, # Zero vector
|
322
|
+
[-0.1] * 64, # Negative values
|
323
|
+
[100.0] * 64, # Large values
|
324
|
+
[0.1, 0.2, 0.3], # Short vector (should be handled)
|
325
|
+
]
|
326
|
+
|
327
|
+
for vector in test_vectors:
|
328
|
+
# Validate vector structure
|
329
|
+
assert isinstance(vector, list)
|
330
|
+
assert all(isinstance(x, (int, float)) for x in vector)
|
331
|
+
|
332
|
+
# Test vector string conversion logic
|
333
|
+
vector_str = "[" + ",".join(map(str, vector)) + "]"
|
334
|
+
assert vector_str.startswith("[")
|
335
|
+
assert vector_str.endswith("]")
|
336
|
+
|
337
|
+
def test_error_handling_logic(self):
|
338
|
+
"""Test error handling logic for various edge cases"""
|
339
|
+
# Test unsupported operators
|
340
|
+
with pytest.raises(ValueError, match="Unsupported operator"):
|
341
|
+
mock_index = PineconeCompatibleIndex(Mock(), "test", "embedding")
|
342
|
+
mock_index._parse_pinecone_filter({"field": {"$unsupported": "value"}})
|
343
|
+
|
344
|
+
def test_consistency_logic(self, mock_index):
|
345
|
+
"""Test consistency logic for multiple operations"""
|
346
|
+
# Test that same input produces same output
|
347
|
+
filter_dict = {"genre": "action", "year": {"$gte": 2000}}
|
348
|
+
|
349
|
+
# Parse multiple times
|
350
|
+
results = []
|
351
|
+
for _ in range(3):
|
352
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
353
|
+
results.append((where_conditions, where_params))
|
354
|
+
|
355
|
+
# All results should be identical
|
356
|
+
for i in range(1, len(results)):
|
357
|
+
assert results[i] == results[0]
|
358
|
+
|
359
|
+
def test_edge_case_handling(self, mock_index):
|
360
|
+
"""Test edge case handling logic"""
|
361
|
+
# Test with very large numbers
|
362
|
+
large_number = 999999999
|
363
|
+
filter_dict = {"id": {"$eq": large_number}}
|
364
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
365
|
+
assert where_conditions == ["id = ?"]
|
366
|
+
assert where_params == [large_number]
|
367
|
+
|
368
|
+
# Test with very small numbers
|
369
|
+
small_number = 0.000001
|
370
|
+
filter_dict = {"rating": {"$eq": small_number}}
|
371
|
+
where_conditions, where_params = mock_index._parse_pinecone_filter(filter_dict)
|
372
|
+
assert where_conditions == ["rating = ?"]
|
373
|
+
assert where_params == [small_number]
|
374
|
+
|
375
|
+
|
376
|
+
if __name__ == "__main__":
|
377
|
+
pytest.main([__file__])
|