llama-index-vector-stores-oceanbase 0.1.0__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.
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.1
2
+ Name: llama-index-vector-stores-oceanbase
3
+ Version: 0.1.0
4
+ Summary: llama-index vector-stores oceanbase integration
5
+ License: MIT
6
+ Author: GITHUBear
7
+ Author-email: shanhaikang.shk@oceanbase.com
8
+ Requires-Python: >=3.9,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Dist: llama-index-core (>=0.11.0,<0.12.0)
16
+ Requires-Dist: pyobvector (>=0.1.6,<0.2.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # LlamaIndex Vector-Stores Integration: Oceanbase
20
+
21
+ [OceanBase Database](https://github.com/oceanbase/oceanbase) is a distributed relational database.
22
+ It is developed entirely by Ant Group. The OceanBase Database is built on a common server cluster.
23
+ Based on the Paxos protocol and its distributed structure, the OceanBase Database provides high availability and linear scalability.
24
+
25
+ OceanBase currently has the ability to store vectors. Users can easily perform the following operations with SQL:
26
+
27
+ - Create a table containing vector type fields;
28
+ - Create a vector index table based on the HNSW algorithm;
29
+ - Perform vector approximate nearest neighbor queries;
30
+ - ...
31
+
32
+ ## Limitations
33
+
34
+ The vector storage capability of OceanBase is still being enhanced, and currently has the following limitations:
35
+
36
+ - Cosine vector distance function is not yet supported for vector index. Currently only inner product distance and Euclidean distance are supported, and Euclidean distance is used by default.
37
+ - It should be noted that `OceanBase` currently only supports post-filtering (i.e., filtering based on metadata after performing an approximate nearest neighbor search).
38
+
39
+ ## Install dependencies
40
+
41
+ We use `pyobvector` to integrate OceanBase vector store into LlamaIndex.
42
+ So it is necessary to install it with `pip install pyobvector` before starting.
43
+
44
+ ## Setup OceanBase
45
+
46
+ We recommend using Docker to deploy OceanBase:
47
+
48
+ ```shell
49
+ docker run --name=ob433 -e MODE=slim -p 2881:2881 -d oceanbase/oceanbase-ce:4.3.3.0-100000142024101215
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ```sh
55
+ %pip install llama-index-vector-stores-oceanbase
56
+ %pip install llama-index
57
+ # choose dashscope as embedding and llm model, your can also use default openai or other model to test
58
+ %pip install llama-index-embeddings-dashscope
59
+ %pip install llama-index-llms-dashscope
60
+ ```
61
+
62
+ ```python
63
+ from llama_index.vector_stores.oceanbase import OceanBaseVectorStore
64
+ from pyobvector import ObVecClient
65
+
66
+ client = ObVecClient()
67
+
68
+ client.perform_raw_text_sql(
69
+ "ALTER SYSTEM ob_vector_memory_limit_percentage = 30"
70
+ )
71
+
72
+ # Initialize OceanBaseVectorStore
73
+ oceanbase = OceanBaseVectorStore(
74
+ client=client,
75
+ dim=1536,
76
+ drop_old=True,
77
+ normalize=True,
78
+ )
79
+ ```
80
+
@@ -0,0 +1,61 @@
1
+ # LlamaIndex Vector-Stores Integration: Oceanbase
2
+
3
+ [OceanBase Database](https://github.com/oceanbase/oceanbase) is a distributed relational database.
4
+ It is developed entirely by Ant Group. The OceanBase Database is built on a common server cluster.
5
+ Based on the Paxos protocol and its distributed structure, the OceanBase Database provides high availability and linear scalability.
6
+
7
+ OceanBase currently has the ability to store vectors. Users can easily perform the following operations with SQL:
8
+
9
+ - Create a table containing vector type fields;
10
+ - Create a vector index table based on the HNSW algorithm;
11
+ - Perform vector approximate nearest neighbor queries;
12
+ - ...
13
+
14
+ ## Limitations
15
+
16
+ The vector storage capability of OceanBase is still being enhanced, and currently has the following limitations:
17
+
18
+ - Cosine vector distance function is not yet supported for vector index. Currently only inner product distance and Euclidean distance are supported, and Euclidean distance is used by default.
19
+ - It should be noted that `OceanBase` currently only supports post-filtering (i.e., filtering based on metadata after performing an approximate nearest neighbor search).
20
+
21
+ ## Install dependencies
22
+
23
+ We use `pyobvector` to integrate OceanBase vector store into LlamaIndex.
24
+ So it is necessary to install it with `pip install pyobvector` before starting.
25
+
26
+ ## Setup OceanBase
27
+
28
+ We recommend using Docker to deploy OceanBase:
29
+
30
+ ```shell
31
+ docker run --name=ob433 -e MODE=slim -p 2881:2881 -d oceanbase/oceanbase-ce:4.3.3.0-100000142024101215
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ```sh
37
+ %pip install llama-index-vector-stores-oceanbase
38
+ %pip install llama-index
39
+ # choose dashscope as embedding and llm model, your can also use default openai or other model to test
40
+ %pip install llama-index-embeddings-dashscope
41
+ %pip install llama-index-llms-dashscope
42
+ ```
43
+
44
+ ```python
45
+ from llama_index.vector_stores.oceanbase import OceanBaseVectorStore
46
+ from pyobvector import ObVecClient
47
+
48
+ client = ObVecClient()
49
+
50
+ client.perform_raw_text_sql(
51
+ "ALTER SYSTEM ob_vector_memory_limit_percentage = 30"
52
+ )
53
+
54
+ # Initialize OceanBaseVectorStore
55
+ oceanbase = OceanBaseVectorStore(
56
+ client=client,
57
+ dim=1536,
58
+ drop_old=True,
59
+ normalize=True,
60
+ )
61
+ ```
@@ -0,0 +1,4 @@
1
+ resource(
2
+ name="py_typed",
3
+ source="py.typed",
4
+ )
@@ -0,0 +1,3 @@
1
+ from llama_index.vector_stores.oceanbase.base import OceanBaseVectorStore
2
+
3
+ __all__ = ["OceanBaseVectorStore"]
@@ -0,0 +1,529 @@
1
+ """OceanBase Vector Store."""
2
+
3
+ import math
4
+ import logging
5
+ import json
6
+ from typing import Any, Optional, List
7
+
8
+ from llama_index.core.bridge.pydantic import PrivateAttr
9
+ from llama_index.core.utils import iter_batch
10
+ from llama_index.core.schema import BaseNode, MetadataMode
11
+ from llama_index.core.vector_stores.types import (
12
+ BasePydanticVectorStore,
13
+ MetadataFilters,
14
+ FilterOperator,
15
+ VectorStoreQuery,
16
+ VectorStoreQueryResult,
17
+ )
18
+ from llama_index.core.vector_stores.utils import (
19
+ metadata_dict_to_node,
20
+ node_to_metadata_dict,
21
+ )
22
+ from sqlalchemy import Column, Table, String, JSON, text, func
23
+ from sqlalchemy.dialects.mysql import LONGTEXT
24
+ from pyobvector import ObVecClient, VECTOR
25
+ import numpy as np
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ DEFAULT_OCEANBASE_BATCH_SIZE = 100
30
+ DEFAULT_OCEANBASE_VECTOR_TABLE_NAME = "llama_vector"
31
+ DEFAULT_OCEANBASE_HNSW_BUILD_PARAM = {"M": 16, "efConstruction": 256}
32
+ DEFAULT_OCEANBASE_HNSW_SEARCH_PARAM = {"efSearch": 64}
33
+ OCEANBASE_SUPPORTED_VECTOR_INDEX_TYPE = "HNSW"
34
+ DEFAULT_OCEANBASE_VECTOR_METRIC_TYPE = "l2"
35
+
36
+ DEFAULT_OCEANBASE_PFIELD = "id"
37
+ DEFAULT_OCEANBASE_DOCID_FIELD = "doc_id"
38
+ DEFAULT_OCEANBASE_VEC_FIELD = "embedding"
39
+ DEFAULT_OCEANBASE_DOC_FIELD = "document"
40
+ DEFAULT_OCEANBASE_METADATA_FIELD = "metadata"
41
+
42
+ DEFAULT_OCEANBASE_VEC_INDEX_NAME = "vidx"
43
+
44
+
45
+ def _parse_filter_value(filter_value: any, is_text_match: bool = False):
46
+ if filter_value is None:
47
+ return filter_value
48
+
49
+ if is_text_match:
50
+ return f"'{filter_value!s}%'"
51
+
52
+ if isinstance(filter_value, str):
53
+ return f"'{filter_value!s}'"
54
+
55
+ if isinstance(filter_value, list):
56
+ if all(isinstance(item, str) for item in filter_value):
57
+ return "(" + ",".join([f"'{v!s}'" for v in filter_value]) + ")"
58
+ return "(" + ",".join([str(v) for v in filter_value]) + ")"
59
+
60
+ return str(filter_value)
61
+
62
+
63
+ def _euclidean_similarity(distance: float) -> float:
64
+ return 1.0 - distance / math.sqrt(2)
65
+
66
+
67
+ def _neg_inner_product_similarity(distance: float) -> float:
68
+ return -distance
69
+
70
+
71
+ def _normalize(vector: List[float]) -> List[float]:
72
+ arr = np.array(vector)
73
+ norm = np.linalg.norm(arr)
74
+ arr = arr / norm
75
+ return arr.tolist()
76
+
77
+
78
+ class OceanBaseVectorStore(BasePydanticVectorStore):
79
+ """OceanBase Vector Store.
80
+
81
+ You need to install `pyobvector` and run a standalone observer or OceanBase cluster.
82
+
83
+ See the following documentation for how to deploy OceanBase:
84
+ https://github.com/oceanbase/oceanbase-doc/blob/V4.3.1/en-US/400.deploy/500.deploy-oceanbase-database-community-edition/100.deployment-overview.md
85
+
86
+ IF USING L2/INNER_PRODUCT metric, IT IS HIGHLY SUGGESTED TO set `normalize = True`.
87
+
88
+ Args:
89
+ _client (ObVecClient): OceanBase vector store client.
90
+ Refer to `pyobvector` for more information.
91
+ dim (int): Dimension of embedding vector.
92
+ table_name (str): Which table name to use. Defaults to "llama_vector".
93
+ vidx_metric_type (str): Metric method of distance between vectors.
94
+ This parameter takes values in `l2` and `inner_product`. Defaults to `l2`.
95
+ vidx_algo_params (Optional[dict]): Which index params to use. Now OceanBase
96
+ supports HNSW only. Refer to `DEFAULT_OCEANBASE_HNSW_BUILD_PARAM`
97
+ for example.
98
+ drop_old (bool): Whether to drop the current table. Defaults
99
+ to False.
100
+ primary_field (str): Name of the primary key column. Defaults to "id".
101
+ doc_id_field (str): Name of the doc id column. Defaults to "doc_id".
102
+ vector_field (str): Name of the vector column. Defaults to "embedding".
103
+ text_field (str): Name of the text column. Defaults to "document".
104
+ metadata_field (Optional[str]): Name of the metadata column.
105
+ Defaults to "metadata". When `metadata_field` is specified,
106
+ the document's metadata will store as json.
107
+ vidx_name (str): Name of the vector index table.
108
+ partitions (ObPartition): Partition strategy of table. Refer to `pyobvector`'s
109
+ documentation for more examples.
110
+ extra_columns (Optional[List[Column]]): Extra sqlalchemy columns
111
+ to add to the table.
112
+ normalize (bool): normalize vector or not.
113
+
114
+ Examples:
115
+ `pip install llama-index-vector-stores-oceanbase`
116
+
117
+ ```python
118
+ from llama_index.vector_stores.oceanbase import OceanBaseVectorStore
119
+
120
+ # Setup ObVecClient
121
+ from pyobvector import ObVecClient
122
+
123
+ client = ObVecClient(
124
+ uri=os.getenv("OB_URI", "127.0.0.1:2881"),
125
+ user=os.getenv("OB_USER", "root@test"),
126
+ password=os.getenv("OB_PWD", ""),
127
+ db_name=os.getenv("OB_DBNAME", "test"),
128
+ )
129
+
130
+ # Initialize OceanBaseVectorStore
131
+ oceanbase = OceanBaseVectorStore(
132
+ client=client,
133
+ dim=1024,
134
+ )
135
+ ```
136
+ """
137
+
138
+ stores_text: bool = True
139
+
140
+ _client: ObVecClient = PrivateAttr()
141
+ _dim: int = PrivateAttr()
142
+ _table_name: str = PrivateAttr()
143
+ _vidx_metric_type: str = PrivateAttr()
144
+ _vidx_algo_params: dict = PrivateAttr()
145
+ _primary_field: str = PrivateAttr()
146
+ _doc_id_field: str = PrivateAttr()
147
+ _vector_field: str = PrivateAttr()
148
+ _text_field: str = PrivateAttr()
149
+ _metadata_field: str = PrivateAttr()
150
+ _vidx_name: str = PrivateAttr()
151
+ _partitions: Optional[Any] = PrivateAttr()
152
+ _extra_columns: Optional[List[Column]] = PrivateAttr()
153
+ _hnsw_ef_search: int = PrivateAttr()
154
+ _normalize: bool = PrivateAttr()
155
+
156
+ def __init__(
157
+ self,
158
+ client: ObVecClient,
159
+ dim: int,
160
+ table_name: str = DEFAULT_OCEANBASE_VECTOR_TABLE_NAME,
161
+ vidx_metric_type: str = DEFAULT_OCEANBASE_VECTOR_METRIC_TYPE,
162
+ vidx_algo_params: Optional[dict] = None,
163
+ drop_old: bool = False,
164
+ *,
165
+ primary_field: str = DEFAULT_OCEANBASE_PFIELD,
166
+ doc_id_field: str = DEFAULT_OCEANBASE_DOCID_FIELD,
167
+ vector_field: str = DEFAULT_OCEANBASE_VEC_FIELD,
168
+ text_field: str = DEFAULT_OCEANBASE_DOC_FIELD,
169
+ metadata_field: str = DEFAULT_OCEANBASE_METADATA_FIELD,
170
+ vidx_name: str = DEFAULT_OCEANBASE_VEC_INDEX_NAME,
171
+ partitions: Optional[Any] = None,
172
+ extra_columns: Optional[List[Column]] = None,
173
+ normalize: bool = False,
174
+ **kwargs,
175
+ ):
176
+ super().__init__()
177
+
178
+ try:
179
+ from pyobvector import ObVecClient
180
+ except ImportError:
181
+ raise ImportError(
182
+ "Could not import pyobvector package. "
183
+ "Please install it with `pip install pyobvector`."
184
+ )
185
+
186
+ if client is not None:
187
+ if not isinstance(client, ObVecClient):
188
+ raise ValueError("client must be of type pyobvector.ObVecClient")
189
+ else:
190
+ raise ValueError("client not specified")
191
+
192
+ self._dim = dim
193
+ self._client: ObVecClient = client
194
+ self._table_name = table_name
195
+ self._extra_columns = extra_columns
196
+ self._vidx_metric_type = vidx_metric_type.lower()
197
+ if self._vidx_metric_type not in ("l2", "inner_product"):
198
+ raise ValueError(
199
+ "`vidx_metric_type` should be set in `l2`/`inner_product`."
200
+ )
201
+ self._vidx_algo_params = vidx_algo_params or DEFAULT_OCEANBASE_HNSW_BUILD_PARAM
202
+
203
+ self._primary_field = primary_field
204
+ self._doc_id_field = doc_id_field
205
+ self._vector_field = vector_field
206
+ self._text_field = text_field
207
+ self._metadata_field = metadata_field
208
+ self._vidx_name = vidx_name
209
+ self._partition = partitions
210
+ self._hnsw_ef_search = -1
211
+ self._normalize = normalize
212
+
213
+ if drop_old:
214
+ self._client.drop_table_if_exist(table_name=self._table_name)
215
+
216
+ self._create_table_with_index()
217
+
218
+ def _enhance_filter_key(self, filter_key: str) -> str:
219
+ return f"{self._metadata_field}->'$.{filter_key}'"
220
+
221
+ def _to_oceanbase_filter(
222
+ self, metadata_filters: Optional[MetadataFilters] = None
223
+ ) -> str:
224
+ filters = []
225
+ for filter in metadata_filters.filters:
226
+ if isinstance(filter, MetadataFilters):
227
+ filters.append(f"({self._to_oceanbase_filter(filter)})")
228
+ continue
229
+
230
+ filter_value = _parse_filter_value(filter.value)
231
+ if filter_value is None and filter.operator != FilterOperator.IS_EMPTY:
232
+ continue
233
+
234
+ if filter.operator == FilterOperator.EQ:
235
+ filters.append(f"{self._enhance_filter_key(filter.key)}={filter_value}")
236
+ elif filter.operator == FilterOperator.GT:
237
+ filters.append(f"{self._enhance_filter_key(filter.key)}>{filter_value}")
238
+ elif filter.operator == FilterOperator.LT:
239
+ filters.append(f"{self._enhance_filter_key(filter.key)}<{filter_value}")
240
+ elif filter.operator == FilterOperator.NE:
241
+ filters.append(
242
+ f"{self._enhance_filter_key(filter.key)}!={filter_value}"
243
+ )
244
+ elif filter.operator == FilterOperator.GTE:
245
+ filters.append(
246
+ f"{self._enhance_filter_key(filter.key)}>={filter_value}"
247
+ )
248
+ elif filter.operator == FilterOperator.LTE:
249
+ filters.append(
250
+ f"{self._enhance_filter_key(filter.key)}<={filter_value}"
251
+ )
252
+ elif filter.operator == FilterOperator.IN:
253
+ filters.append(
254
+ f"{self._enhance_filter_key(filter.key)} in {filter_value}"
255
+ )
256
+ elif filter.operator == FilterOperator.NIN:
257
+ filters.append(
258
+ f"{self._enhance_filter_key(filter.key)} not in {filter_value}"
259
+ )
260
+ elif filter.operator == FilterOperator.TEXT_MATCH:
261
+ filters.append(
262
+ f"{self._enhance_filter_key(filter.key)} like {_parse_filter_value(filter.value, True)}"
263
+ )
264
+ elif filter.operator == FilterOperator.IS_EMPTY:
265
+ filters.append(f"{self._enhance_filter_key(filter.key)} IS NULL")
266
+ else:
267
+ raise ValueError(
268
+ f'Operator {filter.operator} ("{filter.operator.value}") is not supported by OceanBase.'
269
+ )
270
+ return f" {metadata_filters.condition.value} ".join(filters)
271
+
272
+ def _parse_metric_type_str_to_dist_func(self) -> Any:
273
+ if self._vidx_metric_type == "l2":
274
+ return func.l2_distance
275
+ if self._vidx_metric_type == "cosine":
276
+ return func.cosine_distance
277
+ if self._vidx_metric_type == "inner_product":
278
+ return func.negative_inner_product
279
+ raise ValueError(f"Invalid vector index metric type: {self._vidx_metric_type}")
280
+
281
+ def _load_table(self) -> None:
282
+ table = Table(
283
+ self._table_name,
284
+ self._client.metadata_obj,
285
+ autoload_with=self._client.engine,
286
+ )
287
+ column_names = [column.name for column in table.columns]
288
+ optional_len = len(self._extra_columns or [])
289
+ assert len(column_names) == (5 + optional_len)
290
+
291
+ logging.info(f"load exist table with {column_names} columns")
292
+ self._primary_field = column_names[0]
293
+ self._doc_id_field = column_names[1]
294
+ self._vector_field = column_names[2]
295
+ self._text_field = column_names[3]
296
+ self._metadata_field = column_names[4]
297
+
298
+ def _create_table_with_index(self):
299
+ if self._client.check_table_exists(self._table_name):
300
+ self._load_table()
301
+ return
302
+
303
+ cols = [
304
+ Column(
305
+ self._primary_field, String(4096), primary_key=True, autoincrement=False
306
+ ),
307
+ Column(self._doc_id_field, String(4096)),
308
+ Column(self._vector_field, VECTOR(self._dim)),
309
+ Column(self._text_field, LONGTEXT),
310
+ Column(self._metadata_field, JSON),
311
+ ]
312
+ if self._extra_columns is not None:
313
+ cols.extend(self._extra_columns)
314
+
315
+ vidx_params = self._client.prepare_index_params()
316
+ vidx_params.add_index(
317
+ field_name=self._vector_field,
318
+ index_type=OCEANBASE_SUPPORTED_VECTOR_INDEX_TYPE,
319
+ index_name=self._vidx_name,
320
+ metric_type=self._vidx_metric_type,
321
+ params=self._vidx_algo_params,
322
+ )
323
+
324
+ self._client.create_table_with_index_params(
325
+ table_name=self._table_name,
326
+ columns=cols,
327
+ indexes=None,
328
+ vidxs=vidx_params,
329
+ partitions=self._partition,
330
+ )
331
+
332
+ @classmethod
333
+ def class_name(cls) -> str:
334
+ return "OceanBaseVectorStore"
335
+
336
+ @property
337
+ def client(self) -> Any:
338
+ """Get client."""
339
+ return self._client
340
+
341
+ def get_nodes(
342
+ self,
343
+ node_ids: Optional[List[str]] = None,
344
+ filters: Optional[MetadataFilters] = None,
345
+ ) -> List[BaseNode]:
346
+ """Get nodes from OceanBase.
347
+
348
+ Args:
349
+ node_ids (Optional[List[str]], optional): IDs of nodes to delete.
350
+ Defaults to None.
351
+ filters (Optional[MetadataFilters], optional): Metadata filters.
352
+ Defaults to None.
353
+
354
+ Returns:
355
+ List[BaseNode]: List of text nodes.
356
+ """
357
+ if filters is not None:
358
+ filter = self._to_oceanbase_filter(filters)
359
+ else:
360
+ filter = None
361
+
362
+ res = self._client.get(
363
+ table_name=self._table_name,
364
+ ids=node_ids,
365
+ where_clause=[text(filter)] if filter is not None else None,
366
+ output_column_name=[
367
+ self._text_field,
368
+ self._metadata_field,
369
+ ],
370
+ )
371
+
372
+ return [
373
+ metadata_dict_to_node(
374
+ metadata=(json.loads(r[1]) if not isinstance(r[1], dict) else r[1]),
375
+ text=r[0],
376
+ )
377
+ for r in res.fetchall()
378
+ ]
379
+
380
+ def add(
381
+ self,
382
+ nodes: List[BaseNode],
383
+ batch_size: Optional[int] = None,
384
+ extras: Optional[List[dict]] = None,
385
+ ) -> List[str]:
386
+ """Add nodes into OceanBase.
387
+
388
+ Args:
389
+ nodes (List[BaseNode]): List of nodes with embeddings
390
+ to insert.
391
+ batch_size (Optional[int]): Insert nodes in batch.
392
+ extras (Optional[List[dict]]): If `extra_columns` is set
393
+ when initializing `OceanBaseVectorStore`, you can add
394
+ nodes with extra infos.
395
+
396
+ Returns:
397
+ List[str]: List of ids inserted.
398
+ """
399
+ batch_size = batch_size or DEFAULT_OCEANBASE_BATCH_SIZE
400
+
401
+ extra_data = extras or [{} for _ in nodes]
402
+ if len(nodes) != len(extra_data):
403
+ raise ValueError("nodes size & extras size mismatch")
404
+
405
+ data = [
406
+ {
407
+ self._primary_field: node.id_,
408
+ self._doc_id_field: node.ref_doc_id or None,
409
+ self._vector_field: (
410
+ node.get_embedding()
411
+ if not self._normalize
412
+ else _normalize(node.get_embedding())
413
+ ),
414
+ self._text_field: node.get_content(metadata_mode=MetadataMode.NONE),
415
+ self._metadata_field: node_to_metadata_dict(node, remove_text=True),
416
+ **extra,
417
+ }
418
+ for node, extra in zip(nodes, extra_data)
419
+ ]
420
+ for data_batch in iter_batch(data, batch_size):
421
+ self._client.insert(self._table_name, data_batch)
422
+ return [node.id_ for node in nodes]
423
+
424
+ def delete(self, ref_doc_id: str, **delete_kwargs: Any) -> None:
425
+ """Delete nodes using with ref_doc_id.
426
+
427
+ Args:
428
+ ref_doc_id (str): The doc_id of the document to delete.
429
+ """
430
+ self._client.delete(
431
+ table_name=self._table_name,
432
+ where_clause=[text(f"{self._doc_id_field}='{ref_doc_id}'")],
433
+ )
434
+
435
+ def delete_nodes(
436
+ self,
437
+ node_ids: Optional[List[str]] = None,
438
+ filters: Optional[MetadataFilters] = None,
439
+ **delete_kwargs: Any,
440
+ ) -> None:
441
+ """Deletes nodes.
442
+
443
+ Args:
444
+ node_ids (Optional[List[str]], optional): IDs of nodes to delete.
445
+ Defaults to None.
446
+ filters (Optional[MetadataFilters], optional): Metadata filters.
447
+ Defaults to None.
448
+ """
449
+ if filters is not None:
450
+ filter = self._to_oceanbase_filter(filters)
451
+ else:
452
+ filter = None
453
+
454
+ self._client.delete(
455
+ table_name=self._table_name,
456
+ ids=node_ids,
457
+ where_clause=[text(filter)] if filter is not None else None,
458
+ )
459
+
460
+ def clear(self) -> None:
461
+ """Clears table."""
462
+ self._client.perform_raw_text_sql(f"TRUNCATE TABLE {self._table_name}")
463
+
464
+ def _parse_distance_to_similarities(self, distance: float) -> float:
465
+ if self._vidx_metric_type == "l2":
466
+ return _euclidean_similarity(distance)
467
+ elif self._vidx_metric_type == "inner_product":
468
+ return _neg_inner_product_similarity(distance)
469
+ raise ValueError(f"Metric Type {self._vidx_metric_type} is not supported")
470
+
471
+ def query(
472
+ self, query: VectorStoreQuery, param: Optional[dict] = None, **kwargs: Any
473
+ ) -> VectorStoreQueryResult:
474
+ """Perform top-k ANN search.
475
+
476
+ Args:
477
+ query (VectorStoreQuery): query infos
478
+ param (Optional[dict]): The search params for the index type.
479
+ Defaults to None. Refer to `DEFAULT_OCEANBASE_HNSW_SEARCH_PARAM`
480
+ for example.
481
+ """
482
+ search_param = (
483
+ param if param is not None else DEFAULT_OCEANBASE_HNSW_SEARCH_PARAM
484
+ )
485
+ ef_search = search_param.get(
486
+ "efSearch", DEFAULT_OCEANBASE_HNSW_SEARCH_PARAM["efSearch"]
487
+ )
488
+ if ef_search != self._hnsw_ef_search:
489
+ self._client.set_ob_hnsw_ef_search(ef_search)
490
+ self._hnsw_ef_search = ef_search
491
+
492
+ if query.filters:
493
+ qfilters = self._to_oceanbase_filter(query.filters)
494
+ else:
495
+ qfilters = None
496
+
497
+ res = self._client.ann_search(
498
+ table_name=self._table_name,
499
+ vec_data=(
500
+ query.query_embedding
501
+ if not self._normalize
502
+ else _normalize(query.query_embedding)
503
+ ),
504
+ vec_column_name=self._vector_field,
505
+ distance_func=self._parse_metric_type_str_to_dist_func(),
506
+ with_dist=True,
507
+ output_column_names=[
508
+ self._primary_field,
509
+ self._text_field,
510
+ self._metadata_field,
511
+ ],
512
+ topk=query.similarity_top_k,
513
+ where_clause=([text(qfilters)] if qfilters else None),
514
+ )
515
+
516
+ records = []
517
+ for r in res.fetchall():
518
+ records.append(r)
519
+ return VectorStoreQueryResult(
520
+ nodes=[
521
+ metadata_dict_to_node(
522
+ metadata=json.loads(r[2]),
523
+ text=r[1],
524
+ )
525
+ for r in records
526
+ ],
527
+ similarities=[self._parse_distance_to_similarities(r[3]) for r in records],
528
+ ids=[r[0] for r in records],
529
+ )
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ build-backend = "poetry.core.masonry.api"
3
+ requires = ["poetry-core"]
4
+
5
+ [tool.codespell]
6
+ check-filenames = true
7
+ check-hidden = true
8
+ # Feel free to un-skip examples, and experimental, you will just need to
9
+ # work through many typos (--write-changes and --interactive will help)
10
+ skip = "*.csv,*.html,*.json,*.jsonl,*.pdf,*.txt,*.ipynb"
11
+
12
+ [tool.llamahub]
13
+ contains_example = false
14
+ import_path = "llama_index.vector_stores.oceanbase"
15
+
16
+ [tool.llamahub.class_authors]
17
+ OceanBaseVectorStore = "GITHUBear"
18
+
19
+ [tool.mypy]
20
+ disallow_untyped_defs = true
21
+ # Remove venv skip when integrated with pre-commit
22
+ exclude = ["_static", "build", "examples", "notebooks", "venv"]
23
+ ignore_missing_imports = true
24
+ python_version = "3.8"
25
+
26
+ [tool.poetry]
27
+ authors = ["GITHUBear <shanhaikang.shk@oceanbase.com>"]
28
+ description = "llama-index vector-stores oceanbase integration"
29
+ license = "MIT"
30
+ name = "llama-index-vector-stores-oceanbase"
31
+ packages = [{include = "llama_index/"}]
32
+ readme = "README.md"
33
+ version = "0.1.0"
34
+
35
+ [tool.poetry.dependencies]
36
+ python = ">=3.9,<4.0"
37
+ llama-index-core = "^0.11.0"
38
+ pyobvector = "^0.1.6"
39
+
40
+ [tool.poetry.group.dev.dependencies]
41
+ black = {extras = ["jupyter"], version = "<=23.9.1,>=23.7.0"}
42
+ codespell = {extras = ["toml"], version = ">=v2.2.6"}
43
+ ipython = "8.10.0"
44
+ jupyter = "^1.0.0"
45
+ mypy = "0.991"
46
+ pre-commit = "3.2.0"
47
+ pylint = "2.15.10"
48
+ pytest = "7.2.1"
49
+ pytest-mock = "3.11.1"
50
+ ruff = "0.0.292"
51
+ tree-sitter-languages = "^1.8.0"
52
+ types-Deprecated = ">=0.1.0"
53
+ types-PyYAML = "^6.0.12.12"
54
+ types-protobuf = "^4.24.0.4"
55
+ types-redis = "4.5.5.0"
56
+ types-requests = "2.28.11.8" # TODO: unpin when mypy>0.991
57
+ types-setuptools = "67.1.0.0"