pyobvector 0.2.19__py3-none-any.whl → 0.2.21__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.
- pyobvector/client/collection_schema.py +2 -2
- pyobvector/client/exceptions.py +2 -2
- pyobvector/client/fts_index_param.py +39 -9
- pyobvector/client/hybrid_search.py +6 -10
- pyobvector/client/index_param.py +5 -2
- pyobvector/client/milvus_like_client.py +14 -14
- pyobvector/client/ob_client.py +29 -9
- pyobvector/client/ob_vec_client.py +20 -16
- pyobvector/client/ob_vec_json_table_client.py +20 -14
- pyobvector/client/partitions.py +17 -17
- pyobvector/json_table/json_value_returning_func.py +1 -2
- pyobvector/json_table/virtual_data_type.py +1 -1
- pyobvector/schema/array.py +5 -4
- pyobvector/schema/geo_srid_point.py +2 -2
- pyobvector/schema/gis_func.py +1 -2
- pyobvector/schema/reflection.py +2 -2
- pyobvector/util/ob_version.py +1 -2
- {pyobvector-0.2.19.dist-info → pyobvector-0.2.21.dist-info}/METADATA +18 -22
- pyobvector-0.2.21.dist-info/RECORD +40 -0
- {pyobvector-0.2.19.dist-info → pyobvector-0.2.21.dist-info}/WHEEL +1 -1
- pyobvector-0.2.19.dist-info/RECORD +0 -40
- {pyobvector-0.2.19.dist-info → pyobvector-0.2.21.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""FieldSchema & CollectionSchema definition module to be compatible with Milvus."""
|
|
2
2
|
import copy
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional
|
|
4
4
|
from sqlalchemy import Column
|
|
5
5
|
from .schema_type import DataType, convert_datatype_to_sqltype
|
|
6
6
|
from .exceptions import *
|
|
@@ -125,7 +125,7 @@ class CollectionSchema:
|
|
|
125
125
|
"""
|
|
126
126
|
def __init__(
|
|
127
127
|
self,
|
|
128
|
-
fields: Optional[
|
|
128
|
+
fields: Optional[list[FieldSchema]] = None,
|
|
129
129
|
partitions: Optional[ObPartition] = None,
|
|
130
130
|
description: str = "", # ignored in oceanbase
|
|
131
131
|
**kwargs,
|
pyobvector/client/exceptions.py
CHANGED
|
@@ -85,13 +85,13 @@ class ExceptionsMessage:
|
|
|
85
85
|
"Range expression is necessary when partition type is Range"
|
|
86
86
|
)
|
|
87
87
|
PartitionRangeColNameListMissing = (
|
|
88
|
-
"Column name list is necessary when
|
|
88
|
+
"Column name list is necessary when partition type is RangeColumns"
|
|
89
89
|
)
|
|
90
90
|
PartitionListExprMissing = (
|
|
91
91
|
"List expression is necessary when partition type is List"
|
|
92
92
|
)
|
|
93
93
|
PartitionListColNameListMissing = (
|
|
94
|
-
"Column name list is necessary when
|
|
94
|
+
"Column name list is necessary when partition type is ListColumns"
|
|
95
95
|
)
|
|
96
96
|
PartitionHashNameListAndPartCntMissing = (
|
|
97
97
|
"One of hash_part_name_list and part_count must be set when partition type is Hash"
|
|
@@ -1,28 +1,58 @@
|
|
|
1
1
|
"""A module to specify fts index parameters"""
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional, Union
|
|
4
4
|
|
|
5
5
|
class FtsParser(Enum):
|
|
6
|
+
"""Built-in full-text search parser types supported by OceanBase"""
|
|
6
7
|
IK = 0
|
|
7
8
|
NGRAM = 1
|
|
9
|
+
NGRAM2 = 2 # NGRAM2 parser (supported from V4.3.5 BP2+)
|
|
10
|
+
BASIC_ENGLISH = 3 # Basic English parser
|
|
11
|
+
JIEBA = 4 # jieba parser
|
|
8
12
|
|
|
9
13
|
|
|
10
14
|
class FtsIndexParam:
|
|
15
|
+
"""Full-text search index parameter.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
index_name: Index name
|
|
19
|
+
field_names: List of field names to create full-text index on
|
|
20
|
+
parser_type: Parser type, can be FtsParser enum or string (for custom parsers)
|
|
21
|
+
If None, uses default Space parser
|
|
22
|
+
"""
|
|
11
23
|
def __init__(
|
|
12
24
|
self,
|
|
13
25
|
index_name: str,
|
|
14
|
-
field_names:
|
|
15
|
-
parser_type: Optional[FtsParser],
|
|
26
|
+
field_names: list[str],
|
|
27
|
+
parser_type: Optional[Union[FtsParser, str]] = None,
|
|
16
28
|
):
|
|
17
29
|
self.index_name = index_name
|
|
18
30
|
self.field_names = field_names
|
|
19
31
|
self.parser_type = parser_type
|
|
20
32
|
|
|
21
|
-
def param_str(self) -> str
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
def param_str(self) -> Optional[str]:
|
|
34
|
+
"""Convert parser type to string format for SQL."""
|
|
35
|
+
if self.parser_type is None:
|
|
36
|
+
return None # Default Space parser, no need to specify
|
|
37
|
+
|
|
38
|
+
if isinstance(self.parser_type, str):
|
|
39
|
+
# Custom parser name (e.g., "thai_ftparser")
|
|
40
|
+
return self.parser_type.lower()
|
|
41
|
+
|
|
42
|
+
if isinstance(self.parser_type, FtsParser):
|
|
43
|
+
if self.parser_type == FtsParser.IK:
|
|
44
|
+
return "ik"
|
|
45
|
+
if self.parser_type == FtsParser.NGRAM:
|
|
46
|
+
return "ngram"
|
|
47
|
+
if self.parser_type == FtsParser.NGRAM2:
|
|
48
|
+
return "ngram2"
|
|
49
|
+
if self.parser_type == FtsParser.BASIC_ENGLISH:
|
|
50
|
+
return "beng"
|
|
51
|
+
if self.parser_type == FtsParser.JIEBA:
|
|
52
|
+
return "jieba"
|
|
53
|
+
# Raise exception for unrecognized FtsParser enum values
|
|
54
|
+
raise ValueError(f"Unrecognized FtsParser enum value: {self.parser_type}")
|
|
55
|
+
|
|
26
56
|
return None
|
|
27
57
|
|
|
28
58
|
def __iter__(self):
|
|
@@ -34,7 +64,7 @@ class FtsIndexParam:
|
|
|
34
64
|
def __str__(self):
|
|
35
65
|
return str(dict(self))
|
|
36
66
|
|
|
37
|
-
def __eq__(self, other:
|
|
67
|
+
def __eq__(self, other: object) -> bool:
|
|
38
68
|
if isinstance(other, self.__class__):
|
|
39
69
|
return dict(self) == dict(other)
|
|
40
70
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""OceanBase Hybrid Search Client."""
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
from sqlalchemy import text
|
|
7
7
|
|
|
@@ -30,13 +30,9 @@ class HybridSearch(Client):
|
|
|
30
30
|
|
|
31
31
|
if self.ob_version < min_required_version:
|
|
32
32
|
# For versions < 4.4.1.0, check if it's SeekDB
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
version_str = [r[0] for r in res][0]
|
|
37
|
-
if "SeekDB" in version_str:
|
|
38
|
-
logger.info(f"SeekDB detected in version string: {version_str}, allowing hybrid search")
|
|
39
|
-
return
|
|
33
|
+
if self._is_seekdb():
|
|
34
|
+
logger.info("SeekDB detected, allowing hybrid search")
|
|
35
|
+
return
|
|
40
36
|
raise ClusterVersionException(
|
|
41
37
|
code=ErrorCode.NOT_SUPPORTED,
|
|
42
38
|
message=ExceptionsMessage.ClusterVersionIsLow % ("Hybrid Search", "4.4.1.0"),
|
|
@@ -45,7 +41,7 @@ class HybridSearch(Client):
|
|
|
45
41
|
def search(
|
|
46
42
|
self,
|
|
47
43
|
index: str,
|
|
48
|
-
body:
|
|
44
|
+
body: dict[str, Any],
|
|
49
45
|
**kwargs,
|
|
50
46
|
):
|
|
51
47
|
"""Execute hybrid search with parameter compatible with Elasticsearch.
|
|
@@ -70,7 +66,7 @@ class HybridSearch(Client):
|
|
|
70
66
|
def get_sql(
|
|
71
67
|
self,
|
|
72
68
|
index: str,
|
|
73
|
-
body:
|
|
69
|
+
body: dict[str, Any],
|
|
74
70
|
) -> str:
|
|
75
71
|
"""Get the SQL actually to be executed in hybrid search.
|
|
76
72
|
|
pyobvector/client/index_param.py
CHANGED
|
@@ -134,8 +134,11 @@ class IndexParam:
|
|
|
134
134
|
if 'efSearch' in params:
|
|
135
135
|
ob_params['ef_search'] = params['efSearch']
|
|
136
136
|
|
|
137
|
-
if self.is_index_type_sparse_vector()
|
|
138
|
-
|
|
137
|
+
if self.is_index_type_sparse_vector():
|
|
138
|
+
if ob_params['distance'] != 'inner_product':
|
|
139
|
+
raise ValueError("Metric type should be 'inner_product' for sparse vector index.")
|
|
140
|
+
if 'sparse_index_type' in self.kwargs:
|
|
141
|
+
ob_params['type'] = self.kwargs['sparse_index_type']
|
|
139
142
|
return ob_params
|
|
140
143
|
|
|
141
144
|
def param_str(self):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Milvus Like Client."""
|
|
2
2
|
import logging
|
|
3
3
|
import json
|
|
4
|
-
from typing import Optional, Union
|
|
4
|
+
from typing import Optional, Union
|
|
5
5
|
|
|
6
6
|
from sqlalchemy.exc import NoSuchTableError
|
|
7
7
|
from sqlalchemy import (
|
|
@@ -147,7 +147,7 @@ class MilvusLikeClient(Client):
|
|
|
147
147
|
|
|
148
148
|
def get_collection_stats(
|
|
149
149
|
self, collection_name: str, timeout: Optional[float] = None # pylint: disable=unused-argument
|
|
150
|
-
) ->
|
|
150
|
+
) -> dict:
|
|
151
151
|
"""Get collection row count.
|
|
152
152
|
|
|
153
153
|
Args:
|
|
@@ -354,12 +354,12 @@ class MilvusLikeClient(Client):
|
|
|
354
354
|
with_dist: bool = False,
|
|
355
355
|
flter=None,
|
|
356
356
|
limit: int = 10,
|
|
357
|
-
output_fields: Optional[
|
|
357
|
+
output_fields: Optional[list[str]] = None,
|
|
358
358
|
search_params: Optional[dict] = None,
|
|
359
359
|
timeout: Optional[float] = None, # pylint: disable=unused-argument
|
|
360
|
-
partition_names: Optional[
|
|
360
|
+
partition_names: Optional[list[str]] = None,
|
|
361
361
|
**kwargs, # pylint: disable=unused-argument
|
|
362
|
-
) ->
|
|
362
|
+
) -> list[dict]:
|
|
363
363
|
"""Perform ann search.
|
|
364
364
|
Note: OceanBase does not support batch search now. `data` & the return value is not a batch.
|
|
365
365
|
|
|
@@ -467,11 +467,11 @@ class MilvusLikeClient(Client):
|
|
|
467
467
|
self,
|
|
468
468
|
collection_name: str,
|
|
469
469
|
flter=None,
|
|
470
|
-
output_fields: Optional[
|
|
470
|
+
output_fields: Optional[list[str]] = None,
|
|
471
471
|
timeout: Optional[float] = None, # pylint: disable=unused-argument
|
|
472
|
-
partition_names: Optional[
|
|
472
|
+
partition_names: Optional[list[str]] = None,
|
|
473
473
|
**kwargs, # pylint: disable=unused-argument
|
|
474
|
-
) ->
|
|
474
|
+
) -> list[dict]:
|
|
475
475
|
"""Query records.
|
|
476
476
|
|
|
477
477
|
Args:
|
|
@@ -533,11 +533,11 @@ class MilvusLikeClient(Client):
|
|
|
533
533
|
self,
|
|
534
534
|
collection_name: str,
|
|
535
535
|
ids: Union[list, str, int] = None,
|
|
536
|
-
output_fields: Optional[
|
|
536
|
+
output_fields: Optional[list[str]] = None,
|
|
537
537
|
timeout: Optional[float] = None, # pylint: disable=unused-argument
|
|
538
|
-
partition_names: Optional[
|
|
538
|
+
partition_names: Optional[list[str]] = None,
|
|
539
539
|
**kwargs, # pylint: disable=unused-argument
|
|
540
|
-
) ->
|
|
540
|
+
) -> list[dict]:
|
|
541
541
|
"""Get records with specified primary field `ids`.
|
|
542
542
|
|
|
543
543
|
Args:
|
|
@@ -672,7 +672,7 @@ class MilvusLikeClient(Client):
|
|
|
672
672
|
def insert(
|
|
673
673
|
self,
|
|
674
674
|
collection_name: str,
|
|
675
|
-
data: Union[
|
|
675
|
+
data: Union[dict, list[dict]],
|
|
676
676
|
timeout: Optional[float] = None,
|
|
677
677
|
partition_name: Optional[str] = "",
|
|
678
678
|
) -> (
|
|
@@ -700,10 +700,10 @@ class MilvusLikeClient(Client):
|
|
|
700
700
|
def upsert(
|
|
701
701
|
self,
|
|
702
702
|
collection_name: str,
|
|
703
|
-
data: Union[
|
|
703
|
+
data: Union[dict, list[dict]],
|
|
704
704
|
timeout: Optional[float] = None, # pylint: disable=unused-argument
|
|
705
705
|
partition_name: Optional[str] = "",
|
|
706
|
-
) ->
|
|
706
|
+
) -> list[Union[str, int]]:
|
|
707
707
|
"""Update data in table. If primary key is duplicated, replace it.
|
|
708
708
|
|
|
709
709
|
Args:
|
pyobvector/client/ob_client.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional, Union
|
|
3
3
|
from urllib.parse import quote
|
|
4
4
|
|
|
5
5
|
import sqlalchemy.sql.functions as func_mod
|
|
@@ -93,6 +93,26 @@ class ObClient:
|
|
|
93
93
|
self.metadata_obj.clear()
|
|
94
94
|
self.metadata_obj.reflect(bind=self.engine, extend_existing=True)
|
|
95
95
|
|
|
96
|
+
def _is_seekdb(self) -> bool:
|
|
97
|
+
"""Check if the database is SeekDB by querying version.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
bool: True if database is SeekDB, False otherwise
|
|
101
|
+
"""
|
|
102
|
+
is_seekdb = False
|
|
103
|
+
try:
|
|
104
|
+
if hasattr(self, '_is_seekdb_cached'):
|
|
105
|
+
return self._is_seekdb_cached
|
|
106
|
+
with self.engine.connect() as conn:
|
|
107
|
+
result = conn.execute(text("SELECT VERSION()"))
|
|
108
|
+
version_str = [r[0] for r in result][0]
|
|
109
|
+
is_seekdb = "SeekDB" in version_str
|
|
110
|
+
self._is_seekdb_cached = is_seekdb
|
|
111
|
+
logger.debug(f"Version query result: {version_str}, is_seekdb: {is_seekdb}")
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.warning(f"Failed to query version: {e}")
|
|
114
|
+
return is_seekdb
|
|
115
|
+
|
|
96
116
|
def _insert_partition_hint_for_query_sql(self, sql: str, partition_hint: str):
|
|
97
117
|
from_index = sql.find("FROM")
|
|
98
118
|
assert from_index != -1
|
|
@@ -121,8 +141,8 @@ class ObClient:
|
|
|
121
141
|
def create_table(
|
|
122
142
|
self,
|
|
123
143
|
table_name: str,
|
|
124
|
-
columns:
|
|
125
|
-
indexes: Optional[
|
|
144
|
+
columns: list[Column],
|
|
145
|
+
indexes: Optional[list[Index]] = None,
|
|
126
146
|
partitions: Optional[ObPartition] = None,
|
|
127
147
|
**kwargs,
|
|
128
148
|
):
|
|
@@ -188,7 +208,7 @@ class ObClient:
|
|
|
188
208
|
def insert(
|
|
189
209
|
self,
|
|
190
210
|
table_name: str,
|
|
191
|
-
data: Union[
|
|
211
|
+
data: Union[dict, list[dict]],
|
|
192
212
|
partition_name: Optional[str] = "",
|
|
193
213
|
):
|
|
194
214
|
"""Insert data into table.
|
|
@@ -198,7 +218,7 @@ class ObClient:
|
|
|
198
218
|
data (Union[Dict, List[Dict]]): data that will be inserted
|
|
199
219
|
partition_name (Optional[str]): limit the query to certain partition
|
|
200
220
|
"""
|
|
201
|
-
if isinstance(data,
|
|
221
|
+
if isinstance(data, dict):
|
|
202
222
|
data = [data]
|
|
203
223
|
|
|
204
224
|
if len(data) == 0:
|
|
@@ -220,7 +240,7 @@ class ObClient:
|
|
|
220
240
|
def upsert(
|
|
221
241
|
self,
|
|
222
242
|
table_name: str,
|
|
223
|
-
data: Union[
|
|
243
|
+
data: Union[dict, list[dict]],
|
|
224
244
|
partition_name: Optional[str] = "",
|
|
225
245
|
):
|
|
226
246
|
"""Update data in table. If primary key is duplicated, replace it.
|
|
@@ -230,7 +250,7 @@ class ObClient:
|
|
|
230
250
|
data (Union[Dict, List[Dict]]): data that will be upserted
|
|
231
251
|
partition_name (Optional[str]): limit the query to certain partition
|
|
232
252
|
"""
|
|
233
|
-
if isinstance(data,
|
|
253
|
+
if isinstance(data, dict):
|
|
234
254
|
data = [data]
|
|
235
255
|
|
|
236
256
|
if len(data) == 0:
|
|
@@ -345,8 +365,8 @@ class ObClient:
|
|
|
345
365
|
table_name: str,
|
|
346
366
|
ids: Optional[Union[list, str, int]] = None,
|
|
347
367
|
where_clause=None,
|
|
348
|
-
output_column_name: Optional[
|
|
349
|
-
partition_names: Optional[
|
|
368
|
+
output_column_name: Optional[list[str]] = None,
|
|
369
|
+
partition_names: Optional[list[str]] = None,
|
|
350
370
|
n_limits: Optional[int] = None,
|
|
351
371
|
):
|
|
352
372
|
"""Get records with specified primary field `ids`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""OceanBase Vector Store Client."""
|
|
2
2
|
import logging
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional, Union
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
from sqlalchemy import (
|
|
@@ -60,10 +60,10 @@ class ObVecClient(ObClient):
|
|
|
60
60
|
def create_table_with_index_params(
|
|
61
61
|
self,
|
|
62
62
|
table_name: str,
|
|
63
|
-
columns:
|
|
64
|
-
indexes: Optional[
|
|
63
|
+
columns: list[Column],
|
|
64
|
+
indexes: Optional[list[Index]] = None,
|
|
65
65
|
vidxs: Optional[IndexParams] = None,
|
|
66
|
-
fts_idxs: Optional[
|
|
66
|
+
fts_idxs: Optional[list[FtsIndexParam]] = None,
|
|
67
67
|
partitions: Optional[ObPartition] = None,
|
|
68
68
|
):
|
|
69
69
|
"""Create table with optional index_params.
|
|
@@ -99,7 +99,11 @@ class ObVecClient(ObClient):
|
|
|
99
99
|
create_table_sql = str(CreateTable(table).compile(self.engine))
|
|
100
100
|
new_sql = create_table_sql[:create_table_sql.rfind(')')]
|
|
101
101
|
for sparse_vidx in sparse_vidxs:
|
|
102
|
-
|
|
102
|
+
sparse_params = sparse_vidx._parse_kwargs()
|
|
103
|
+
if 'type' in sparse_params:
|
|
104
|
+
new_sql += f",\n\tVECTOR INDEX {sparse_vidx.index_name}({sparse_vidx.field_name}) with (type={sparse_params['type']}, distance=inner_product)"
|
|
105
|
+
else:
|
|
106
|
+
new_sql += f",\n\tVECTOR INDEX {sparse_vidx.index_name}({sparse_vidx.field_name}) with (distance=inner_product)"
|
|
103
107
|
new_sql += "\n)"
|
|
104
108
|
conn.execute(text(new_sql))
|
|
105
109
|
else:
|
|
@@ -136,7 +140,7 @@ class ObVecClient(ObClient):
|
|
|
136
140
|
table_name: str,
|
|
137
141
|
is_vec_index: bool,
|
|
138
142
|
index_name: str,
|
|
139
|
-
column_names:
|
|
143
|
+
column_names: list[str],
|
|
140
144
|
vidx_params: Optional[str] = None,
|
|
141
145
|
**kw,
|
|
142
146
|
):
|
|
@@ -270,12 +274,12 @@ class ObVecClient(ObClient):
|
|
|
270
274
|
distance_func,
|
|
271
275
|
with_dist: bool = False,
|
|
272
276
|
topk: int = 10,
|
|
273
|
-
output_column_names: Optional[
|
|
274
|
-
output_columns: Optional[Union[
|
|
275
|
-
extra_output_cols: Optional[
|
|
277
|
+
output_column_names: Optional[list[str]] = None,
|
|
278
|
+
output_columns: Optional[Union[list, tuple]] = None,
|
|
279
|
+
extra_output_cols: Optional[list] = None,
|
|
276
280
|
where_clause=None,
|
|
277
|
-
partition_names: Optional[
|
|
278
|
-
idx_name_hint: Optional[
|
|
281
|
+
partition_names: Optional[list[str]] = None,
|
|
282
|
+
idx_name_hint: Optional[list[str]] = None,
|
|
279
283
|
distance_threshold: Optional[float] = None,
|
|
280
284
|
**kwargs,
|
|
281
285
|
): # pylint: disable=unused-argument
|
|
@@ -399,11 +403,11 @@ class ObVecClient(ObClient):
|
|
|
399
403
|
distance_func,
|
|
400
404
|
with_dist: bool = False,
|
|
401
405
|
topk: int = 10,
|
|
402
|
-
output_column_names: Optional[
|
|
403
|
-
extra_output_cols: Optional[
|
|
406
|
+
output_column_names: Optional[list[str]] = None,
|
|
407
|
+
extra_output_cols: Optional[list] = None,
|
|
404
408
|
where_clause=None,
|
|
405
|
-
partition_names: Optional[
|
|
406
|
-
str_list: Optional[
|
|
409
|
+
partition_names: Optional[list[str]] = None,
|
|
410
|
+
str_list: Optional[list[str]] = None,
|
|
407
411
|
**kwargs,
|
|
408
412
|
): # pylint: disable=unused-argument
|
|
409
413
|
"""Perform post ann search.
|
|
@@ -479,7 +483,7 @@ class ObVecClient(ObClient):
|
|
|
479
483
|
vec_column_name: str,
|
|
480
484
|
distance_func,
|
|
481
485
|
topk: int = 10,
|
|
482
|
-
output_column_names: Optional[
|
|
486
|
+
output_column_names: Optional[list[str]] = None,
|
|
483
487
|
where_clause=None,
|
|
484
488
|
**kwargs,
|
|
485
489
|
): # pylint: disable=unused-argument
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Optional, 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
|
|
8
8
|
from sqlalchemy.orm import declarative_base, sessionmaker, Session
|
|
9
9
|
from sqlglot import parse_one, exp, Expression, to_identifier
|
|
10
|
-
from sqlglot.expressions import Concat
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
from .ob_vec_client import ObVecClient
|
|
14
13
|
from ..json_table import (
|
|
15
|
-
OceanBase,
|
|
16
14
|
ChangeColumn,
|
|
17
15
|
JsonTableBool,
|
|
18
16
|
JsonTableTimestamp,
|
|
@@ -58,7 +56,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
58
56
|
class JsonTableMetadata:
|
|
59
57
|
def __init__(self, user_id: str):
|
|
60
58
|
self.user_id = user_id
|
|
61
|
-
self.meta_cache:
|
|
59
|
+
self.meta_cache: dict[str, list] = {}
|
|
62
60
|
|
|
63
61
|
@classmethod
|
|
64
62
|
def _parse_col_type(cls, col_type: str):
|
|
@@ -319,7 +317,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
319
317
|
def _check_table_exists(self, jtable_name: str) -> bool:
|
|
320
318
|
return jtable_name in self.jmetadata.meta_cache
|
|
321
319
|
|
|
322
|
-
def _check_col_exists(self, jtable_name: str, col_name: str) -> Optional[
|
|
320
|
+
def _check_col_exists(self, jtable_name: str, col_name: str) -> Optional[dict]:
|
|
323
321
|
if not self._check_table_exists(jtable_name):
|
|
324
322
|
return None
|
|
325
323
|
for col_meta in self.jmetadata.meta_cache[jtable_name]:
|
|
@@ -339,7 +337,7 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
339
337
|
col_type_str += '(' + ','.join(col_type_params_list) + ')'
|
|
340
338
|
return col_type_str
|
|
341
339
|
|
|
342
|
-
def _parse_col_constraints(self, expr: Expression) ->
|
|
340
|
+
def _parse_col_constraints(self, expr: Expression) -> dict:
|
|
343
341
|
col_has_default = False
|
|
344
342
|
col_nullable = True
|
|
345
343
|
for cons in expr:
|
|
@@ -817,11 +815,12 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
817
815
|
):
|
|
818
816
|
real_user_id = opt_user_id or self.user_id
|
|
819
817
|
|
|
820
|
-
|
|
818
|
+
from_key = 'from_' if 'from_' in ast.args else 'from'
|
|
819
|
+
table_name = ast.args[from_key].this.this.this
|
|
821
820
|
if not self._check_table_exists(table_name):
|
|
822
821
|
raise ValueError(f"Table {table_name} does not exists")
|
|
823
822
|
|
|
824
|
-
ast.args[
|
|
823
|
+
ast.args[from_key].args['this'].args['this'] = to_identifier(name=JSON_TABLE_DATA_TABLE_NAME, quoted=False)
|
|
825
824
|
|
|
826
825
|
col_meta = self.jmetadata.meta_cache[table_name]
|
|
827
826
|
json_table_meta_str = []
|
|
@@ -883,12 +882,19 @@ class ObVecJsonTableClient(ObVecClient):
|
|
|
883
882
|
identifier.args['quoted'] = False
|
|
884
883
|
col.args['table'] = identifier
|
|
885
884
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
885
|
+
# Manually create the JOIN node for json_table
|
|
886
|
+
# In some versions of sqlglot, comma-separated tables may not be parsed as
|
|
887
|
+
# explicit JOINS, so we directly parse the json_table expression and create a JOIN node
|
|
888
|
+
# explicitly
|
|
889
|
+
json_table_expr = parse_one(json_table_str, dialect="oceanbase")
|
|
890
|
+
|
|
891
|
+
join_node = exp.Join()
|
|
892
|
+
join_node.args['this'] = json_table_expr
|
|
893
|
+
join_node.args['kind'] = None # CROSS JOIN (implicit join with comma)
|
|
894
|
+
|
|
895
|
+
if 'joins' not in ast.args:
|
|
896
|
+
ast.args['joins'] = []
|
|
897
|
+
ast.args['joins'].append(join_node)
|
|
892
898
|
|
|
893
899
|
if real_user_id:
|
|
894
900
|
extra_filter_str = f"{JSON_TABLE_DATA_TABLE_NAME}.user_id = '{real_user_id}' AND {JSON_TABLE_DATA_TABLE_NAME}.jtable_name = '{table_name}'"
|
pyobvector/client/partitions.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""A module to do compilation of OceanBase Parition Clause."""
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional, Union
|
|
3
3
|
import logging
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from .enum import IntEnum
|
|
@@ -69,11 +69,11 @@ class RangeListPartInfo:
|
|
|
69
69
|
Using 100 / `MAXVALUE` when create Range/RangeColumns partition.
|
|
70
70
|
"""
|
|
71
71
|
part_name: str
|
|
72
|
-
part_upper_bound_expr: Union[
|
|
72
|
+
part_upper_bound_expr: Union[list, str, int]
|
|
73
73
|
|
|
74
74
|
def get_part_expr_str(self):
|
|
75
75
|
"""Parse part_upper_bound_expr to text SQL."""
|
|
76
|
-
if isinstance(self.part_upper_bound_expr,
|
|
76
|
+
if isinstance(self.part_upper_bound_expr, list):
|
|
77
77
|
return ",".join([str(v) for v in self.part_upper_bound_expr])
|
|
78
78
|
if isinstance(self.part_upper_bound_expr, str):
|
|
79
79
|
return self.part_upper_bound_expr
|
|
@@ -87,9 +87,9 @@ class ObRangePartition(ObPartition):
|
|
|
87
87
|
def __init__(
|
|
88
88
|
self,
|
|
89
89
|
is_range_columns: bool,
|
|
90
|
-
range_part_infos:
|
|
90
|
+
range_part_infos: list[RangeListPartInfo],
|
|
91
91
|
range_expr: Optional[str] = None,
|
|
92
|
-
col_name_list: Optional[
|
|
92
|
+
col_name_list: Optional[list[str]] = None,
|
|
93
93
|
):
|
|
94
94
|
super().__init__(PartType.RangeColumns if is_range_columns else PartType.Range)
|
|
95
95
|
self.range_part_infos = range_part_infos
|
|
@@ -140,9 +140,9 @@ class ObSubRangePartition(ObRangePartition):
|
|
|
140
140
|
def __init__(
|
|
141
141
|
self,
|
|
142
142
|
is_range_columns: bool,
|
|
143
|
-
range_part_infos:
|
|
143
|
+
range_part_infos: list[RangeListPartInfo],
|
|
144
144
|
range_expr: Optional[str] = None,
|
|
145
|
-
col_name_list: Optional[
|
|
145
|
+
col_name_list: Optional[list[str]] = None,
|
|
146
146
|
):
|
|
147
147
|
super().__init__(is_range_columns, range_part_infos, range_expr, col_name_list)
|
|
148
148
|
self.is_sub = True
|
|
@@ -176,9 +176,9 @@ class ObListPartition(ObPartition):
|
|
|
176
176
|
def __init__(
|
|
177
177
|
self,
|
|
178
178
|
is_list_columns: bool,
|
|
179
|
-
list_part_infos:
|
|
179
|
+
list_part_infos: list[RangeListPartInfo],
|
|
180
180
|
list_expr: Optional[str] = None,
|
|
181
|
-
col_name_list: Optional[
|
|
181
|
+
col_name_list: Optional[list[str]] = None,
|
|
182
182
|
):
|
|
183
183
|
super().__init__(PartType.ListColumns if is_list_columns else PartType.List)
|
|
184
184
|
self.list_part_infos = list_part_infos
|
|
@@ -228,9 +228,9 @@ class ObSubListPartition(ObListPartition):
|
|
|
228
228
|
def __init__(
|
|
229
229
|
self,
|
|
230
230
|
is_list_columns: bool,
|
|
231
|
-
list_part_infos:
|
|
231
|
+
list_part_infos: list[RangeListPartInfo],
|
|
232
232
|
list_expr: Optional[str] = None,
|
|
233
|
-
col_name_list: Optional[
|
|
233
|
+
col_name_list: Optional[list[str]] = None,
|
|
234
234
|
):
|
|
235
235
|
super().__init__(is_list_columns, list_part_infos, list_expr, col_name_list)
|
|
236
236
|
self.is_sub = True
|
|
@@ -263,7 +263,7 @@ class ObHashPartition(ObPartition):
|
|
|
263
263
|
def __init__(
|
|
264
264
|
self,
|
|
265
265
|
hash_expr: str,
|
|
266
|
-
hash_part_name_list:
|
|
266
|
+
hash_part_name_list: list[str] = None,
|
|
267
267
|
part_count: Optional[int] = None,
|
|
268
268
|
):
|
|
269
269
|
super().__init__(PartType.Hash)
|
|
@@ -308,7 +308,7 @@ class ObSubHashPartition(ObHashPartition):
|
|
|
308
308
|
def __init__(
|
|
309
309
|
self,
|
|
310
310
|
hash_expr: str,
|
|
311
|
-
hash_part_name_list:
|
|
311
|
+
hash_part_name_list: list[str] = None,
|
|
312
312
|
part_count: Optional[int] = None,
|
|
313
313
|
):
|
|
314
314
|
super().__init__(hash_expr, hash_part_name_list, part_count)
|
|
@@ -334,8 +334,8 @@ class ObKeyPartition(ObPartition):
|
|
|
334
334
|
"""Key partition strategy."""
|
|
335
335
|
def __init__(
|
|
336
336
|
self,
|
|
337
|
-
col_name_list:
|
|
338
|
-
key_part_name_list:
|
|
337
|
+
col_name_list: list[str],
|
|
338
|
+
key_part_name_list: list[str] = None,
|
|
339
339
|
part_count: Optional[int] = None,
|
|
340
340
|
):
|
|
341
341
|
super().__init__(PartType.Key)
|
|
@@ -381,8 +381,8 @@ class ObSubKeyPartition(ObKeyPartition):
|
|
|
381
381
|
"""Key subpartition strategy."""
|
|
382
382
|
def __init__(
|
|
383
383
|
self,
|
|
384
|
-
col_name_list:
|
|
385
|
-
key_part_name_list:
|
|
384
|
+
col_name_list: list[str],
|
|
385
|
+
key_part_name_list: list[str] = None,
|
|
386
386
|
part_count: Optional[int] = None,
|
|
387
387
|
):
|
|
388
388
|
super().__init__(col_name_list, key_part_name_list, part_count)
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import re
|
|
3
|
-
from typing import Tuple
|
|
4
3
|
|
|
5
4
|
from sqlalchemy.ext.compiler import compiles
|
|
6
5
|
from sqlalchemy.sql.functions import FunctionElement
|
|
7
|
-
from sqlalchemy import
|
|
6
|
+
from sqlalchemy import Text
|
|
8
7
|
|
|
9
8
|
logger = logging.getLogger(__name__)
|
|
10
9
|
|
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
|
|
2
2
|
from decimal import Decimal, InvalidOperation, ROUND_DOWN
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from typing import Optional
|
|
5
|
-
from
|
|
5
|
+
from typing import Annotated
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel, Field, AfterValidator, create_model
|
|
8
8
|
|
pyobvector/schema/array.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""ARRAY: An extended data type for SQLAlchemy"""
|
|
2
2
|
import json
|
|
3
|
-
from typing import Any,
|
|
3
|
+
from typing import Any, Optional, Union
|
|
4
|
+
from collections.abc import Sequence
|
|
4
5
|
|
|
5
6
|
from sqlalchemy.sql.type_api import TypeEngine
|
|
6
7
|
from sqlalchemy.types import UserDefinedType, String
|
|
@@ -49,7 +50,7 @@ class ARRAY(UserDefinedType):
|
|
|
49
50
|
|
|
50
51
|
def _validate_dimension(self, value: list[Any]):
|
|
51
52
|
arr_depth = self._get_list_depth(value)
|
|
52
|
-
assert arr_depth == self.dim, "Array dimension mismatch, expected {}, got {}"
|
|
53
|
+
assert arr_depth == self.dim, f"Array dimension mismatch, expected {self.dim}, got {arr_depth}"
|
|
53
54
|
|
|
54
55
|
def bind_processor(self, dialect):
|
|
55
56
|
item_type = self.item_type
|
|
@@ -58,7 +59,7 @@ class ARRAY(UserDefinedType):
|
|
|
58
59
|
|
|
59
60
|
item_proc = item_type.dialect_impl(dialect).bind_processor(dialect)
|
|
60
61
|
|
|
61
|
-
def process(value: Optional[Sequence[Any] | str]) -> Optional[str]:
|
|
62
|
+
def process(value: Optional[Union[Sequence[Any] | str]]) -> Optional[str]:
|
|
62
63
|
if value is None:
|
|
63
64
|
return None
|
|
64
65
|
if isinstance(value, str):
|
|
@@ -85,7 +86,7 @@ class ARRAY(UserDefinedType):
|
|
|
85
86
|
|
|
86
87
|
item_proc = item_type.dialect_impl(dialect).result_processor(dialect, coltype)
|
|
87
88
|
|
|
88
|
-
def process(value: Optional[str]) -> Optional[
|
|
89
|
+
def process(value: Optional[str]) -> Optional[list[Any]]:
|
|
89
90
|
if value is None:
|
|
90
91
|
return None
|
|
91
92
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""Point: OceanBase GIS data type for SQLAlchemy"""
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
3
|
from sqlalchemy.types import UserDefinedType, String
|
|
4
4
|
|
|
5
5
|
class POINT(UserDefinedType):
|
|
@@ -24,7 +24,7 @@ class POINT(UserDefinedType):
|
|
|
24
24
|
return f"POINT SRID {self.srid}"
|
|
25
25
|
|
|
26
26
|
@classmethod
|
|
27
|
-
def to_db(cls, value:
|
|
27
|
+
def to_db(cls, value: tuple[float, float]):
|
|
28
28
|
"""Parse tuple to POINT literal"""
|
|
29
29
|
return f"POINT({value[0]} {value[1]})"
|
|
30
30
|
|
pyobvector/schema/gis_func.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""gis_func: An extended system function in GIS."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Tuple
|
|
5
4
|
|
|
6
5
|
from sqlalchemy.ext.compiler import compiles
|
|
7
6
|
from sqlalchemy.sql.functions import FunctionElement
|
|
@@ -30,7 +29,7 @@ def compile_ST_GeomFromText(element, compiler, **kwargs): # pylint: disable=unus
|
|
|
30
29
|
for idx, arg in enumerate(element.args):
|
|
31
30
|
if idx == 0:
|
|
32
31
|
if (
|
|
33
|
-
(not isinstance(arg,
|
|
32
|
+
(not isinstance(arg, tuple)) or
|
|
34
33
|
(len(arg) != 2) or
|
|
35
34
|
(not all(isinstance(x, float) for x in arg))
|
|
36
35
|
):
|
pyobvector/schema/reflection.py
CHANGED
|
@@ -35,12 +35,12 @@ class OceanBaseTableDefinitionParser(MySQLTableDefinitionParser):
|
|
|
35
35
|
|
|
36
36
|
self._re_array_column = _re_compile(
|
|
37
37
|
r"\s*"
|
|
38
|
-
r"
|
|
38
|
+
r"{iq}(?P<name>(?:{esc_fq}|[^{fq}])+){fq}\s+"
|
|
39
39
|
r"(?P<coltype_with_args>(?i:(?<!\w)array(?!\w))\s*\([^()]*(?:\([^()]*\)[^()]*)*\))"
|
|
40
40
|
r"(?:\s+(?P<notnull>(?:NOT\s+)?NULL))?"
|
|
41
41
|
r"(?:\s+DEFAULT\s+(?P<default>(?:NULL|'(?:''|[^'])*'|\(.+?\)|[\-\w\.\(\)]+)))?"
|
|
42
42
|
r"(?:\s+COMMENT\s+'(?P<comment>(?:''|[^'])*)')?"
|
|
43
|
-
r"\s*,?\s*$"
|
|
43
|
+
r"\s*,?\s*$".format(**quotes)
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
self._re_key = _re_compile(
|
pyobvector/util/ob_version.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""OceanBase cluster version module."""
|
|
2
2
|
import copy
|
|
3
|
-
from typing import List
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
class ObVersion:
|
|
@@ -9,7 +8,7 @@ class ObVersion:
|
|
|
9
8
|
Attributes:
|
|
10
9
|
version_nums (List[int]): version number of OceanBase cluster. For example, '4.3.3.0'
|
|
11
10
|
"""
|
|
12
|
-
def __init__(self, version_nums:
|
|
11
|
+
def __init__(self, version_nums: list[int]):
|
|
13
12
|
self.version_nums = copy.deepcopy(version_nums)
|
|
14
13
|
|
|
15
14
|
@classmethod
|
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyobvector
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.21
|
|
4
4
|
Summary: A python SDK for OceanBase Vector Store, based on SQLAlchemy, compatible with Milvus API.
|
|
5
|
+
Project-URL: Homepage, https://github.com/oceanbase/pyobvector
|
|
6
|
+
Project-URL: Repository, https://github.com/oceanbase/pyobvector.git
|
|
7
|
+
Author-email: "shanhaikang.shk" <shanhaikang.shk@oceanbase.com>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
5
9
|
License-File: LICENSE
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Requires-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
-
Requires-Dist: aiomysql (>=0.3.2,<0.4.0)
|
|
17
|
-
Requires-Dist: numpy (>=1.17.0)
|
|
18
|
-
Requires-Dist: pydantic (>=2.7.0,<3)
|
|
19
|
-
Requires-Dist: pymysql (>=1.1.1,<2.0.0)
|
|
20
|
-
Requires-Dist: sqlalchemy (>=1.4,<=3)
|
|
21
|
-
Requires-Dist: sqlglot (>=26.0.1)
|
|
10
|
+
Keywords: obvector,oceanbase,vector store
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Requires-Dist: aiomysql>=0.3.2
|
|
13
|
+
Requires-Dist: numpy>=1.17.0
|
|
14
|
+
Requires-Dist: pydantic<3,>=2.7.0
|
|
15
|
+
Requires-Dist: pymysql>=1.1.1
|
|
16
|
+
Requires-Dist: sqlalchemy<=3,>=1.4
|
|
17
|
+
Requires-Dist: sqlglot>=26.0.1
|
|
22
18
|
Description-Content-Type: text/markdown
|
|
23
19
|
|
|
24
20
|
# pyobvector
|
|
@@ -32,13 +28,13 @@ A python SDK for OceanBase Multimodal Store (Vector Store / Full Text Search / J
|
|
|
32
28
|
- git clone this repo, then install with:
|
|
33
29
|
|
|
34
30
|
```shell
|
|
35
|
-
|
|
31
|
+
uv sync
|
|
36
32
|
```
|
|
37
33
|
|
|
38
34
|
- install with pip:
|
|
39
35
|
|
|
40
36
|
```shell
|
|
41
|
-
pip install pyobvector==0.2.
|
|
37
|
+
pip install pyobvector==0.2.21
|
|
42
38
|
```
|
|
43
39
|
|
|
44
40
|
## Build Doc
|
|
@@ -234,17 +230,17 @@ res = self.client.ann_search(
|
|
|
234
230
|
The `ann_search` method supports flexible output column selection through the `output_columns` parameter:
|
|
235
231
|
|
|
236
232
|
- **`output_columns`** (recommended): Accepts SQLAlchemy Column objects, expressions, or a mix of both
|
|
237
|
-
|
|
233
|
+
|
|
238
234
|
- Column objects: `table.c.id`, `table.c.name`
|
|
239
235
|
- Expressions: `(table.c.age + 10).label('age_plus_10')`
|
|
240
236
|
- JSON queries: `text("JSON_EXTRACT(meta, '$.key') as extracted_key")`
|
|
241
237
|
- String functions: `func.concat(table.c.name, ' (', table.c.age, ')').label('name_age')`
|
|
242
238
|
- **`output_column_names`** (legacy): Accepts list of column name strings
|
|
243
|
-
|
|
239
|
+
|
|
244
240
|
- Example: `['id', 'name', 'meta']`
|
|
245
241
|
- **Parameter Priority**: `output_columns` takes precedence over `output_column_names` when both are provided
|
|
246
242
|
- **`distance_threshold`** (optional): Filter results by distance threshold
|
|
247
|
-
|
|
243
|
+
|
|
248
244
|
- Type: `Optional[float]`
|
|
249
245
|
- Only returns results where `distance <= threshold`
|
|
250
246
|
- Example: `distance_threshold=0.5` returns only results with distance <= 0.5
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
pyobvector/__init__.py,sha256=5RtWI_ol3qxj_m7UXTQzByTmGZdIiQ0yyCyHZUhRDas,3888
|
|
2
|
+
pyobvector/client/__init__.py,sha256=fDK2FVdSm3-XCwTqsam7zisic5UMhANUq97r29i27nc,2819
|
|
3
|
+
pyobvector/client/collection_schema.py,sha256=lQTlanxhfZ0W74HUEB7XwvnkwJASxNSsxurXCvMv8nw,5532
|
|
4
|
+
pyobvector/client/enum.py,sha256=3lPjSltITSE694-qOAP4yoX6fzCjKD4WAewmIxFs49o,139
|
|
5
|
+
pyobvector/client/exceptions.py,sha256=UxU05Md2ZGFspZ8pd02B8EAhp8PBbOUJFopa28IwAHI,3777
|
|
6
|
+
pyobvector/client/fts_index_param.py,sha256=owpjerUV4MIxB69dzwMsh7XIosgLB0OXgaLghV7wUYQ,2475
|
|
7
|
+
pyobvector/client/hybrid_search.py,sha256=xctd-UMYPT4FTr2hWNf8dHWRyT0GAAqSDy3tz_oTP00,2565
|
|
8
|
+
pyobvector/client/index_param.py,sha256=Tbu_-qg4SIXIzn1qpTg8Zl1L_RCXCUAEsZPKsA0LsNY,7062
|
|
9
|
+
pyobvector/client/milvus_like_client.py,sha256=waOqsgGtc5FSEM-BECBGCa6m81d_Pxf1r40LFU-LrmE,27875
|
|
10
|
+
pyobvector/client/ob_client.py,sha256=BsWCEGR30Klf2OUe88qLhBm7jRPBkgfjylQoy7B8EkM,16975
|
|
11
|
+
pyobvector/client/ob_vec_client.py,sha256=65189GcKo4sj_494XLNhqed_vKyu4sTIp_Tv9-zgBcY,20235
|
|
12
|
+
pyobvector/client/ob_vec_json_table_client.py,sha256=NFj7-1AQpTMk5XllecAgTKSGAq8qMIa36FM2cuhd_fQ,39605
|
|
13
|
+
pyobvector/client/partitions.py,sha256=CfkSe0ftb0jbdgdvNF9_SvXxcDhpOoS3pe7ezMeNI0c,15615
|
|
14
|
+
pyobvector/client/schema_type.py,sha256=gH2YiBsgkryo-R0GB_NYuRXMvzvrSjOamZTy6pwn2n0,1673
|
|
15
|
+
pyobvector/json_table/__init__.py,sha256=X5MmK3f10oyJleUUFZJFeunMEfzmf6P1f_7094b-FZc,554
|
|
16
|
+
pyobvector/json_table/json_value_returning_func.py,sha256=fW5Rk5vrVfyTPJPciPyb64A0CqSJBwBS-PYbcS9LhOY,1815
|
|
17
|
+
pyobvector/json_table/oceanbase_dialect.py,sha256=lxpbWBQdK18LWXLmGyk_-ODv6VfnwGLHbcpsQMElOUo,4480
|
|
18
|
+
pyobvector/json_table/virtual_data_type.py,sha256=ALffzZgByEwMsC-JdL_iEW3kwJcfqNGaNSIY645oUfo,3456
|
|
19
|
+
pyobvector/schema/__init__.py,sha256=OMn7Cd2E8o_tm2ArbAXS_zRbiDW2sj7YKPLnbpmaueg,2405
|
|
20
|
+
pyobvector/schema/array.py,sha256=YiGIyALN1_XxWuz9YyuiqhpyRN1DIu9n286vq6O09_U,4181
|
|
21
|
+
pyobvector/schema/dialect.py,sha256=6D9A7Niqig2mwK7C8sP7mCP7kYr5be2jV0xqL87mlz4,1999
|
|
22
|
+
pyobvector/schema/full_text_index.py,sha256=ohQX8uTPdRswEJONuN5A-bNv203d0N0b2BsJ7etx71g,2071
|
|
23
|
+
pyobvector/schema/geo_srid_point.py,sha256=BZuiySoemvtLBPYoZ_BRQT4Gd7fcU1C2xdQfkxpb0OQ,1203
|
|
24
|
+
pyobvector/schema/gis_func.py,sha256=sDhD701I45sRCXNY_E20ujjxe0ldAXofd6kTAhMW12g,3093
|
|
25
|
+
pyobvector/schema/match_against_func.py,sha256=ExTQJvAXHaZwBo1Sjy6IlnF1nF6D9xGUsF4f7zaP8Q0,1336
|
|
26
|
+
pyobvector/schema/ob_table.py,sha256=wlb6Oo9LG-sr8XnG_wbX1Qi5CgnS0XUzNL5qTdsncoY,392
|
|
27
|
+
pyobvector/schema/reflection.py,sha256=guS74lW1wzBOCv5FWeXS1a_KnS-5Md9C_dYaNqiK2yA,5903
|
|
28
|
+
pyobvector/schema/replace_stmt.py,sha256=FtGLXHz6DwzD0FOZPn1EZgXdbHZu-K9HIHS02rZqYrE,560
|
|
29
|
+
pyobvector/schema/sparse_vector.py,sha256=ojqUrvKUnxQE8FErB2B58KO540otOJBqiPTMlwcUW2M,1061
|
|
30
|
+
pyobvector/schema/vec_dist_func.py,sha256=4GAWSrhFNDYooBpbBg604wDrByPrewp46Y4VeoDxV7Y,2986
|
|
31
|
+
pyobvector/schema/vector.py,sha256=dFKfPcTOto0jNxVjhvDmJM7Q4wwp6Z-HcZ3K6oZxUMc,1120
|
|
32
|
+
pyobvector/schema/vector_index.py,sha256=D1ZnhJEObWUCd9ESO57KN1ctl10tkEIH_gV0kwGrvu8,2250
|
|
33
|
+
pyobvector/util/__init__.py,sha256=-opvZ4Ya0ByTAhls06547-zW3vkdYRkUH6W5OCKUHD4,388
|
|
34
|
+
pyobvector/util/ob_version.py,sha256=69me12REWn_T6oi7XjVAF_Dfq_iiaEVZjiHdjog57zs,1518
|
|
35
|
+
pyobvector/util/sparse_vector.py,sha256=1IG0CRYfC2z39nGwuG43TImQkWiuPAXOlOnYqJ1hA-o,1275
|
|
36
|
+
pyobvector/util/vector.py,sha256=58glSQqjOSTrGyNhUEIrs9r4F9oaYO_SdPNhMfbSnWs,2392
|
|
37
|
+
pyobvector-0.2.21.dist-info/METADATA,sha256=uuBysjcnC3Xb8Nevi8MfkEgqTheVkjdHFKbTuMheld4,14120
|
|
38
|
+
pyobvector-0.2.21.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
39
|
+
pyobvector-0.2.21.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
40
|
+
pyobvector-0.2.21.dist-info/RECORD,,
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
pyobvector/__init__.py,sha256=5RtWI_ol3qxj_m7UXTQzByTmGZdIiQ0yyCyHZUhRDas,3888
|
|
2
|
-
pyobvector/client/__init__.py,sha256=fDK2FVdSm3-XCwTqsam7zisic5UMhANUq97r29i27nc,2819
|
|
3
|
-
pyobvector/client/collection_schema.py,sha256=a7JQk83ZxMsvMDGt5CC_4lz2-skONqKgk-OGUz297hM,5538
|
|
4
|
-
pyobvector/client/enum.py,sha256=3lPjSltITSE694-qOAP4yoX6fzCjKD4WAewmIxFs49o,139
|
|
5
|
-
pyobvector/client/exceptions.py,sha256=CjoquSCc0H705MtvhpQW_F7_KaviYrKAHkndpF8sJx4,3777
|
|
6
|
-
pyobvector/client/fts_index_param.py,sha256=UvU82p9_x444WAQMqhIUPHbqVHV5B3cFazje1Gw-slo,1105
|
|
7
|
-
pyobvector/client/hybrid_search.py,sha256=xuD3CJh4jxm9EFjee6sx7zDGogvQT0qTni79F9Url7Q,2840
|
|
8
|
-
pyobvector/client/index_param.py,sha256=Dg-FEFQPBRxb7qXChqoLgBSljpdJzF-C7ESUbG9x1mA,6926
|
|
9
|
-
pyobvector/client/milvus_like_client.py,sha256=jHxB-ZmIGZiIDxEpurSlAKffiCF_KFNZP-14_vq1RQM,27887
|
|
10
|
-
pyobvector/client/ob_client.py,sha256=csSRVWqbhnsaN2gj7FSwL7QExh4vbTx6EyqPznKRSz4,16172
|
|
11
|
-
pyobvector/client/ob_vec_client.py,sha256=P6IMtsYxS_S78VsQtltK3UenW91iYfRKa0s1hIc8CI0,19916
|
|
12
|
-
pyobvector/client/ob_vec_json_table_client.py,sha256=rq80AfqAKhosLcrBFROAoINVSkr-48xlRH91Jt4pEwA,39246
|
|
13
|
-
pyobvector/client/partitions.py,sha256=Bxwr5yVNlXwZc7SXBC03NeqL9giy4Fe6S2qZdHD8xGw,15621
|
|
14
|
-
pyobvector/client/schema_type.py,sha256=gH2YiBsgkryo-R0GB_NYuRXMvzvrSjOamZTy6pwn2n0,1673
|
|
15
|
-
pyobvector/json_table/__init__.py,sha256=X5MmK3f10oyJleUUFZJFeunMEfzmf6P1f_7094b-FZc,554
|
|
16
|
-
pyobvector/json_table/json_value_returning_func.py,sha256=NWSV2zhe2-1KhIprQaFqOH3vUVF46YaHIZUqX66WZKM,1864
|
|
17
|
-
pyobvector/json_table/oceanbase_dialect.py,sha256=lxpbWBQdK18LWXLmGyk_-ODv6VfnwGLHbcpsQMElOUo,4480
|
|
18
|
-
pyobvector/json_table/virtual_data_type.py,sha256=uQh6ZQ0UbwpVO9TFegGeu4E8bXW7rdLHAXFQJdiEjLs,3467
|
|
19
|
-
pyobvector/schema/__init__.py,sha256=OMn7Cd2E8o_tm2ArbAXS_zRbiDW2sj7YKPLnbpmaueg,2405
|
|
20
|
-
pyobvector/schema/array.py,sha256=WDWLZbCdu8stK8wlGWfKUjkhWifS8vbsfYUEEJsQOlQ,4163
|
|
21
|
-
pyobvector/schema/dialect.py,sha256=6D9A7Niqig2mwK7C8sP7mCP7kYr5be2jV0xqL87mlz4,1999
|
|
22
|
-
pyobvector/schema/full_text_index.py,sha256=ohQX8uTPdRswEJONuN5A-bNv203d0N0b2BsJ7etx71g,2071
|
|
23
|
-
pyobvector/schema/geo_srid_point.py,sha256=RwEoCgGTmXDc0le1B2E3mZudtqiFdMf2M0Va1ocmVSY,1210
|
|
24
|
-
pyobvector/schema/gis_func.py,sha256=u7bqaB5qIylW8GvRdglLQL2H1SheQZNnAqgZrOGyrks,3118
|
|
25
|
-
pyobvector/schema/match_against_func.py,sha256=ExTQJvAXHaZwBo1Sjy6IlnF1nF6D9xGUsF4f7zaP8Q0,1336
|
|
26
|
-
pyobvector/schema/ob_table.py,sha256=wlb6Oo9LG-sr8XnG_wbX1Qi5CgnS0XUzNL5qTdsncoY,392
|
|
27
|
-
pyobvector/schema/reflection.py,sha256=orA0_lCdcIHw2TumtRCrAH3zG2yAWrGjXOmK5mK9XPw,5903
|
|
28
|
-
pyobvector/schema/replace_stmt.py,sha256=FtGLXHz6DwzD0FOZPn1EZgXdbHZu-K9HIHS02rZqYrE,560
|
|
29
|
-
pyobvector/schema/sparse_vector.py,sha256=ojqUrvKUnxQE8FErB2B58KO540otOJBqiPTMlwcUW2M,1061
|
|
30
|
-
pyobvector/schema/vec_dist_func.py,sha256=4GAWSrhFNDYooBpbBg604wDrByPrewp46Y4VeoDxV7Y,2986
|
|
31
|
-
pyobvector/schema/vector.py,sha256=dFKfPcTOto0jNxVjhvDmJM7Q4wwp6Z-HcZ3K6oZxUMc,1120
|
|
32
|
-
pyobvector/schema/vector_index.py,sha256=D1ZnhJEObWUCd9ESO57KN1ctl10tkEIH_gV0kwGrvu8,2250
|
|
33
|
-
pyobvector/util/__init__.py,sha256=-opvZ4Ya0ByTAhls06547-zW3vkdYRkUH6W5OCKUHD4,388
|
|
34
|
-
pyobvector/util/ob_version.py,sha256=cWkQWoJkIxG6OEF9-gBwJK8LUorltHuKSVAb_NFkpdE,1542
|
|
35
|
-
pyobvector/util/sparse_vector.py,sha256=1IG0CRYfC2z39nGwuG43TImQkWiuPAXOlOnYqJ1hA-o,1275
|
|
36
|
-
pyobvector/util/vector.py,sha256=58glSQqjOSTrGyNhUEIrs9r4F9oaYO_SdPNhMfbSnWs,2392
|
|
37
|
-
pyobvector-0.2.19.dist-info/METADATA,sha256=9r91zFxzOPl3zzhSy521IZKsVWl0oXXzVx7ELQFjsV4,14310
|
|
38
|
-
pyobvector-0.2.19.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
39
|
-
pyobvector-0.2.19.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
40
|
-
pyobvector-0.2.19.dist-info/RECORD,,
|
|
File without changes
|