matrixone-python-sdk 0.1.9__tar.gz → 0.1.10__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.
Files changed (90) hide show
  1. {matrixone_python_sdk-0.1.9/matrixone_python_sdk.egg-info → matrixone_python_sdk-0.1.10}/PKG-INFO +32 -1
  2. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/README.md +31 -0
  3. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/README_USER.md +31 -0
  4. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_24_query_update.py +23 -6
  5. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_26_stage_operations.py +6 -6
  6. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/base_client.py +17 -1
  7. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/__init__.py +52 -0
  8. matrixone_python_sdk-0.1.10/matrixone/sqlalchemy_ext/json_functions.py +296 -0
  9. matrixone_python_sdk-0.1.10/matrixone/sqlalchemy_ext/json_type.py +261 -0
  10. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10/matrixone_python_sdk.egg-info}/PKG-INFO +32 -1
  11. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/SOURCES.txt +2 -0
  12. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/pyproject.toml +1 -1
  13. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/LICENSE +0 -0
  14. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/MANIFEST.in +0 -0
  15. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_01_basic_connection.py +0 -0
  16. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_02_account_management.py +0 -0
  17. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_03_async_operations.py +0 -0
  18. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_04_transaction_management.py +0 -0
  19. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_05_snapshot_restore.py +0 -0
  20. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_06_sqlalchemy_integration.py +0 -0
  21. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_07_advanced_features.py +0 -0
  22. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_08_pubsub_operations.py +0 -0
  23. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_09_logger_integration.py +0 -0
  24. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_10_version_management.py +0 -0
  25. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_11_matrixone_version_demo.py +0 -0
  26. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_12_vector_basics.py +0 -0
  27. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_13_vector_indexes.py +0 -0
  28. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_14_vector_search.py +0 -0
  29. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_15_vector_advanced.py +0 -0
  30. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_18_snapshot_orm.py +0 -0
  31. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_19_sqlalchemy_style_orm.py +0 -0
  32. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_20_sqlalchemy_engine_integration.py +0 -0
  33. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_21_advanced_orm_features.py +0 -0
  34. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_22_unified_sql_builder.py +0 -0
  35. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_23_load_data_operations.py +0 -0
  36. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_25_metadata_operations.py +0 -0
  37. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_27_export_operations.py +0 -0
  38. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_28_sqlalchemy_select.py +0 -0
  39. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_29_complex_queries.py +0 -0
  40. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_30_with_snapshot_method.py +0 -0
  41. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_connection_hooks.py +0 -0
  42. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_dynamic_logging.py +0 -0
  43. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_ivf_stats_complete.py +0 -0
  44. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/__init__.py +0 -0
  45. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/account.py +0 -0
  46. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/async_client.py +0 -0
  47. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/async_orm.py +0 -0
  48. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/cli_tools.py +0 -0
  49. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/client.py +0 -0
  50. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/clone.py +0 -0
  51. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/config.py +0 -0
  52. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/connection_hooks.py +0 -0
  53. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/exceptions.py +0 -0
  54. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/export.py +0 -0
  55. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/fulltext_manager.py +0 -0
  56. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/index_utils.py +0 -0
  57. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/load_data.py +0 -0
  58. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/logger.py +0 -0
  59. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/metadata.py +0 -0
  60. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/moctl.py +0 -0
  61. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/orm.py +0 -0
  62. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/pitr.py +0 -0
  63. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/pubsub.py +0 -0
  64. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/restore.py +0 -0
  65. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/search_vector_index.py +0 -0
  66. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/session.py +0 -0
  67. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/snapshot.py +0 -0
  68. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sql_builder.py +0 -0
  69. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/dialect.py +0 -0
  70. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/fulltext_index.py +0 -0
  71. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/fulltext_search.py +0 -0
  72. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/hnsw_config.py +0 -0
  73. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/ivf_config.py +0 -0
  74. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/snapshot.py +0 -0
  75. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/table_builder.py +0 -0
  76. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/vector_index.py +0 -0
  77. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/vector_type.py +0 -0
  78. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_select.py +0 -0
  79. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/stage.py +0 -0
  80. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/vector_manager.py +0 -0
  81. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/version.py +0 -0
  82. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/dependency_links.txt +0 -0
  83. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/entry_points.txt +0 -0
  84. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/not-zip-safe +0 -0
  85. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/requires.txt +0 -0
  86. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/top_level.txt +0 -0
  87. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/mo_diag.py +0 -0
  88. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/requirements.txt +0 -0
  89. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/setup.cfg +0 -0
  90. {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrixone-python-sdk
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: A comprehensive Python SDK for MatrixOne database operations with vector search, fulltext search, and advanced features
5
5
  Home-page: https://github.com/matrixorigin/matrixone
6
6
  Author: MatrixOne Team
@@ -171,6 +171,37 @@ conda activate matrixone
171
171
  pip install matrixone-python-sdk
172
172
  ```
173
173
 
174
+ ## ⚠️ Important: Column Naming Convention
175
+
176
+ **🚨 CRITICAL: Always use lowercase with underscores (snake_case) for column names!**
177
+
178
+ MatrixOne does not support SQL standard double-quoted identifiers in queries, which causes issues with camelCase column names when using SQLAlchemy ORM.
179
+
180
+ ```python
181
+ # ❌ DON'T: CamelCase column names (will fail in SELECT queries)
182
+ class User(Base):
183
+ userName = Column(String(50)) # CREATE succeeds, SELECT fails!
184
+ userId = Column(Integer) # Will cause SQL syntax errors
185
+
186
+ # ✅ DO: Use lowercase with underscores (snake_case)
187
+ class User(Base):
188
+ user_name = Column(String(50)) # Works perfectly
189
+ user_id = Column(Integer) # All operations succeed
190
+ ```
191
+
192
+ **Why this matters:**
193
+ - ✅ CREATE TABLE works with both styles (uses backticks)
194
+ - ✅ INSERT works with both styles
195
+ - ❌ **SELECT fails with camelCase** (uses double quotes, not supported by MatrixOne)
196
+
197
+ **Example of the problem:**
198
+ ```python
199
+ # CamelCase generates: SELECT "userName" FROM user ❌ Fails!
200
+ # snake_case generates: SELECT user_name FROM user ✅ Works!
201
+ ```
202
+
203
+ ---
204
+
174
205
  ## Quick Start
175
206
 
176
207
  ### Basic Usage
@@ -70,6 +70,37 @@ A comprehensive Python SDK for MatrixOne that provides SQLAlchemy-like interface
70
70
  - Non-interactive mode for scripting and automation
71
71
  - Batch operations on tables and indexes
72
72
 
73
+ ## ⚠️ Important: Column Naming Convention
74
+
75
+ **🚨 CRITICAL: Always use lowercase with underscores (snake_case) for column names!**
76
+
77
+ MatrixOne does not support SQL standard double-quoted identifiers in queries, which causes issues with camelCase column names when using SQLAlchemy ORM.
78
+
79
+ ```python
80
+ # ❌ DON'T: CamelCase column names (will fail in SELECT queries)
81
+ class User(Base):
82
+ userName = Column(String(50)) # CREATE succeeds, SELECT fails!
83
+ userId = Column(Integer) # Will cause SQL syntax errors
84
+
85
+ # ✅ DO: Use lowercase with underscores (snake_case)
86
+ class User(Base):
87
+ user_name = Column(String(50)) # Works perfectly
88
+ user_id = Column(Integer) # All operations succeed
89
+ ```
90
+
91
+ **Why this matters:**
92
+ - ✅ CREATE TABLE works with both styles (uses backticks)
93
+ - ✅ INSERT works with both styles
94
+ - ❌ **SELECT fails with camelCase** (uses double quotes, not supported by MatrixOne)
95
+
96
+ **Example of the problem:**
97
+ ```python
98
+ # CamelCase generates: SELECT "userName" FROM user ❌ Fails!
99
+ # snake_case generates: SELECT user_name FROM user ✅ Works!
100
+ ```
101
+
102
+ ---
103
+
73
104
  ## 🚀 Installation
74
105
 
75
106
  ### From PyPI (Stable Release)
@@ -103,6 +103,37 @@ conda activate matrixone
103
103
  pip install matrixone-python-sdk
104
104
  ```
105
105
 
106
+ ## ⚠️ Important: Column Naming Convention
107
+
108
+ **🚨 CRITICAL: Always use lowercase with underscores (snake_case) for column names!**
109
+
110
+ MatrixOne does not support SQL standard double-quoted identifiers in queries, which causes issues with camelCase column names when using SQLAlchemy ORM.
111
+
112
+ ```python
113
+ # ❌ DON'T: CamelCase column names (will fail in SELECT queries)
114
+ class User(Base):
115
+ userName = Column(String(50)) # CREATE succeeds, SELECT fails!
116
+ userId = Column(Integer) # Will cause SQL syntax errors
117
+
118
+ # ✅ DO: Use lowercase with underscores (snake_case)
119
+ class User(Base):
120
+ user_name = Column(String(50)) # Works perfectly
121
+ user_id = Column(Integer) # All operations succeed
122
+ ```
123
+
124
+ **Why this matters:**
125
+ - ✅ CREATE TABLE works with both styles (uses backticks)
126
+ - ✅ INSERT works with both styles
127
+ - ❌ **SELECT fails with camelCase** (uses double quotes, not supported by MatrixOne)
128
+
129
+ **Example of the problem:**
130
+ ```python
131
+ # CamelCase generates: SELECT "userName" FROM user ❌ Fails!
132
+ # snake_case generates: SELECT user_name FROM user ✅ Works!
133
+ ```
134
+
135
+ ---
136
+
106
137
  ## Quick Start
107
138
 
108
139
  ### Basic Usage
@@ -36,6 +36,7 @@ from matrixone import Client
36
36
  from matrixone.orm import declarative_base
37
37
  from matrixone.config import get_connection_params
38
38
  from sqlalchemy import Column, Integer, String, DECIMAL, TIMESTAMP, func
39
+ from sqlalchemy.dialects.mysql import JSON
39
40
 
40
41
  # Create MatrixOne logger
41
42
  logger = logging.getLogger(__name__)
@@ -59,6 +60,7 @@ class User(Base):
59
60
  login_count = Column(Integer, default=0)
60
61
  last_login = Column(TIMESTAMP)
61
62
  created_at = Column(TIMESTAMP, server_default=func.current_timestamp())
63
+ preferences = Column(JSON, nullable=True) # JSON field for user preferences
62
64
 
63
65
 
64
66
  class Product(Base):
@@ -87,7 +89,8 @@ def demo_simple_updates():
87
89
  # Create tables
88
90
  client.create_all(Base)
89
91
 
90
- # Insert sample data
92
+ # Insert sample data with JSON fields
93
+ # Note: Python dict in JSON column is automatically serialized
91
94
  users_data = [
92
95
  {
93
96
  "id": 1,
@@ -98,6 +101,7 @@ def demo_simple_updates():
98
101
  "salary": 50000.00,
99
102
  "status": "active",
100
103
  "login_count": 5,
104
+ "preferences": {"theme": "dark", "language": "en", "notifications": True},
101
105
  },
102
106
  {
103
107
  "id": 2,
@@ -108,6 +112,7 @@ def demo_simple_updates():
108
112
  "salary": 60000.00,
109
113
  "status": "active",
110
114
  "login_count": 10,
115
+ "preferences": {"theme": "light", "language": "zh", "notifications": False},
111
116
  },
112
117
  {
113
118
  "id": 3,
@@ -118,18 +123,22 @@ def demo_simple_updates():
118
123
  "salary": 70000.00,
119
124
  "status": "inactive",
120
125
  "login_count": 2,
126
+ "preferences": {"theme": "auto", "language": "en", "email_digest": "daily"},
121
127
  },
122
128
  ]
129
+ # Using batch_insert with JSON dict - auto-serialization
123
130
  client.batch_insert("example_users_update", users_data)
131
+ print(" ✓ Inserted users with JSON preferences using batch_insert()")
124
132
 
125
133
  # 1. Simple update with key-value pairs
126
- print("1. Simple update with key-value pairs")
134
+ print("\n1. Simple update with key-value pairs")
127
135
  query = client.query(User)
128
136
  result = query.update(full_name="Alice Updated", email="alice.updated@example.com").filter(User.id == 1).execute()
129
137
 
130
138
  # Verify the update
131
139
  updated_user = client.query(User).filter(User.id == 1).first()
132
140
  print(f" Updated user: {updated_user.full_name}, {updated_user.email}")
141
+ print(f" User preferences (JSON): {updated_user.preferences}")
133
142
 
134
143
  # 2. Update multiple records with condition
135
144
  print("\n2. Update multiple records with condition")
@@ -165,7 +174,7 @@ def demo_sqlalchemy_expressions():
165
174
  # Create tables
166
175
  client.create_all(Base)
167
176
 
168
- # Insert sample data
177
+ # Insert sample data with JSON - Using batch_insert
169
178
  users_data = [
170
179
  {
171
180
  "id": 1,
@@ -176,7 +185,14 @@ def demo_sqlalchemy_expressions():
176
185
  "salary": 50000.00,
177
186
  "status": "active",
178
187
  "login_count": 5,
188
+ "preferences": {"theme": "dark", "language": "en"},
179
189
  },
190
+ ]
191
+ client.batch_insert("example_users_update", users_data)
192
+
193
+ # Insert another user using client.insert() with JSON dict
194
+ client.insert(
195
+ "example_users_update",
180
196
  {
181
197
  "id": 2,
182
198
  "username": "bob",
@@ -186,12 +202,13 @@ def demo_sqlalchemy_expressions():
186
202
  "salary": 60000.00,
187
203
  "status": "active",
188
204
  "login_count": 10,
205
+ "preferences": {"theme": "light", "language": "zh", "notifications": False},
189
206
  },
190
- ]
191
- client.batch_insert("example_users_update", users_data)
207
+ )
208
+ print(" Inserted users using batch_insert() and insert() with JSON dicts")
192
209
 
193
210
  # 1. Update with SQLAlchemy expressions
194
- print("1. Update with SQLAlchemy expressions")
211
+ print("\n1. Update with SQLAlchemy expressions")
195
212
  query = client.query(User)
196
213
  result = query.update(login_count=User.login_count + 1, salary=User.salary * 1.1).filter(User.id == 1).execute()
197
214
 
@@ -225,7 +225,7 @@ class StageOperationsDemo:
225
225
  self.results['files_created'].append(csv_file)
226
226
 
227
227
  # Load data from stage using client.load_data
228
- result = client.load_data.from_stage_csv('demo_file_stage', 'stage_users.csv', StageUser)
228
+ result = client.load_data.read_csv_stage('demo_file_stage', 'stage_users.csv', StageUser)
229
229
  self.logger.info(f"✅ Loaded {result.affected_rows} rows from stage")
230
230
 
231
231
  # Verify data
@@ -392,7 +392,7 @@ class StageOperationsDemo:
392
392
  # Load data from stage within session
393
393
  with client.session() as tx:
394
394
  # Load data from pre-existing stage in session
395
- result = tx.load_data.from_stage_csv('tx_temp_stage', 'tx_data.csv', TxStageData)
395
+ result = tx.load_data.read_csv_stage('tx_temp_stage', 'tx_data.csv', TxStageData)
396
396
  # Check for both ResultSet and SQLAlchemy Result
397
397
  affected = result.affected_rows if hasattr(result, 'affected_rows') else result.rowcount
398
398
  self.logger.info(f"✅ Loaded {affected} rows in session")
@@ -706,7 +706,7 @@ class StageOperationsDemo:
706
706
 
707
707
  # Load customers
708
708
  print("Loading customers dimension...")
709
- stage.load_csv('customers.csv', Customer, ignore_lines=1)
709
+ stage.load_csv('customers.csv', Customer, skiprows=1)
710
710
  count = client.query(Customer).count()
711
711
  print(f"✅ Loaded {count} customers")
712
712
 
@@ -718,13 +718,13 @@ class StageOperationsDemo:
718
718
 
719
719
  # Load orders
720
720
  print("Loading orders fact table...")
721
- stage.load_csv('orders.csv', Order, ignore_lines=1)
721
+ stage.load_csv('orders.csv', Order, skiprows=1)
722
722
  count = client.query(Order).count()
723
723
  print(f"✅ Loaded {count} orders")
724
724
 
725
725
  # Load shipping (TSV)
726
726
  print("Loading shipping logistics...")
727
- stage.load_tsv('shipping.tsv', Shipping, ignore_lines=1)
727
+ stage.load_tsv('shipping.tsv', Shipping, skiprows=1)
728
728
  count = client.query(Shipping).count()
729
729
  print(f"✅ Loaded {count} shipping records")
730
730
 
@@ -794,7 +794,7 @@ class StageOperationsDemo:
794
794
  self.results['files_created'].append(delta_file)
795
795
 
796
796
  print("📥 Loading incremental orders...")
797
- stage.load_csv('orders_delta.csv', Order, ignore_lines=1)
797
+ stage.load_csv('orders_delta.csv', Order, skiprows=1)
798
798
  count = client.query(Order).count()
799
799
  print(f"✅ Total orders now: {count} (+2 new)")
800
800
 
@@ -67,15 +67,22 @@ class BaseMatrixOneClient:
67
67
 
68
68
  SQL INSERT statement string
69
69
  """
70
+ import json
71
+
70
72
  columns = list(data.keys())
71
73
  values = list(data.values())
72
74
 
73
- # Convert vectors to string format
75
+ # Convert values to appropriate SQL format
74
76
  formatted_values = []
75
77
  for value in values:
76
78
  if value is None:
77
79
  formatted_values.append("NULL")
80
+ elif isinstance(value, dict):
81
+ # JSON type: serialize dict to JSON string
82
+ json_str = json.dumps(value)
83
+ formatted_values.append(f"'{json_str}'")
78
84
  elif isinstance(value, list):
85
+ # Vector type: format as [1,2,3]
79
86
  formatted_values.append("'" + "[" + ",".join(map(str, value)) + "]" + "'")
80
87
  else:
81
88
  formatted_values.append(f"'{str(value)}'")
@@ -98,6 +105,8 @@ class BaseMatrixOneClient:
98
105
 
99
106
  SQL batch INSERT statement string
100
107
  """
108
+ import json
109
+
101
110
  if not data_list:
102
111
  return ""
103
112
 
@@ -113,7 +122,14 @@ class BaseMatrixOneClient:
113
122
  value = data[col]
114
123
  if value is None:
115
124
  formatted_values.append("NULL")
125
+ elif isinstance(value, dict):
126
+ # JSON type: serialize dict to JSON string
127
+ json_str = json.dumps(value)
128
+ # Escape single quotes in JSON string to prevent SQL injection
129
+ escaped_json = json_str.replace("'", "''")
130
+ formatted_values.append(f"'{escaped_json}'")
116
131
  elif isinstance(value, list):
132
+ # Vector type: format as [1,2,3]
117
133
  formatted_values.append("'" + "[" + ",".join(map(str, value)) + "]" + "'")
118
134
  else:
119
135
  # Escape single quotes in string values to prevent SQL injection
@@ -99,6 +99,35 @@ from .vector_type import (
99
99
  within_distance,
100
100
  )
101
101
 
102
+ # Import JSON functions
103
+ from .json_functions import (
104
+ JSON_EXTRACT,
105
+ JSON_EXTRACT_FLOAT64,
106
+ JSON_EXTRACT_STRING,
107
+ JSON_INSERT,
108
+ JSON_QUOTE,
109
+ JSON_REPLACE,
110
+ JSON_ROW,
111
+ JSON_SET,
112
+ JSON_UNQUOTE,
113
+ json_extract,
114
+ json_extract_float64,
115
+ json_extract_string,
116
+ json_insert,
117
+ json_quote,
118
+ json_replace,
119
+ json_row,
120
+ json_set,
121
+ json_unquote,
122
+ )
123
+
124
+ # Import custom JSON type
125
+ from .json_type import (
126
+ JSON,
127
+ JSONPathElement,
128
+ MATRIXONE_JSON,
129
+ )
130
+
102
131
  __all__ = [
103
132
  "VectorType",
104
133
  "Vectorf32",
@@ -159,4 +188,27 @@ __all__ = [
159
188
  "boolean_match",
160
189
  "natural_match",
161
190
  "group",
191
+ # JSON functions
192
+ "json_extract",
193
+ "json_extract_string",
194
+ "json_extract_float64",
195
+ "json_set",
196
+ "json_insert",
197
+ "json_replace",
198
+ "json_quote",
199
+ "json_unquote",
200
+ "json_row",
201
+ "JSON_EXTRACT",
202
+ "JSON_EXTRACT_STRING",
203
+ "JSON_EXTRACT_FLOAT64",
204
+ "JSON_SET",
205
+ "JSON_INSERT",
206
+ "JSON_REPLACE",
207
+ "JSON_QUOTE",
208
+ "JSON_UNQUOTE",
209
+ "JSON_ROW",
210
+ # JSON type
211
+ "JSON",
212
+ "JSONPathElement",
213
+ "MATRIXONE_JSON",
162
214
  ]
@@ -0,0 +1,296 @@
1
+ """
2
+ JSON functions for MatrixOne SQLAlchemy integration.
3
+
4
+ This module provides JSON manipulation functions compatible with MatrixOne's
5
+ JSON support. These functions handle path literals correctly to avoid parameter
6
+ binding issues.
7
+ """
8
+
9
+ from sqlalchemy import literal_column
10
+ from sqlalchemy.sql.expression import ColumnElement
11
+ from sqlalchemy.sql import functions
12
+
13
+
14
+ class json_extract(functions.GenericFunction):
15
+ """
16
+ Extract values from JSON documents using JSON path expressions.
17
+
18
+ This function wraps MatrixOne's JSON_EXTRACT function and ensures
19
+ that path arguments are treated as literals rather than bound parameters.
20
+
21
+ Args:
22
+ json_column: The JSON column or expression
23
+ *paths: One or more JSON path expressions (e.g., '$.name', '$.age')
24
+
25
+ Returns:
26
+ SQLAlchemy function expression
27
+
28
+ Examples:
29
+ >>> from matrixone.sqlalchemy_ext.json_functions import json_extract
30
+ >>> from sqlalchemy import select
31
+ >>>
32
+ >>> # Extract single path
33
+ >>> stmt = select(json_extract(Product.specs, '$.brand'))
34
+ >>>
35
+ >>> # Extract multiple paths
36
+ >>> stmt = select(json_extract(Product.specs, '$.brand', '$.model'))
37
+ >>>
38
+ >>> # Use in WHERE clause
39
+ >>> stmt = select(Product).where(
40
+ ... json_extract(Product.specs, '$.category') == 'Electronics'
41
+ ... )
42
+ """
43
+
44
+ type = None
45
+ name = 'json_extract'
46
+ inherit_cache = True
47
+
48
+ def __init__(self, json_column: ColumnElement, *paths: str, **kwargs):
49
+ # Convert paths to literal columns to avoid parameter binding
50
+ literal_paths = [literal_column(f"'{path}'") for path in paths]
51
+ super().__init__(json_column, *literal_paths, **kwargs)
52
+
53
+
54
+ class json_extract_string(functions.GenericFunction):
55
+ """
56
+ Extract JSON value as string type.
57
+
58
+ Args:
59
+ json_column: The JSON column or expression
60
+ path: JSON path expression
61
+
62
+ Returns:
63
+ SQLAlchemy function expression returning string
64
+
65
+ Example:
66
+ >>> stmt = select(json_extract_string(Product.specs, '$.name'))
67
+ """
68
+
69
+ type = None
70
+ name = 'json_extract_string'
71
+ inherit_cache = True
72
+
73
+ def __init__(self, json_column: ColumnElement, path: str, **kwargs):
74
+ literal_path = literal_column(f"'{path}'")
75
+ super().__init__(json_column, literal_path, **kwargs)
76
+
77
+
78
+ class json_extract_float64(functions.GenericFunction):
79
+ """
80
+ Extract JSON numeric value as float.
81
+
82
+ Args:
83
+ json_column: The JSON column or expression
84
+ path: JSON path expression
85
+
86
+ Returns:
87
+ SQLAlchemy function expression returning float
88
+
89
+ Example:
90
+ >>> stmt = select(json_extract_float64(Product.specs, '$.price'))
91
+ """
92
+
93
+ type = None
94
+ name = 'json_extract_float64'
95
+ inherit_cache = True
96
+
97
+ def __init__(self, json_column: ColumnElement, path: str, **kwargs):
98
+ literal_path = literal_column(f"'{path}'")
99
+ super().__init__(json_column, literal_path, **kwargs)
100
+
101
+
102
+ class json_set(functions.GenericFunction):
103
+ """
104
+ Insert or update JSON values (upsert behavior).
105
+
106
+ Args:
107
+ json_column: The JSON column or expression
108
+ *path_value_pairs: Alternating path and value arguments
109
+
110
+ Returns:
111
+ SQLAlchemy function expression
112
+
113
+ Example:
114
+ >>> from sqlalchemy import update
115
+ >>>
116
+ >>> # Update single key
117
+ >>> stmt = update(Product).values(
118
+ ... specs=json_set(Product.specs, '$.ram', 32)
119
+ ... )
120
+ >>>
121
+ >>> # Update multiple keys
122
+ >>> stmt = update(Product).values(
123
+ ... specs=json_set(Product.specs, '$.ram', 32, '$.storage', '1TB')
124
+ ... )
125
+ """
126
+
127
+ type = None
128
+ name = 'json_set'
129
+ inherit_cache = True
130
+
131
+ def __init__(self, json_column: ColumnElement, *path_value_pairs, **kwargs):
132
+ # Process path-value pairs
133
+ # Paths should be literals, values can be bound parameters
134
+ processed_args = []
135
+ for i, arg in enumerate(path_value_pairs):
136
+ if i % 2 == 0: # Path (even index)
137
+ processed_args.append(literal_column(f"'{arg}'"))
138
+ else: # Value (odd index)
139
+ processed_args.append(arg)
140
+
141
+ super().__init__(json_column, *processed_args, **kwargs)
142
+
143
+
144
+ class json_insert(functions.GenericFunction):
145
+ """
146
+ Insert JSON values only if path doesn't exist.
147
+
148
+ Args:
149
+ json_column: The JSON column or expression
150
+ *path_value_pairs: Alternating path and value arguments
151
+
152
+ Returns:
153
+ SQLAlchemy function expression
154
+
155
+ Example:
156
+ >>> stmt = update(Product).values(
157
+ ... specs=json_insert(Product.specs, '$.warranty', '2 years')
158
+ ... )
159
+ """
160
+
161
+ type = None
162
+ name = 'json_insert'
163
+ inherit_cache = True
164
+
165
+ def __init__(self, json_column: ColumnElement, *path_value_pairs, **kwargs):
166
+ processed_args = []
167
+ for i, arg in enumerate(path_value_pairs):
168
+ if i % 2 == 0: # Path
169
+ processed_args.append(literal_column(f"'{arg}'"))
170
+ else: # Value
171
+ processed_args.append(arg)
172
+
173
+ super().__init__(json_column, *processed_args, **kwargs)
174
+
175
+
176
+ class json_replace(functions.GenericFunction):
177
+ """
178
+ Update JSON values only if path exists.
179
+
180
+ Args:
181
+ json_column: The JSON column or expression
182
+ *path_value_pairs: Alternating path and value arguments
183
+
184
+ Returns:
185
+ SQLAlchemy function expression
186
+
187
+ Example:
188
+ >>> stmt = update(Product).values(
189
+ ... specs=json_replace(Product.specs, '$.price', 999.99)
190
+ ... )
191
+ """
192
+
193
+ type = None
194
+ name = 'json_replace'
195
+ inherit_cache = True
196
+
197
+ def __init__(self, json_column: ColumnElement, *path_value_pairs, **kwargs):
198
+ processed_args = []
199
+ for i, arg in enumerate(path_value_pairs):
200
+ if i % 2 == 0: # Path
201
+ processed_args.append(literal_column(f"'{arg}'"))
202
+ else: # Value
203
+ processed_args.append(arg)
204
+
205
+ super().__init__(json_column, *processed_args, **kwargs)
206
+
207
+
208
+ class json_quote(functions.GenericFunction):
209
+ """
210
+ Convert string to JSON string (add quotes and escape).
211
+
212
+ Args:
213
+ string_value: String to quote
214
+
215
+ Returns:
216
+ SQLAlchemy function expression
217
+
218
+ Example:
219
+ >>> stmt = select(json_quote('Hello "World"'))
220
+ """
221
+
222
+ type = None
223
+ name = 'json_quote'
224
+ inherit_cache = True
225
+
226
+
227
+ class json_unquote(functions.GenericFunction):
228
+ """
229
+ Remove JSON string quotes and unescape.
230
+
231
+ Args:
232
+ json_string: JSON string to unquote
233
+
234
+ Returns:
235
+ SQLAlchemy function expression
236
+
237
+ Example:
238
+ >>> stmt = select(json_unquote('"Hello"'))
239
+ """
240
+
241
+ type = None
242
+ name = 'json_unquote'
243
+ inherit_cache = True
244
+
245
+
246
+ class json_row(functions.GenericFunction):
247
+ """
248
+ Construct JSON object from key-value pairs.
249
+
250
+ Args:
251
+ *key_value_pairs: Alternating key and value arguments
252
+
253
+ Returns:
254
+ SQLAlchemy function expression
255
+
256
+ Example:
257
+ >>> stmt = select(json_row('name', 'John', 'age', 30))
258
+ """
259
+
260
+ type = None
261
+ name = 'json_row'
262
+ inherit_cache = True
263
+
264
+
265
+ # Convenience aliases (lowercase versions)
266
+ JSON_EXTRACT = json_extract
267
+ JSON_EXTRACT_STRING = json_extract_string
268
+ JSON_EXTRACT_FLOAT64 = json_extract_float64
269
+ JSON_SET = json_set
270
+ JSON_INSERT = json_insert
271
+ JSON_REPLACE = json_replace
272
+ JSON_QUOTE = json_quote
273
+ JSON_UNQUOTE = json_unquote
274
+ JSON_ROW = json_row
275
+
276
+
277
+ __all__ = [
278
+ 'json_extract',
279
+ 'json_extract_string',
280
+ 'json_extract_float64',
281
+ 'json_set',
282
+ 'json_insert',
283
+ 'json_replace',
284
+ 'json_quote',
285
+ 'json_unquote',
286
+ 'json_row',
287
+ 'JSON_EXTRACT',
288
+ 'JSON_EXTRACT_STRING',
289
+ 'JSON_EXTRACT_FLOAT64',
290
+ 'JSON_SET',
291
+ 'JSON_INSERT',
292
+ 'JSON_REPLACE',
293
+ 'JSON_QUOTE',
294
+ 'JSON_UNQUOTE',
295
+ 'JSON_ROW',
296
+ ]