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.
- {matrixone_python_sdk-0.1.9/matrixone_python_sdk.egg-info → matrixone_python_sdk-0.1.10}/PKG-INFO +32 -1
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/README.md +31 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/README_USER.md +31 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_24_query_update.py +23 -6
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_26_stage_operations.py +6 -6
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/base_client.py +17 -1
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/__init__.py +52 -0
- matrixone_python_sdk-0.1.10/matrixone/sqlalchemy_ext/json_functions.py +296 -0
- matrixone_python_sdk-0.1.10/matrixone/sqlalchemy_ext/json_type.py +261 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10/matrixone_python_sdk.egg-info}/PKG-INFO +32 -1
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/SOURCES.txt +2 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/pyproject.toml +1 -1
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/LICENSE +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/MANIFEST.in +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_01_basic_connection.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_02_account_management.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_03_async_operations.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_04_transaction_management.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_05_snapshot_restore.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_06_sqlalchemy_integration.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_07_advanced_features.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_08_pubsub_operations.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_09_logger_integration.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_10_version_management.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_11_matrixone_version_demo.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_12_vector_basics.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_13_vector_indexes.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_14_vector_search.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_15_vector_advanced.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_18_snapshot_orm.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_19_sqlalchemy_style_orm.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_20_sqlalchemy_engine_integration.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_21_advanced_orm_features.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_22_unified_sql_builder.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_23_load_data_operations.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_25_metadata_operations.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_27_export_operations.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_28_sqlalchemy_select.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_29_complex_queries.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_30_with_snapshot_method.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_connection_hooks.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_dynamic_logging.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_ivf_stats_complete.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/__init__.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/account.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/async_client.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/async_orm.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/cli_tools.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/client.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/clone.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/config.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/connection_hooks.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/exceptions.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/export.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/fulltext_manager.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/index_utils.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/load_data.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/logger.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/metadata.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/moctl.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/orm.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/pitr.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/pubsub.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/restore.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/search_vector_index.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/session.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/snapshot.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sql_builder.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/dialect.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/fulltext_index.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/fulltext_search.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/hnsw_config.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/ivf_config.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/snapshot.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/table_builder.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/vector_index.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/vector_type.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_select.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/stage.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/vector_manager.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/version.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/dependency_links.txt +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/entry_points.txt +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/not-zip-safe +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/requires.txt +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone_python_sdk.egg-info/top_level.txt +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/mo_diag.py +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/requirements.txt +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/setup.cfg +0 -0
- {matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/setup.py +0 -0
{matrixone_python_sdk-0.1.9/matrixone_python_sdk.egg-info → matrixone_python_sdk-0.1.10}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matrixone-python-sdk
|
|
3
|
-
Version: 0.1.
|
|
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
|
{matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_24_query_update.py
RENAMED
|
@@ -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("
|
|
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
|
-
|
|
207
|
+
)
|
|
208
|
+
print(" ✓ Inserted users using batch_insert() and insert() with JSON dicts")
|
|
192
209
|
|
|
193
210
|
# 1. Update with SQLAlchemy expressions
|
|
194
|
-
print("
|
|
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
|
|
{matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/examples/example_26_stage_operations.py
RENAMED
|
@@ -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.
|
|
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.
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
{matrixone_python_sdk-0.1.9 → matrixone_python_sdk-0.1.10}/matrixone/sqlalchemy_ext/__init__.py
RENAMED
|
@@ -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
|
+
]
|