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.
Files changed (122) hide show
  1. matrixone/__init__.py +155 -0
  2. matrixone/account.py +723 -0
  3. matrixone/async_client.py +3913 -0
  4. matrixone/async_metadata_manager.py +311 -0
  5. matrixone/async_orm.py +123 -0
  6. matrixone/async_vector_index_manager.py +633 -0
  7. matrixone/base_client.py +208 -0
  8. matrixone/client.py +4672 -0
  9. matrixone/config.py +452 -0
  10. matrixone/connection_hooks.py +286 -0
  11. matrixone/exceptions.py +89 -0
  12. matrixone/logger.py +782 -0
  13. matrixone/metadata.py +820 -0
  14. matrixone/moctl.py +219 -0
  15. matrixone/orm.py +2277 -0
  16. matrixone/pitr.py +646 -0
  17. matrixone/pubsub.py +771 -0
  18. matrixone/restore.py +411 -0
  19. matrixone/search_vector_index.py +1176 -0
  20. matrixone/snapshot.py +550 -0
  21. matrixone/sql_builder.py +844 -0
  22. matrixone/sqlalchemy_ext/__init__.py +161 -0
  23. matrixone/sqlalchemy_ext/adapters.py +163 -0
  24. matrixone/sqlalchemy_ext/dialect.py +534 -0
  25. matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
  26. matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
  27. matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
  28. matrixone/sqlalchemy_ext/ivf_config.py +252 -0
  29. matrixone/sqlalchemy_ext/table_builder.py +351 -0
  30. matrixone/sqlalchemy_ext/vector_index.py +1721 -0
  31. matrixone/sqlalchemy_ext/vector_type.py +948 -0
  32. matrixone/version.py +580 -0
  33. matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
  34. matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
  35. matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
  36. matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
  37. matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
  38. matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
  39. tests/__init__.py +19 -0
  40. tests/offline/__init__.py +20 -0
  41. tests/offline/conftest.py +77 -0
  42. tests/offline/test_account.py +703 -0
  43. tests/offline/test_async_client_query_comprehensive.py +1218 -0
  44. tests/offline/test_basic.py +54 -0
  45. tests/offline/test_case_sensitivity.py +227 -0
  46. tests/offline/test_connection_hooks_offline.py +287 -0
  47. tests/offline/test_dialect_schema_handling.py +609 -0
  48. tests/offline/test_explain_methods.py +346 -0
  49. tests/offline/test_filter_logical_in.py +237 -0
  50. tests/offline/test_fulltext_search_comprehensive.py +795 -0
  51. tests/offline/test_ivf_config.py +249 -0
  52. tests/offline/test_join_methods.py +281 -0
  53. tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
  54. tests/offline/test_logical_in_method.py +237 -0
  55. tests/offline/test_matrixone_version_parsing.py +264 -0
  56. tests/offline/test_metadata_offline.py +557 -0
  57. tests/offline/test_moctl.py +300 -0
  58. tests/offline/test_moctl_simple.py +251 -0
  59. tests/offline/test_model_support_offline.py +359 -0
  60. tests/offline/test_model_support_simple.py +225 -0
  61. tests/offline/test_pinecone_filter_offline.py +377 -0
  62. tests/offline/test_pitr.py +585 -0
  63. tests/offline/test_pubsub.py +712 -0
  64. tests/offline/test_query_update.py +283 -0
  65. tests/offline/test_restore.py +445 -0
  66. tests/offline/test_snapshot_comprehensive.py +384 -0
  67. tests/offline/test_sql_escaping_edge_cases.py +551 -0
  68. tests/offline/test_sqlalchemy_integration.py +382 -0
  69. tests/offline/test_sqlalchemy_vector_integration.py +434 -0
  70. tests/offline/test_table_builder.py +198 -0
  71. tests/offline/test_unified_filter.py +398 -0
  72. tests/offline/test_unified_transaction.py +495 -0
  73. tests/offline/test_vector_index.py +238 -0
  74. tests/offline/test_vector_operations.py +688 -0
  75. tests/offline/test_vector_type.py +174 -0
  76. tests/offline/test_version_core.py +328 -0
  77. tests/offline/test_version_management.py +372 -0
  78. tests/offline/test_version_standalone.py +652 -0
  79. tests/online/__init__.py +20 -0
  80. tests/online/conftest.py +216 -0
  81. tests/online/test_account_management.py +194 -0
  82. tests/online/test_advanced_features.py +344 -0
  83. tests/online/test_async_client_interfaces.py +330 -0
  84. tests/online/test_async_client_online.py +285 -0
  85. tests/online/test_async_model_insert_online.py +293 -0
  86. tests/online/test_async_orm_online.py +300 -0
  87. tests/online/test_async_simple_query_online.py +802 -0
  88. tests/online/test_async_transaction_simple_query.py +300 -0
  89. tests/online/test_basic_connection.py +130 -0
  90. tests/online/test_client_online.py +238 -0
  91. tests/online/test_config.py +90 -0
  92. tests/online/test_config_validation.py +123 -0
  93. tests/online/test_connection_hooks_new_online.py +217 -0
  94. tests/online/test_dialect_schema_handling_online.py +331 -0
  95. tests/online/test_filter_logical_in_online.py +374 -0
  96. tests/online/test_fulltext_comprehensive.py +1773 -0
  97. tests/online/test_fulltext_label_online.py +433 -0
  98. tests/online/test_fulltext_search_online.py +842 -0
  99. tests/online/test_ivf_stats_online.py +506 -0
  100. tests/online/test_logger_integration.py +311 -0
  101. tests/online/test_matrixone_query_orm.py +540 -0
  102. tests/online/test_metadata_online.py +579 -0
  103. tests/online/test_model_insert_online.py +255 -0
  104. tests/online/test_mysql_driver_validation.py +213 -0
  105. tests/online/test_orm_advanced_features.py +2022 -0
  106. tests/online/test_orm_cte_integration.py +269 -0
  107. tests/online/test_orm_online.py +270 -0
  108. tests/online/test_pinecone_filter.py +708 -0
  109. tests/online/test_pubsub_operations.py +352 -0
  110. tests/online/test_query_methods.py +225 -0
  111. tests/online/test_query_update_online.py +433 -0
  112. tests/online/test_search_vector_index.py +557 -0
  113. tests/online/test_simple_fulltext_online.py +915 -0
  114. tests/online/test_snapshot_comprehensive.py +998 -0
  115. tests/online/test_sqlalchemy_engine_integration.py +336 -0
  116. tests/online/test_sqlalchemy_integration.py +425 -0
  117. tests/online/test_transaction_contexts.py +1219 -0
  118. tests/online/test_transaction_insert_methods.py +356 -0
  119. tests/online/test_transaction_query_methods.py +288 -0
  120. tests/online/test_unified_filter_online.py +529 -0
  121. tests/online/test_vector_comprehensive.py +706 -0
  122. tests/online/test_version_management.py +291 -0
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # Copyright 2021 - 2022 Matrix Origin
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ """
18
+ Offline unit tests for unified filter interface
19
+
20
+ Test SQL generation for various filter conditions without database connection
21
+ """
22
+
23
+ import unittest
24
+ from unittest.mock import Mock, MagicMock
25
+ from sqlalchemy import Column, Integer, String, DECIMAL, and_, or_, not_, func
26
+ from matrixone.orm import declarative_base
27
+
28
+ # Import the ORM classes
29
+ import sys
30
+ import os
31
+
32
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
33
+
34
+ from matrixone.orm import BaseMatrixOneQuery
35
+
36
+ Base = declarative_base()
37
+
38
+
39
+ class User(Base):
40
+ """Test user model"""
41
+
42
+ __tablename__ = "users"
43
+
44
+ id = Column(Integer, primary_key=True)
45
+ name = Column(String(100))
46
+ email = Column(String(100))
47
+ age = Column(Integer)
48
+ city = Column(String(50))
49
+
50
+
51
+ class Product(Base):
52
+ """Test product model"""
53
+
54
+ __tablename__ = "products"
55
+
56
+ id = Column(Integer, primary_key=True)
57
+ name = Column(String(100))
58
+ price = Column(DECIMAL(10, 2))
59
+ category = Column(String(50))
60
+ stock = Column(Integer)
61
+
62
+
63
+ class TestUnifiedFilter(unittest.TestCase):
64
+ """Test unified filter interface SQL generation"""
65
+
66
+ def setUp(self):
67
+ """Set up test fixtures"""
68
+ self.mock_client = Mock()
69
+ self.user_query = BaseMatrixOneQuery(User, self.mock_client)
70
+ self.product_query = BaseMatrixOneQuery(Product, self.mock_client)
71
+
72
+ def test_string_condition(self):
73
+ """Test string condition SQL generation"""
74
+ query = self.user_query.filter("age < 30")
75
+ sql, params = query._build_sql()
76
+
77
+ expected_sql = "SELECT * FROM users WHERE age < 30"
78
+ self.assertEqual(sql, expected_sql)
79
+ self.assertEqual(params, [])
80
+
81
+ def test_sqlalchemy_expression(self):
82
+ """Test SQLAlchemy expression SQL generation"""
83
+ query = self.product_query.filter(Product.category == "电子产品")
84
+ sql, params = query._build_sql()
85
+
86
+ expected_sql = "SELECT * FROM products WHERE category = '电子产品'"
87
+ self.assertEqual(sql, expected_sql)
88
+ self.assertEqual(params, [])
89
+
90
+ def test_or_condition(self):
91
+ """Test OR condition SQL generation"""
92
+ query = self.user_query.filter(or_(User.age < 30, User.city == "北京"))
93
+ sql, params = query._build_sql()
94
+
95
+ expected_sql = "SELECT * FROM users WHERE age < 30 OR city = '北京'"
96
+ self.assertEqual(sql, expected_sql)
97
+ self.assertEqual(params, [])
98
+
99
+ def test_and_condition(self):
100
+ """Test AND condition SQL generation"""
101
+ query = self.product_query.filter(and_(Product.category == "电子产品", Product.price > 1000))
102
+ sql, params = query._build_sql()
103
+
104
+ expected_sql = "SELECT * FROM products WHERE category = '电子产品' AND price > 1000"
105
+ self.assertEqual(sql, expected_sql)
106
+ self.assertEqual(params, [])
107
+
108
+ def test_multiple_filter_calls(self):
109
+ """Test multiple filter calls (AND relationship)"""
110
+ query = (
111
+ self.product_query.filter(Product.category == "电子产品").filter(Product.price > 1000).filter(Product.stock > 50)
112
+ )
113
+ sql, params = query._build_sql()
114
+
115
+ expected_sql = "SELECT * FROM products WHERE category = '电子产品' AND price > 1000 AND stock > 50"
116
+ self.assertEqual(sql, expected_sql)
117
+ self.assertEqual(params, [])
118
+
119
+ def test_complex_nested_condition(self):
120
+ """Test complex nested OR condition"""
121
+ query = self.product_query.filter(
122
+ or_(
123
+ and_(Product.category == "电子产品", Product.price > 2000),
124
+ and_(Product.category == "图书", Product.stock > 400),
125
+ )
126
+ )
127
+ sql, params = query._build_sql()
128
+
129
+ expected_sql = (
130
+ "SELECT * FROM products WHERE category = '电子产品' AND price > 2000 OR category = '图书' AND stock > 400"
131
+ )
132
+ self.assertEqual(sql, expected_sql)
133
+ self.assertEqual(params, [])
134
+
135
+ def test_not_condition(self):
136
+ """Test NOT condition SQL generation"""
137
+ query = self.product_query.filter(not_(Product.category == "电子产品"))
138
+ sql, params = query._build_sql()
139
+
140
+ expected_sql = "SELECT * FROM products WHERE category != '电子产品'"
141
+ self.assertEqual(sql, expected_sql)
142
+ self.assertEqual(params, [])
143
+
144
+ def test_mixed_condition_types(self):
145
+ """Test mixed string and SQLAlchemy expression conditions"""
146
+ query = (
147
+ self.user_query.filter("age > 25")
148
+ .filter(User.city != "深圳")
149
+ .filter(or_(User.name.like("张%"), User.name.like("李%")))
150
+ )
151
+ sql, params = query._build_sql()
152
+
153
+ expected_sql = "SELECT * FROM users WHERE age > 25 AND city != '深圳' AND name LIKE '张%' OR name LIKE '李%'"
154
+ self.assertEqual(sql, expected_sql)
155
+ self.assertEqual(params, [])
156
+
157
+ def test_filter_by_keyword_args(self):
158
+ """Test filter_by with keyword arguments"""
159
+ query = self.user_query.filter_by(city="北京", age=25)
160
+ sql, params = query._build_sql()
161
+
162
+ expected_sql = "SELECT * FROM users WHERE city = '北京' AND age = 25"
163
+ self.assertEqual(sql, expected_sql)
164
+ self.assertEqual(params, [])
165
+
166
+ def test_filter_by_with_parameters(self):
167
+ """Test filter_by with parameterized values"""
168
+ query = self.user_query.filter_by(city="北京")
169
+ query._where_params.append(25) # Simulate parameter
170
+ sql, params = query._build_sql()
171
+
172
+ expected_sql = "SELECT * FROM users WHERE city = '北京'"
173
+ self.assertEqual(sql, expected_sql)
174
+ self.assertEqual(params, [25])
175
+
176
+ def test_in_condition(self):
177
+ """Test IN condition SQL generation"""
178
+ query = self.user_query.filter(User.city.in_(["北京", "上海", "广州"]))
179
+ sql, params = query._build_sql()
180
+
181
+ expected_sql = "SELECT * FROM users WHERE city IN ('北京', '上海', '广州')"
182
+ self.assertEqual(sql, expected_sql)
183
+ self.assertEqual(params, [])
184
+
185
+ def test_like_condition(self):
186
+ """Test LIKE condition SQL generation"""
187
+ query = self.user_query.filter(User.email.like("%example.com%"))
188
+ sql, params = query._build_sql()
189
+
190
+ # SQLAlchemy compiles LIKE with single quotes, but our regex removes them
191
+ expected_sql = "SELECT * FROM users WHERE email LIKE '%com%'"
192
+ self.assertEqual(sql, expected_sql)
193
+ self.assertEqual(params, [])
194
+
195
+ def test_between_condition(self):
196
+ """Test BETWEEN condition SQL generation"""
197
+ query = self.user_query.filter(User.age.between(25, 35))
198
+ sql, params = query._build_sql()
199
+
200
+ expected_sql = "SELECT * FROM users WHERE age BETWEEN 25 AND 35"
201
+ self.assertEqual(sql, expected_sql)
202
+ self.assertEqual(params, [])
203
+
204
+ def test_greater_than_condition(self):
205
+ """Test greater than condition SQL generation"""
206
+ query = self.product_query.filter(Product.price > 1000)
207
+ sql, params = query._build_sql()
208
+
209
+ expected_sql = "SELECT * FROM products WHERE price > 1000"
210
+ self.assertEqual(sql, expected_sql)
211
+ self.assertEqual(params, [])
212
+
213
+ def test_less_than_condition(self):
214
+ """Test less than condition SQL generation"""
215
+ query = self.product_query.filter(Product.stock < 100)
216
+ sql, params = query._build_sql()
217
+
218
+ expected_sql = "SELECT * FROM products WHERE stock < 100"
219
+ self.assertEqual(sql, expected_sql)
220
+ self.assertEqual(params, [])
221
+
222
+ def test_greater_equal_condition(self):
223
+ """Test greater than or equal condition SQL generation"""
224
+ query = self.user_query.filter(User.age >= 25)
225
+ sql, params = query._build_sql()
226
+
227
+ expected_sql = "SELECT * FROM users WHERE age >= 25"
228
+ self.assertEqual(sql, expected_sql)
229
+ self.assertEqual(params, [])
230
+
231
+ def test_less_equal_condition(self):
232
+ """Test less than or equal condition SQL generation"""
233
+ query = self.user_query.filter(User.age <= 35)
234
+ sql, params = query._build_sql()
235
+
236
+ expected_sql = "SELECT * FROM users WHERE age <= 35"
237
+ self.assertEqual(sql, expected_sql)
238
+ self.assertEqual(params, [])
239
+
240
+ def test_not_equal_condition(self):
241
+ """Test not equal condition SQL generation"""
242
+ query = self.user_query.filter(User.city != "深圳")
243
+ sql, params = query._build_sql()
244
+
245
+ expected_sql = "SELECT * FROM users WHERE city != '深圳'"
246
+ self.assertEqual(sql, expected_sql)
247
+ self.assertEqual(params, [])
248
+
249
+ def test_complex_and_or_combination(self):
250
+ """Test complex AND/OR combination"""
251
+ query = self.user_query.filter(
252
+ and_(
253
+ or_(User.age >= 25, User.city.in_(["北京", "上海"])),
254
+ not_(User.email.like("%test%")),
255
+ )
256
+ )
257
+ sql, params = query._build_sql()
258
+
259
+ expected_sql = "SELECT * FROM users WHERE (age >= 25 OR city IN ('北京', '上海')) AND email NOT LIKE '%test%'"
260
+ self.assertEqual(sql, expected_sql)
261
+ self.assertEqual(params, [])
262
+
263
+ def test_multiple_or_conditions(self):
264
+ """Test multiple OR conditions"""
265
+ query = self.user_query.filter(or_(User.city == "北京", User.city == "上海", User.age > 30))
266
+ sql, params = query._build_sql()
267
+
268
+ expected_sql = "SELECT * FROM users WHERE city = '北京' OR city = '上海' OR age > 30"
269
+ self.assertEqual(sql, expected_sql)
270
+ self.assertEqual(params, [])
271
+
272
+ def test_deeply_nested_conditions(self):
273
+ """Test deeply nested conditions"""
274
+ query = self.product_query.filter(
275
+ or_(
276
+ and_(Product.category == "电子产品", or_(Product.price > 2000, Product.stock < 100)),
277
+ and_(Product.category == "图书", Product.stock > 400),
278
+ )
279
+ )
280
+ sql, params = query._build_sql()
281
+
282
+ # SQLAlchemy preserves parentheses for nested OR conditions
283
+ expected_sql = "SELECT * FROM products WHERE category = '电子产品' AND (price > 2000 OR stock < 100) OR category = '图书' AND stock > 400"
284
+ self.assertEqual(sql, expected_sql)
285
+ self.assertEqual(params, [])
286
+
287
+ def test_string_with_parameters(self):
288
+ """Test string condition with parameters"""
289
+ # Add parameters before calling filter
290
+ query = self.user_query
291
+ query._where_params.append(25)
292
+ query = query.filter("age > ?")
293
+ sql, params = query._build_sql()
294
+
295
+ # Parameters are processed during filter() call
296
+ expected_sql = "SELECT * FROM users WHERE age > 25"
297
+ self.assertEqual(sql, expected_sql)
298
+ self.assertEqual(params, [])
299
+
300
+ def test_string_with_multiple_parameters(self):
301
+ """Test string condition with multiple parameters"""
302
+ # Add parameters before calling filter
303
+ query = self.user_query
304
+ query._where_params.extend([25, 35])
305
+ query = query.filter("age BETWEEN ? AND ?")
306
+ sql, params = query._build_sql()
307
+
308
+ # Parameters are processed during filter() call
309
+ expected_sql = "SELECT * FROM users WHERE age BETWEEN 25 AND 35"
310
+ self.assertEqual(sql, expected_sql)
311
+ self.assertEqual(params, [])
312
+
313
+ def test_string_with_string_parameters(self):
314
+ """Test string condition with string parameters"""
315
+ # Add parameters before calling filter
316
+ query = self.user_query
317
+ query._where_params.append("北京")
318
+ query = query.filter("city = ?")
319
+ sql, params = query._build_sql()
320
+
321
+ # Parameters are processed during filter() call
322
+ expected_sql = "SELECT * FROM users WHERE city = '北京'"
323
+ self.assertEqual(sql, expected_sql)
324
+ self.assertEqual(params, [])
325
+
326
+ def test_empty_filter(self):
327
+ """Test query without any filters"""
328
+ query = self.user_query
329
+ sql, params = query._build_sql()
330
+
331
+ expected_sql = "SELECT * FROM users"
332
+ self.assertEqual(sql, expected_sql)
333
+ self.assertEqual(params, [])
334
+
335
+ def test_filter_with_order_by(self):
336
+ """Test filter combined with order by"""
337
+ query = self.product_query.filter(Product.price > 1000).order_by("price DESC")
338
+ sql, params = query._build_sql()
339
+
340
+ expected_sql = "SELECT * FROM products WHERE price > 1000 ORDER BY price DESC"
341
+ self.assertEqual(sql, expected_sql)
342
+ self.assertEqual(params, [])
343
+
344
+ def test_filter_with_limit(self):
345
+ """Test filter combined with limit"""
346
+ query = self.user_query.filter(User.age > 25).limit(10)
347
+ sql, params = query._build_sql()
348
+
349
+ expected_sql = "SELECT * FROM users WHERE age > 25 LIMIT 10"
350
+ self.assertEqual(sql, expected_sql)
351
+ self.assertEqual(params, [])
352
+
353
+ def test_filter_with_offset(self):
354
+ """Test filter combined with offset"""
355
+ query = self.user_query.filter(User.age > 25).offset(5)
356
+ sql, params = query._build_sql()
357
+
358
+ expected_sql = "SELECT * FROM users WHERE age > 25 OFFSET 5"
359
+ self.assertEqual(sql, expected_sql)
360
+ self.assertEqual(params, [])
361
+
362
+ def test_filter_with_select_columns(self):
363
+ """Test filter combined with select columns"""
364
+ query = self.user_query.select("name", "email").filter(User.age > 25)
365
+ sql, params = query._build_sql()
366
+
367
+ expected_sql = "SELECT name, email FROM users WHERE age > 25"
368
+ self.assertEqual(sql, expected_sql)
369
+ self.assertEqual(params, [])
370
+
371
+ def test_filter_with_group_by(self):
372
+ """Test filter combined with group by"""
373
+ query = self.product_query.select("category", "COUNT(*) as count").filter(Product.price > 100).group_by("category")
374
+ sql, params = query._build_sql()
375
+
376
+ expected_sql = "SELECT category, COUNT(*) as count FROM products WHERE price > 100 GROUP BY category"
377
+ self.assertEqual(sql, expected_sql)
378
+ self.assertEqual(params, [])
379
+
380
+ def test_filter_with_having(self):
381
+ """Test filter combined with having"""
382
+ query = (
383
+ self.product_query.select("category", "COUNT(*) as count")
384
+ .filter(Product.price > 100)
385
+ .group_by("category")
386
+ .having(func.count("*") > 1)
387
+ )
388
+ sql, params = query._build_sql()
389
+
390
+ expected_sql = (
391
+ "SELECT category, COUNT(*) as count FROM products WHERE price > 100 GROUP BY category HAVING count(*) > 1"
392
+ )
393
+ self.assertEqual(sql, expected_sql)
394
+ self.assertEqual(params, [])
395
+
396
+
397
+ if __name__ == "__main__":
398
+ unittest.main()