pyobvector 0.2.7__tar.gz → 0.2.9__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.
- {pyobvector-0.2.7 → pyobvector-0.2.9}/PKG-INFO +3 -3
- {pyobvector-0.2.7 → pyobvector-0.2.9}/README.md +1 -1
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/ob_vec_json_table_client.py +59 -34
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyproject.toml +2 -2
- {pyobvector-0.2.7 → pyobvector-0.2.9}/LICENSE +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/__init__.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/__init__.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/collection_schema.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/enum.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/exceptions.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/fts_index_param.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/index_param.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/milvus_like_client.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/ob_vec_client.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/partitions.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/client/schema_type.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/json_table/__init__.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/json_table/json_value_returning_func.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/json_table/oceanbase_dialect.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/json_table/virtual_data_type.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/__init__.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/dialect.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/full_text_index.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/geo_srid_point.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/gis_func.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/match_against_func.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/ob_table.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/reflection.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/replace_stmt.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/vec_dist_func.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/vector.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/schema/vector_index.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/util/__init__.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/util/ob_version.py +0 -0
- {pyobvector-0.2.7 → pyobvector-0.2.9}/pyobvector/util/vector.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pyobvector
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: A python SDK for OceanBase Vector Store, based on SQLAlchemy, compatible with Milvus API.
|
|
5
5
|
Author: shanhaikang.shk
|
|
6
6
|
Author-email: shanhaikang.shk@oceanbase.com
|
|
@@ -15,7 +15,7 @@ Requires-Dist: aiomysql (>=0.2.0,<0.3.0)
|
|
|
15
15
|
Requires-Dist: numpy (>=1.17.0,<2.0.0)
|
|
16
16
|
Requires-Dist: pydantic (>=2.7.0,<3)
|
|
17
17
|
Requires-Dist: pymysql (>=1.1.1,<2.0.0)
|
|
18
|
-
Requires-Dist: sqlalchemy (>=1.4
|
|
18
|
+
Requires-Dist: sqlalchemy (>=1.4,<=3)
|
|
19
19
|
Requires-Dist: sqlglot (>=26.0.1,<27.0.0)
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
|
|
@@ -36,7 +36,7 @@ poetry install
|
|
|
36
36
|
- install with pip:
|
|
37
37
|
|
|
38
38
|
```shell
|
|
39
|
-
pip install pyobvector==0.2.
|
|
39
|
+
pip install pyobvector==0.2.9
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
## Build Doc
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
|
-
from typing import Dict, List, Optional, Any
|
|
4
|
+
from typing import Dict, List, Optional, Any, Union
|
|
5
5
|
|
|
6
6
|
from sqlalchemy import Column, Integer, String, JSON, Engine, select, text, func, CursorResult
|
|
7
7
|
from sqlalchemy.dialects.mysql import TINYINT
|
|
@@ -144,15 +144,16 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
144
144
|
if self.user_id is None:
|
|
145
145
|
return True
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
147
|
+
with self.session() as session:
|
|
148
|
+
with session.begin():
|
|
149
|
+
distinct_admin_ids = (
|
|
150
|
+
session.query(ObVecJsonTableClient.JsonTableDataTBL.admin_id)
|
|
151
|
+
.filter_by(user_id = self.user_id)
|
|
152
|
+
.distinct()
|
|
153
|
+
.all()
|
|
154
|
+
)
|
|
155
|
+
admin_ids = [admin_id[0] for admin_id in distinct_admin_ids]
|
|
156
|
+
return (len(admin_ids) == 0) or (len(admin_ids) == 1 and admin_ids[0] == self.admin_id)
|
|
156
157
|
|
|
157
158
|
def _reset(self):
|
|
158
159
|
# Only for test
|
|
@@ -163,7 +164,12 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
163
164
|
def refresh_metadata(self) -> None:
|
|
164
165
|
self.jmetadata.reflect(self.engine)
|
|
165
166
|
|
|
166
|
-
def perform_json_table_sql(
|
|
167
|
+
def perform_json_table_sql(
|
|
168
|
+
self,
|
|
169
|
+
sql: str,
|
|
170
|
+
select_with_data_id: bool = False,
|
|
171
|
+
opt_user_id: Optional[str] = None,
|
|
172
|
+
) -> Union[Optional[CursorResult], int]:
|
|
167
173
|
"""Perform common SQL that operates on JSON Table."""
|
|
168
174
|
ast = parse_one(sql, dialect="oceanbase")
|
|
169
175
|
if isinstance(ast, exp.Create):
|
|
@@ -176,16 +182,16 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
176
182
|
self._handle_alter_json_table(ast)
|
|
177
183
|
return None
|
|
178
184
|
elif isinstance(ast, exp.Insert):
|
|
179
|
-
self._handle_jtable_dml_insert(ast)
|
|
180
|
-
return
|
|
185
|
+
row_count = self._handle_jtable_dml_insert(ast, opt_user_id)
|
|
186
|
+
return row_count
|
|
181
187
|
elif isinstance(ast, exp.Update):
|
|
182
|
-
self._handle_jtable_dml_update(ast)
|
|
183
|
-
return
|
|
188
|
+
row_count = self._handle_jtable_dml_update(ast, opt_user_id)
|
|
189
|
+
return row_count
|
|
184
190
|
elif isinstance(ast, exp.Delete):
|
|
185
|
-
self._handle_jtable_dml_delete(ast)
|
|
186
|
-
return
|
|
191
|
+
row_count = self._handle_jtable_dml_delete(ast, opt_user_id)
|
|
192
|
+
return row_count
|
|
187
193
|
elif isinstance(ast, exp.Select):
|
|
188
|
-
return self._handle_jtable_dml_select(ast, select_with_data_id)
|
|
194
|
+
return self._handle_jtable_dml_select(ast, select_with_data_id, opt_user_id)
|
|
189
195
|
else:
|
|
190
196
|
raise ValueError(f"{type(ast)} not supported")
|
|
191
197
|
|
|
@@ -642,8 +648,10 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
642
648
|
finally:
|
|
643
649
|
session.close()
|
|
644
650
|
|
|
645
|
-
def _handle_jtable_dml_insert(self, ast: Expression):
|
|
646
|
-
|
|
651
|
+
def _handle_jtable_dml_insert(self, ast: Expression, opt_user_id: Optional[str] = None):
|
|
652
|
+
real_user_id = opt_user_id or self.user_id
|
|
653
|
+
|
|
654
|
+
if real_user_id is None:
|
|
647
655
|
raise ValueError(f"inserting is disabled when user_id is None")
|
|
648
656
|
|
|
649
657
|
if isinstance(ast.this, exp.Schema):
|
|
@@ -673,6 +681,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
673
681
|
raise ValueError(f"Invalid ast type {ast.this}")
|
|
674
682
|
|
|
675
683
|
session = self.session()
|
|
684
|
+
n_new_records = 0
|
|
676
685
|
for tuple in ast.expression.expressions:
|
|
677
686
|
expr_list = tuple.expressions
|
|
678
687
|
if len(expr_list) != len(insert_col_names):
|
|
@@ -691,21 +700,26 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
691
700
|
logger.debug(f"================= [INSERT] =============== {kv}")
|
|
692
701
|
|
|
693
702
|
session.add(ObVecJsonTableClient.JsonTableDataTBL(
|
|
694
|
-
user_id =
|
|
703
|
+
user_id = real_user_id,
|
|
695
704
|
admin_id = self.admin_id,
|
|
696
705
|
jtable_name = table_name,
|
|
697
706
|
jdata = kv,
|
|
698
707
|
))
|
|
708
|
+
n_new_records += 1
|
|
699
709
|
|
|
700
710
|
try:
|
|
701
711
|
session.commit()
|
|
702
712
|
except Exception as e:
|
|
703
713
|
session.rollback()
|
|
704
714
|
logger.error(f"Error occurred: {e}")
|
|
715
|
+
n_new_records = 0
|
|
705
716
|
finally:
|
|
706
717
|
session.close()
|
|
718
|
+
return n_new_records
|
|
719
|
+
|
|
720
|
+
def _handle_jtable_dml_update(self, ast: Expression, opt_user_id: Optional[str] = None):
|
|
721
|
+
real_user_id = opt_user_id or self.user_id
|
|
707
722
|
|
|
708
|
-
def _handle_jtable_dml_update(self, ast: Expression):
|
|
709
723
|
table_name = ast.this.this.this
|
|
710
724
|
if not self._check_table_exists(table_name):
|
|
711
725
|
raise ValueError(f"Table {table_name} does not exists")
|
|
@@ -733,7 +747,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
733
747
|
path_settings.append(f"'$.{col_name}', {str(col_expr)}")
|
|
734
748
|
|
|
735
749
|
where_clause = None
|
|
736
|
-
if 'where' in ast.args.keys():
|
|
750
|
+
if 'where' in ast.args.keys() and ast.args['where']:
|
|
737
751
|
for column in ast.args['where'].find_all(exp.Column):
|
|
738
752
|
where_col_name = column.this.this
|
|
739
753
|
if not self._check_col_exists(table_name, where_col_name):
|
|
@@ -741,8 +755,8 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
741
755
|
column.parent.args['this'] = parse_one(
|
|
742
756
|
f"JSON_VALUE({JSON_TABLE_DATA_TABLE_NAME}.jdata, '$.{where_col_name}')"
|
|
743
757
|
)
|
|
744
|
-
if
|
|
745
|
-
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{
|
|
758
|
+
if real_user_id:
|
|
759
|
+
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{real_user_id}' AND {JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}' AND ({str(ast.args['where'].this)})"
|
|
746
760
|
else:
|
|
747
761
|
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}' AND ({str(ast.args['where'].this)})"
|
|
748
762
|
|
|
@@ -752,15 +766,18 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
752
766
|
update_sql = f"UPDATE {JSON_TABLE_DATA_TABLE_NAME} SET jdata = JSON_REPLACE({JSON_TABLE_DATA_TABLE_NAME}.jdata, {', '.join(path_settings)})"
|
|
753
767
|
|
|
754
768
|
logger.debug(f"===================== do update: {update_sql}")
|
|
755
|
-
self.perform_raw_text_sql(update_sql)
|
|
769
|
+
res = self.perform_raw_text_sql(update_sql)
|
|
770
|
+
return res.rowcount
|
|
771
|
+
|
|
772
|
+
def _handle_jtable_dml_delete(self, ast: Expression, opt_user_id: Optional[str] = None):
|
|
773
|
+
real_user_id = opt_user_id or self.user_id
|
|
756
774
|
|
|
757
|
-
def _handle_jtable_dml_delete(self, ast: Expression):
|
|
758
775
|
table_name = ast.this.this.this
|
|
759
776
|
if not self._check_table_exists(table_name):
|
|
760
777
|
raise ValueError(f"Table {table_name} does not exists")
|
|
761
778
|
|
|
762
779
|
where_clause = None
|
|
763
|
-
if 'where' in ast.args.keys():
|
|
780
|
+
if 'where' in ast.args.keys() and ast.args['where']:
|
|
764
781
|
for column in ast.args['where'].find_all(exp.Column):
|
|
765
782
|
where_col_name = column.this.this
|
|
766
783
|
if not self._check_col_exists(table_name, where_col_name):
|
|
@@ -768,8 +785,8 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
768
785
|
column.parent.args['this'] = parse_one(
|
|
769
786
|
f"JSON_VALUE({JSON_TABLE_DATA_TABLE_NAME}.jdata, '$.{where_col_name}')"
|
|
770
787
|
)
|
|
771
|
-
if
|
|
772
|
-
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{
|
|
788
|
+
if real_user_id:
|
|
789
|
+
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{real_user_id}' AND {JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}' AND ({str(ast.args['where'].this)})"
|
|
773
790
|
else:
|
|
774
791
|
where_clause = f"{JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}' AND ({str(ast.args['where'].this)})"
|
|
775
792
|
|
|
@@ -779,7 +796,8 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
779
796
|
delete_sql = f"DELETE FROM {JSON_TABLE_DATA_TABLE_NAME}"
|
|
780
797
|
|
|
781
798
|
logger.debug(f"===================== do delete: {delete_sql}")
|
|
782
|
-
self.perform_raw_text_sql(delete_sql)
|
|
799
|
+
res = self.perform_raw_text_sql(delete_sql)
|
|
800
|
+
return res.rowcount
|
|
783
801
|
|
|
784
802
|
def _get_full_datatype(self, jdata_type: str):
|
|
785
803
|
if jdata_type.upper() == "VARCHAR":
|
|
@@ -788,7 +806,14 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
788
806
|
return "DECIMAL(10, 0)"
|
|
789
807
|
return jdata_type
|
|
790
808
|
|
|
791
|
-
def _handle_jtable_dml_select(
|
|
809
|
+
def _handle_jtable_dml_select(
|
|
810
|
+
self,
|
|
811
|
+
ast: Expression,
|
|
812
|
+
select_with_data_id: bool = False,
|
|
813
|
+
opt_user_id: Optional[str] = None
|
|
814
|
+
):
|
|
815
|
+
real_user_id = opt_user_id or self.user_id
|
|
816
|
+
|
|
792
817
|
table_name = ast.args['from'].this.this.this
|
|
793
818
|
if not self._check_table_exists(table_name):
|
|
794
819
|
raise ValueError(f"Table {table_name} does not exists")
|
|
@@ -856,8 +881,8 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
856
881
|
else:
|
|
857
882
|
ast.args['joins'] = [join_node]
|
|
858
883
|
|
|
859
|
-
if
|
|
860
|
-
extra_filter_str = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{
|
|
884
|
+
if real_user_id:
|
|
885
|
+
extra_filter_str = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{real_user_id}' AND {JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}'"
|
|
861
886
|
else:
|
|
862
887
|
extra_filter_str = f"{JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}'"
|
|
863
888
|
if 'where' in ast.args.keys():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pyobvector"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.9"
|
|
4
4
|
description = "A python SDK for OceanBase Vector Store, based on SQLAlchemy, compatible with Milvus API."
|
|
5
5
|
authors = ["shanhaikang.shk <shanhaikang.shk@oceanbase.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -8,7 +8,7 @@ readme = "README.md"
|
|
|
8
8
|
[tool.poetry.dependencies]
|
|
9
9
|
python = ">=3.9,<4.0"
|
|
10
10
|
numpy = ">=1.17.0,<2.0.0"
|
|
11
|
-
sqlalchemy = ">=1.4
|
|
11
|
+
sqlalchemy = ">=1.4,<=3"
|
|
12
12
|
pymysql = "^1.1.1"
|
|
13
13
|
aiomysql = "^0.2.0"
|
|
14
14
|
sqlglot = "^26.0.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|