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,300 @@
|
|
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
|
+
Online tests for Async ORM functionality - tests actual database operations
|
17
|
+
"""
|
18
|
+
|
19
|
+
import pytest
|
20
|
+
import pytest_asyncio
|
21
|
+
import asyncio
|
22
|
+
import os
|
23
|
+
import sys
|
24
|
+
from datetime import datetime
|
25
|
+
|
26
|
+
# Add the matrixone package to the path
|
27
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
28
|
+
|
29
|
+
from matrixone import AsyncClient
|
30
|
+
from sqlalchemy import Column, Integer, String, DECIMAL
|
31
|
+
|
32
|
+
# SQLAlchemy compatibility import
|
33
|
+
try:
|
34
|
+
from sqlalchemy.orm import declarative_base
|
35
|
+
except ImportError:
|
36
|
+
from matrixone.orm import declarative_base
|
37
|
+
|
38
|
+
Base = declarative_base()
|
39
|
+
from matrixone.exceptions import QueryError
|
40
|
+
from .test_config import online_config
|
41
|
+
|
42
|
+
|
43
|
+
class AsyncUser(Base):
|
44
|
+
"""Test user model for async operations"""
|
45
|
+
|
46
|
+
__tablename__ = "test_async_users"
|
47
|
+
|
48
|
+
id = Column(Integer, primary_key=True)
|
49
|
+
name = Column(String(100))
|
50
|
+
email = Column(String(100))
|
51
|
+
age = Column(Integer)
|
52
|
+
|
53
|
+
def to_dict(self):
|
54
|
+
"""Convert model instance to dictionary"""
|
55
|
+
return {'id': self.id, 'name': self.name, 'email': self.email, 'age': self.age}
|
56
|
+
|
57
|
+
@classmethod
|
58
|
+
def from_dict(cls, data):
|
59
|
+
"""Create model instance from dictionary"""
|
60
|
+
return cls(**data)
|
61
|
+
|
62
|
+
|
63
|
+
class AsyncProduct(Base):
|
64
|
+
"""Test product model for async operations"""
|
65
|
+
|
66
|
+
__tablename__ = "test_async_products"
|
67
|
+
|
68
|
+
id = Column(Integer, primary_key=True)
|
69
|
+
name = Column(String(100))
|
70
|
+
price = Column(DECIMAL(10, 2))
|
71
|
+
category = Column(String(50))
|
72
|
+
|
73
|
+
def to_dict(self):
|
74
|
+
"""Convert model instance to dictionary"""
|
75
|
+
return {
|
76
|
+
'id': self.id,
|
77
|
+
'name': self.name,
|
78
|
+
'price': float(self.price) if self.price is not None else None,
|
79
|
+
'category': self.category,
|
80
|
+
}
|
81
|
+
|
82
|
+
@classmethod
|
83
|
+
def from_dict(cls, data):
|
84
|
+
"""Create model instance from dictionary"""
|
85
|
+
return cls(**data)
|
86
|
+
|
87
|
+
|
88
|
+
class TestAsyncORMOnline:
|
89
|
+
"""Online tests for Async ORM functionality"""
|
90
|
+
|
91
|
+
@pytest_asyncio.fixture(scope="function")
|
92
|
+
async def test_database(self):
|
93
|
+
"""Set up test database and return database name"""
|
94
|
+
return "test_async_orm_db"
|
95
|
+
|
96
|
+
@pytest_asyncio.fixture(scope="function")
|
97
|
+
async def test_async_client(self, test_database):
|
98
|
+
"""Create and connect AsyncClient for testing"""
|
99
|
+
host, port, user, password, _ = online_config.get_connection_params()
|
100
|
+
|
101
|
+
# First create the database using default connection
|
102
|
+
temp_client = AsyncClient()
|
103
|
+
await temp_client.connect(host, port, user, password, "test") # Connect to default database
|
104
|
+
await temp_client.execute(f"CREATE DATABASE IF NOT EXISTS {test_database}")
|
105
|
+
await temp_client.disconnect()
|
106
|
+
|
107
|
+
# Now connect to the test database
|
108
|
+
client = AsyncClient()
|
109
|
+
await client.connect(host, port, user, password, test_database)
|
110
|
+
|
111
|
+
try:
|
112
|
+
# Create test tables
|
113
|
+
await client.execute(
|
114
|
+
"""
|
115
|
+
CREATE TABLE IF NOT EXISTS test_async_users (
|
116
|
+
id INT PRIMARY KEY,
|
117
|
+
name VARCHAR(100),
|
118
|
+
email VARCHAR(100),
|
119
|
+
age INT
|
120
|
+
)
|
121
|
+
"""
|
122
|
+
)
|
123
|
+
|
124
|
+
await client.execute(
|
125
|
+
"""
|
126
|
+
CREATE TABLE IF NOT EXISTS test_async_products (
|
127
|
+
id INT PRIMARY KEY,
|
128
|
+
name VARCHAR(100),
|
129
|
+
price DECIMAL(10,2),
|
130
|
+
category VARCHAR(50)
|
131
|
+
)
|
132
|
+
"""
|
133
|
+
)
|
134
|
+
|
135
|
+
# Insert test data
|
136
|
+
await client.execute(
|
137
|
+
"""
|
138
|
+
INSERT INTO test_async_users VALUES
|
139
|
+
(1, 'Alice Johnson', 'alice@example.com', 28),
|
140
|
+
(2, 'Charlie Brown', 'charlie@example.com', 32),
|
141
|
+
(3, 'Diana Prince', 'diana@example.com', 27)
|
142
|
+
"""
|
143
|
+
)
|
144
|
+
|
145
|
+
await client.execute(
|
146
|
+
"""
|
147
|
+
INSERT INTO test_async_products VALUES
|
148
|
+
(1, 'Tablet', 299.99, 'Electronics'),
|
149
|
+
(2, 'Notebook', 5.99, 'Stationery'),
|
150
|
+
(3, 'Headphones', 149.99, 'Electronics')
|
151
|
+
"""
|
152
|
+
)
|
153
|
+
|
154
|
+
yield client
|
155
|
+
|
156
|
+
finally:
|
157
|
+
try:
|
158
|
+
await client.disconnect()
|
159
|
+
# Clean up database
|
160
|
+
cleanup_client = AsyncClient()
|
161
|
+
await cleanup_client.connect(host, port, user, password, "test")
|
162
|
+
await cleanup_client.execute(f"DROP DATABASE IF EXISTS {test_database}")
|
163
|
+
await cleanup_client.disconnect()
|
164
|
+
except Exception as e:
|
165
|
+
print(f"Warning: Failed to cleanup or disconnect: {e}")
|
166
|
+
|
167
|
+
@pytest.mark.asyncio
|
168
|
+
async def test_async_orm_query_basic_operations(self, test_async_client):
|
169
|
+
"""Test basic async ORM query operations"""
|
170
|
+
# Test all() method - like SQLAlchemy, no database parameter needed
|
171
|
+
users = await test_async_client.query(AsyncUser).all()
|
172
|
+
assert len(users) == 3
|
173
|
+
|
174
|
+
# Test first() method
|
175
|
+
first_user = await test_async_client.query(AsyncUser).first()
|
176
|
+
assert first_user is not None
|
177
|
+
assert isinstance(first_user, AsyncUser)
|
178
|
+
|
179
|
+
# Test filter_by() method
|
180
|
+
alice = await test_async_client.query(AsyncUser).filter_by(name="Alice Johnson").first()
|
181
|
+
assert alice is not None
|
182
|
+
assert alice.name == "Alice Johnson"
|
183
|
+
assert alice.email == "alice@example.com"
|
184
|
+
|
185
|
+
@pytest.mark.asyncio
|
186
|
+
async def test_async_orm_query_filtering(self, test_async_client):
|
187
|
+
"""Test async ORM query filtering"""
|
188
|
+
# Test filter_by with multiple conditions
|
189
|
+
electronics = await test_async_client.query(AsyncProduct).filter_by(category="Electronics").all()
|
190
|
+
assert len(electronics) == 2
|
191
|
+
|
192
|
+
# Test filter() method with conditions
|
193
|
+
expensive_products = await test_async_client.query(AsyncProduct).filter("price > ?", 100).all()
|
194
|
+
assert len(expensive_products) == 2
|
195
|
+
|
196
|
+
# Test combined filtering
|
197
|
+
expensive_electronics = (
|
198
|
+
await test_async_client.query(AsyncProduct).filter_by(category="Electronics").filter("price > ?", 100).all()
|
199
|
+
)
|
200
|
+
assert len(expensive_electronics) == 2
|
201
|
+
|
202
|
+
@pytest.mark.asyncio
|
203
|
+
async def test_async_orm_query_ordering_and_limiting(self, test_async_client):
|
204
|
+
"""Test async ORM query ordering and limiting"""
|
205
|
+
# Test order_by
|
206
|
+
users_by_age = await test_async_client.query(AsyncUser).order_by("age DESC").all()
|
207
|
+
assert len(users_by_age) == 3
|
208
|
+
# Should be ordered by age descending
|
209
|
+
assert users_by_age[0].age >= users_by_age[1].age
|
210
|
+
|
211
|
+
# Test limit
|
212
|
+
limited_users = await test_async_client.query(AsyncUser).limit(2).all()
|
213
|
+
assert len(limited_users) == 2
|
214
|
+
|
215
|
+
# Test combined ordering and limiting
|
216
|
+
top_products = await test_async_client.query(AsyncProduct).order_by("price DESC").limit(2).all()
|
217
|
+
assert len(top_products) == 2
|
218
|
+
assert top_products[0].price >= top_products[1].price
|
219
|
+
|
220
|
+
@pytest.mark.asyncio
|
221
|
+
async def test_async_orm_query_count(self, test_async_client):
|
222
|
+
"""Test async ORM query count functionality"""
|
223
|
+
# Test count() method
|
224
|
+
total_users = await test_async_client.query(AsyncUser).count()
|
225
|
+
assert total_users == 3
|
226
|
+
|
227
|
+
# Test count with filtering
|
228
|
+
electronics_count = await test_async_client.query(AsyncProduct).filter_by(category="Electronics").count()
|
229
|
+
assert electronics_count == 2
|
230
|
+
|
231
|
+
# Test count with complex filtering
|
232
|
+
expensive_count = await test_async_client.query(AsyncProduct).filter("price > ?", 50).count()
|
233
|
+
assert expensive_count == 2
|
234
|
+
|
235
|
+
@pytest.mark.asyncio
|
236
|
+
async def test_async_orm_query_parameter_handling(self, test_async_client):
|
237
|
+
"""Test async ORM query parameter handling"""
|
238
|
+
# Test string parameters
|
239
|
+
user = await test_async_client.query(AsyncUser).filter("name = ?", "Charlie Brown").first()
|
240
|
+
assert user is not None
|
241
|
+
assert user.name == "Charlie Brown"
|
242
|
+
|
243
|
+
# Test numeric parameters
|
244
|
+
user = await test_async_client.query(AsyncUser).filter("age = ?", 28).first()
|
245
|
+
assert user is not None
|
246
|
+
assert user.age == 28
|
247
|
+
|
248
|
+
# Test multiple parameters
|
249
|
+
user = await test_async_client.query(AsyncUser).filter("name = ? AND age > ?", "Diana Prince", 25).first()
|
250
|
+
assert user is not None
|
251
|
+
assert user.name == "Diana Prince"
|
252
|
+
assert user.age > 25
|
253
|
+
|
254
|
+
@pytest.mark.asyncio
|
255
|
+
async def test_async_orm_query_error_handling(self, test_async_client):
|
256
|
+
"""Test async ORM query error handling"""
|
257
|
+
# Test query with invalid condition
|
258
|
+
with pytest.raises(QueryError):
|
259
|
+
await test_async_client.query(AsyncUser).filter("invalid_syntax").all()
|
260
|
+
|
261
|
+
@pytest.mark.asyncio
|
262
|
+
async def test_async_orm_model_serialization(self, test_async_client):
|
263
|
+
"""Test async ORM model serialization methods"""
|
264
|
+
# Test model creation and serialization
|
265
|
+
user = AsyncUser(id=1, name="Test User", email="test@example.com", age=30)
|
266
|
+
|
267
|
+
# Test to_dict
|
268
|
+
user_dict = user.to_dict()
|
269
|
+
assert isinstance(user_dict, dict)
|
270
|
+
assert user_dict['id'] == 1
|
271
|
+
assert user_dict['name'] == "Test User"
|
272
|
+
|
273
|
+
# Test from_dict
|
274
|
+
new_user = AsyncUser.from_dict(user_dict)
|
275
|
+
assert new_user.id == user.id
|
276
|
+
assert new_user.name == user.name
|
277
|
+
assert new_user.email == user.email
|
278
|
+
assert new_user.age == user.age
|
279
|
+
|
280
|
+
@pytest.mark.asyncio
|
281
|
+
async def test_async_orm_concurrent_operations(self, test_async_client):
|
282
|
+
"""Test async ORM concurrent operations"""
|
283
|
+
# Test concurrent queries
|
284
|
+
tasks = [
|
285
|
+
test_async_client.query(AsyncUser).filter_by(name="Alice Johnson").first(),
|
286
|
+
test_async_client.query(AsyncUser).filter_by(name="Charlie Brown").first(),
|
287
|
+
test_async_client.query(AsyncUser).filter_by(name="Diana Prince").first(),
|
288
|
+
]
|
289
|
+
|
290
|
+
results = await asyncio.gather(*tasks)
|
291
|
+
|
292
|
+
# All queries should complete successfully
|
293
|
+
assert len(results) == 3
|
294
|
+
for result in results:
|
295
|
+
assert result is not None
|
296
|
+
assert isinstance(result, AsyncUser)
|
297
|
+
|
298
|
+
|
299
|
+
if __name__ == '__main__':
|
300
|
+
pytest.main([__file__])
|