vectordb-bench 1.0.4__py3-none-any.whl → 1.0.7__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.
Files changed (77) hide show
  1. vectordb_bench/__init__.py +1 -0
  2. vectordb_bench/backend/cases.py +45 -1
  3. vectordb_bench/backend/clients/__init__.py +47 -0
  4. vectordb_bench/backend/clients/api.py +2 -0
  5. vectordb_bench/backend/clients/aws_opensearch/aws_opensearch.py +104 -40
  6. vectordb_bench/backend/clients/aws_opensearch/cli.py +52 -15
  7. vectordb_bench/backend/clients/aws_opensearch/config.py +27 -7
  8. vectordb_bench/backend/clients/hologres/cli.py +50 -0
  9. vectordb_bench/backend/clients/hologres/config.py +121 -0
  10. vectordb_bench/backend/clients/hologres/hologres.py +365 -0
  11. vectordb_bench/backend/clients/lancedb/lancedb.py +1 -0
  12. vectordb_bench/backend/clients/milvus/cli.py +29 -9
  13. vectordb_bench/backend/clients/milvus/config.py +2 -0
  14. vectordb_bench/backend/clients/milvus/milvus.py +1 -1
  15. vectordb_bench/backend/clients/oceanbase/cli.py +1 -0
  16. vectordb_bench/backend/clients/oceanbase/config.py +3 -1
  17. vectordb_bench/backend/clients/oceanbase/oceanbase.py +20 -4
  18. vectordb_bench/backend/clients/oss_opensearch/cli.py +155 -0
  19. vectordb_bench/backend/clients/oss_opensearch/config.py +157 -0
  20. vectordb_bench/backend/clients/oss_opensearch/oss_opensearch.py +582 -0
  21. vectordb_bench/backend/clients/oss_opensearch/run.py +166 -0
  22. vectordb_bench/backend/clients/pgdiskann/cli.py +45 -0
  23. vectordb_bench/backend/clients/pgdiskann/config.py +16 -0
  24. vectordb_bench/backend/clients/pgdiskann/pgdiskann.py +94 -26
  25. vectordb_bench/backend/clients/s3_vectors/config.py +41 -0
  26. vectordb_bench/backend/clients/s3_vectors/s3_vectors.py +171 -0
  27. vectordb_bench/backend/clients/tidb/cli.py +0 -4
  28. vectordb_bench/backend/clients/tidb/config.py +22 -2
  29. vectordb_bench/backend/clients/zilliz_cloud/cli.py +14 -1
  30. vectordb_bench/backend/clients/zilliz_cloud/config.py +4 -1
  31. vectordb_bench/backend/dataset.py +70 -0
  32. vectordb_bench/backend/filter.py +17 -0
  33. vectordb_bench/backend/runner/mp_runner.py +4 -0
  34. vectordb_bench/backend/runner/rate_runner.py +23 -11
  35. vectordb_bench/backend/runner/read_write_runner.py +10 -9
  36. vectordb_bench/backend/runner/serial_runner.py +23 -7
  37. vectordb_bench/backend/task_runner.py +5 -4
  38. vectordb_bench/cli/cli.py +36 -0
  39. vectordb_bench/cli/vectordbbench.py +4 -0
  40. vectordb_bench/fig/custom_case_run_test.png +0 -0
  41. vectordb_bench/fig/custom_dataset.png +0 -0
  42. vectordb_bench/fig/homepage/bar-chart.png +0 -0
  43. vectordb_bench/fig/homepage/concurrent.png +0 -0
  44. vectordb_bench/fig/homepage/custom.png +0 -0
  45. vectordb_bench/fig/homepage/label_filter.png +0 -0
  46. vectordb_bench/fig/homepage/qp$.png +0 -0
  47. vectordb_bench/fig/homepage/run_test.png +0 -0
  48. vectordb_bench/fig/homepage/streaming.png +0 -0
  49. vectordb_bench/fig/homepage/table.png +0 -0
  50. vectordb_bench/fig/run_test_select_case.png +0 -0
  51. vectordb_bench/fig/run_test_select_db.png +0 -0
  52. vectordb_bench/fig/run_test_submit.png +0 -0
  53. vectordb_bench/frontend/components/check_results/filters.py +1 -4
  54. vectordb_bench/frontend/components/check_results/nav.py +2 -1
  55. vectordb_bench/frontend/components/concurrent/charts.py +5 -0
  56. vectordb_bench/frontend/components/int_filter/charts.py +60 -0
  57. vectordb_bench/frontend/components/streaming/data.py +7 -0
  58. vectordb_bench/frontend/components/welcome/welcomePrams.py +42 -4
  59. vectordb_bench/frontend/config/dbCaseConfigs.py +142 -16
  60. vectordb_bench/frontend/config/styles.py +4 -0
  61. vectordb_bench/frontend/pages/concurrent.py +1 -1
  62. vectordb_bench/frontend/pages/custom.py +1 -1
  63. vectordb_bench/frontend/pages/int_filter.py +56 -0
  64. vectordb_bench/frontend/pages/streaming.py +16 -3
  65. vectordb_bench/interface.py +5 -1
  66. vectordb_bench/metric.py +7 -0
  67. vectordb_bench/models.py +39 -4
  68. vectordb_bench/results/S3Vectors/result_20250722_standard_s3vectors.json +2509 -0
  69. vectordb_bench/results/getLeaderboardDataV2.py +23 -2
  70. vectordb_bench/results/leaderboard_v2.json +200 -0
  71. vectordb_bench/results/leaderboard_v2_streaming.json +128 -0
  72. {vectordb_bench-1.0.4.dist-info → vectordb_bench-1.0.7.dist-info}/METADATA +40 -8
  73. {vectordb_bench-1.0.4.dist-info → vectordb_bench-1.0.7.dist-info}/RECORD +77 -51
  74. {vectordb_bench-1.0.4.dist-info → vectordb_bench-1.0.7.dist-info}/WHEEL +0 -0
  75. {vectordb_bench-1.0.4.dist-info → vectordb_bench-1.0.7.dist-info}/entry_points.txt +0 -0
  76. {vectordb_bench-1.0.4.dist-info → vectordb_bench-1.0.7.dist-info}/licenses/LICENSE +0 -0
  77. {vectordb_bench-1.0.4.dist-info → vectordb_bench-1.0.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,121 @@
1
+ from pydantic import BaseModel, SecretStr
2
+
3
+ from ..api import DBCaseConfig, DBConfig, IndexType, MetricType
4
+
5
+
6
+ class HologresConfig(DBConfig):
7
+ user_name: SecretStr = SecretStr("hologres")
8
+ password: SecretStr
9
+ host: str = "localhost"
10
+ port: int = 5432
11
+ db_name: str
12
+
13
+ def to_dict(self) -> dict:
14
+ user_str = self.user_name.get_secret_value()
15
+ pwd_str = self.password.get_secret_value()
16
+ return {
17
+ "host": self.host,
18
+ "port": self.port,
19
+ "dbname": self.db_name,
20
+ "user": user_str,
21
+ "password": pwd_str,
22
+ }
23
+
24
+
25
+ class HologresIndexConfig(BaseModel, DBCaseConfig):
26
+ index: IndexType = IndexType.Hologres_HGraph
27
+ metric_type: MetricType | None = None
28
+
29
+ create_index_before_load: bool = False
30
+ create_index_after_load: bool = True
31
+
32
+ min_flush_proxima_row_count: int = 1000
33
+ min_compaction_proxima_row_count: int = 1000
34
+ max_total_size_to_merge_mb: int = 4096
35
+ full_compact_max_file_size_mb: int = 4096
36
+
37
+ base_quantization_type: str = "sq8_uniform"
38
+ precise_quantization_type: str = "fp32"
39
+ use_reorder: bool = True
40
+ build_thread_count: int = 16
41
+ max_degree: int = 64
42
+ ef_construction: int = 400
43
+
44
+ ef_search: int = 51
45
+
46
+ def index_param(self) -> dict:
47
+ return {
48
+ "algorithm": self.algorithm(),
49
+ "distance_method": self.distance_method(),
50
+ "builder_params": self.builder_params(),
51
+ "full_compact_max_file_size_mb": self.full_compact_max_file_size_mb,
52
+ }
53
+
54
+ def search_param(self) -> dict:
55
+ return {
56
+ "distance_function": self.distance_function(),
57
+ "order_direction": self.order_direction(),
58
+ "searcher_params": self.search_params(),
59
+ }
60
+
61
+ def algorithm(self) -> str:
62
+ return self.index.value
63
+
64
+ def is_proxima(self) -> bool:
65
+ return self.index == IndexType.Hologres_Graph
66
+
67
+ def distance_method(self) -> str:
68
+ if self.metric_type == MetricType.L2:
69
+ if self.index == IndexType.Hologres_Graph:
70
+ return "SquaredEuclidean"
71
+ return "Euclidean"
72
+ if self.metric_type == MetricType.IP:
73
+ return "InnerProduct"
74
+ if self.metric_type == MetricType.COSINE:
75
+ if self.index == IndexType.Hologres_Graph:
76
+ return "InnerProduct"
77
+ return "Cosine"
78
+ return "Euclidean"
79
+
80
+ def distance_function(self) -> str:
81
+ if self.metric_type == MetricType.L2:
82
+ if self.index == IndexType.Hologres_Graph:
83
+ return "approx_squared_euclidean_distance"
84
+ return "approx_euclidean_distance"
85
+ if self.metric_type == MetricType.IP:
86
+ return "approx_inner_product_distance"
87
+ if self.metric_type == MetricType.COSINE:
88
+ if self.index == IndexType.Hologres_Graph:
89
+ return "approx_inner_product_distance"
90
+ return "approx_cosine_distance"
91
+ return "approx_euclidean_distance"
92
+
93
+ def order_direction(self) -> str:
94
+ if self.metric_type == MetricType.L2:
95
+ return "ASC"
96
+ if self.metric_type in {MetricType.IP, MetricType.COSINE}:
97
+ return "DESC"
98
+ return "ASC"
99
+
100
+ def builder_params(self) -> dict:
101
+ if self.use_reorder:
102
+ self.base_quantization_type = "sq8_uniform"
103
+ else:
104
+ self.base_quantization_type = "fp32"
105
+
106
+ return {
107
+ "min_flush_proxima_row_count": self.min_flush_proxima_row_count,
108
+ "min_compaction_proxima_row_count": self.min_compaction_proxima_row_count,
109
+ "max_total_size_to_merge_mb": self.max_total_size_to_merge_mb,
110
+ "build_thread_count": self.build_thread_count,
111
+ "base_quantization_type": self.base_quantization_type,
112
+ "max_degree": self.max_degree,
113
+ "ef_construction": self.ef_construction,
114
+ "precise_quantization_type": self.precise_quantization_type,
115
+ "use_reorder": self.use_reorder,
116
+ }
117
+
118
+ def searcher_params(self) -> dict:
119
+ return {
120
+ "ef_search": self.ef_search,
121
+ }
@@ -0,0 +1,365 @@
1
+ """Wrapper around the Hologres vector database over VectorDB"""
2
+
3
+ import json
4
+ import logging
5
+ from collections.abc import Generator
6
+ from contextlib import contextmanager
7
+ from io import StringIO
8
+ from typing import Any
9
+
10
+ import psycopg
11
+ from psycopg import Connection, Cursor, sql
12
+
13
+ from ..api import VectorDB
14
+ from .config import HologresConfig, HologresIndexConfig
15
+
16
+ log = logging.getLogger(__name__)
17
+
18
+
19
+ class Hologres(VectorDB):
20
+ """Use psycopg instructions"""
21
+
22
+ conn: psycopg.Connection[Any] | None = None
23
+ cursor: psycopg.Cursor[Any] | None = None
24
+
25
+ _tg_name: str = "vdb_bench_tg_1"
26
+
27
+ def __init__(
28
+ self,
29
+ dim: int,
30
+ db_config: HologresConfig,
31
+ db_case_config: HologresIndexConfig,
32
+ collection_name: str = "vector_collection",
33
+ drop_old: bool = False,
34
+ **kwargs,
35
+ ):
36
+ self.name = "Alibaba Cloud Hologres"
37
+ self.db_config = db_config
38
+ self.case_config = db_case_config
39
+ self.table_name = collection_name
40
+ self.dim = dim
41
+
42
+ self._primary_field = "id"
43
+ self._vector_field = "embedding"
44
+
45
+ # construct basic units
46
+ self.conn, self.cursor = self._create_connection(**self.db_config)
47
+
48
+ # create vector extension
49
+ if self.case_config.is_proxima():
50
+ self.cursor.execute("CREATE EXTENSION proxima;")
51
+ self.conn.commit()
52
+
53
+ log.info(f"{self.name} config values: {self.db_config}\n{self.case_config}")
54
+ if not any(
55
+ (
56
+ self.case_config.create_index_before_load,
57
+ self.case_config.create_index_after_load,
58
+ ),
59
+ ):
60
+ msg = (
61
+ f"{self.name} config must create an index using create_index_before_load or create_index_after_load"
62
+ f"{self.name} config values: {self.db_config}\n{self.case_config}"
63
+ )
64
+ log.error(msg)
65
+ raise RuntimeError(msg)
66
+
67
+ if drop_old:
68
+ self._drop_table()
69
+ self._create_table(dim)
70
+ if self.case_config.create_index_before_load:
71
+ self._create_index()
72
+
73
+ self.cursor.close()
74
+ self.conn.close()
75
+ self.cursor = None
76
+ self.conn = None
77
+
78
+ @staticmethod
79
+ def _create_connection(**kwargs) -> tuple[Connection, Cursor]:
80
+ conn = psycopg.connect(**kwargs)
81
+ conn.autocommit = True
82
+ cursor = conn.cursor()
83
+
84
+ assert conn is not None, "Connection is not initialized"
85
+ assert cursor is not None, "Cursor is not initialized"
86
+
87
+ return conn, cursor
88
+
89
+ @contextmanager
90
+ def init(self) -> Generator[None, None, None]:
91
+ """
92
+ Examples:
93
+ >>> with self.init():
94
+ >>> self.insert_embeddings()
95
+ >>> self.search_embedding()
96
+ """
97
+
98
+ self.conn, self.cursor = self._create_connection(**self.db_config)
99
+
100
+ self._set_search_guc()
101
+
102
+ try:
103
+ yield
104
+ finally:
105
+ self.cursor.close()
106
+ self.conn.close()
107
+ self.cursor = None
108
+ self.conn = None
109
+
110
+ def _set_search_guc(self):
111
+ assert self.conn is not None, "Connection is not initialized"
112
+ assert self.cursor is not None, "Cursor is not initialized"
113
+
114
+ sql_guc = sql.SQL(f"SET hg_vector_ef_search = {self.case_config.ef_search};")
115
+ log.info(f"{self.name} client set search guc: {sql_guc.as_string()}")
116
+ self.cursor.execute(sql_guc)
117
+ self.conn.commit()
118
+
119
+ def _drop_table(self):
120
+ assert self.conn is not None, "Connection is not initialized"
121
+ assert self.cursor is not None, "Cursor is not initialized"
122
+
123
+ log.info(f"{self.name} client drop table : {self.table_name}")
124
+ self.cursor.execute(
125
+ sql.SQL("DROP TABLE IF EXISTS {table_name};").format(
126
+ table_name=sql.Identifier(self.table_name),
127
+ ),
128
+ )
129
+ self.conn.commit()
130
+
131
+ try:
132
+ log.info(f"{self.name} client drop table group : {self._tg_name}")
133
+ self.cursor.execute(sql.SQL(f"CALL HG_DROP_TABLE_GROUP('{self._tg_name}');"))
134
+ except Exception as e:
135
+ log.info(f"{self.name} client drop table group : {self._tg_name} failed, error: {e}, ignore.")
136
+ finally:
137
+ self.conn.commit()
138
+
139
+ def optimize(self, data_size: int | None = None):
140
+ if self.case_config.create_index_after_load:
141
+ self._create_index()
142
+ self._full_compact()
143
+ self._analyze()
144
+
145
+ def _vacuum(self):
146
+ log.info(f"{self.name} client vacuum table : {self.table_name}")
147
+ try:
148
+ # VACUUM cannot run inside a transaction block
149
+ # it's better to new a connection
150
+ self.conn.autocommit = True
151
+ with self.conn.cursor() as cursor:
152
+ cursor.execute(
153
+ sql.SQL(
154
+ """
155
+ VACUUM {table_name};
156
+ """
157
+ ).format(
158
+ table_name=sql.Identifier(self.table_name),
159
+ )
160
+ )
161
+ log.info(f"{self.name} client vacuum table : {self.table_name} done")
162
+ except Exception as e:
163
+ log.warning(f"Failed to vacuum table: {self.table_name} error: {e}")
164
+ raise e from None
165
+ finally:
166
+ self.conn.autocommit = True
167
+
168
+ def _analyze(self):
169
+ log.info(f"{self.name} client analyze table : {self.table_name}")
170
+ self.cursor.execute(sql.SQL(f"ANALYZE {self.table_name};"))
171
+ log.info(f"{self.name} client analyze table : {self.table_name} done")
172
+
173
+ def _full_compact(self):
174
+ log.info(f"{self.name} client full compact table : {self.table_name}")
175
+ self.cursor.execute(
176
+ sql.SQL(
177
+ """
178
+ SELECT hologres.hg_full_compact_table(
179
+ '{table_name}',
180
+ 'max_file_size_mb={full_compact_max_file_size_mb}'
181
+ );
182
+ """
183
+ ).format(
184
+ table_name=sql.SQL(self.table_name),
185
+ full_compact_max_file_size_mb=sql.SQL(str(self.case_config.full_compact_max_file_size_mb)),
186
+ )
187
+ )
188
+ log.info(f"{self.name} client full compact table : {self.table_name} done")
189
+
190
+ def _create_index(self):
191
+ assert self.conn is not None, "Connection is not initialized"
192
+ assert self.cursor is not None, "Cursor is not initialized"
193
+
194
+ sql_index = sql.SQL(
195
+ """
196
+ CALL set_table_property ('{table_name}', 'vectors', '{{
197
+ "embedding": {{
198
+ "algorithm": "{algorithm}",
199
+ "distance_method": "{distance_method}",
200
+ "builder_params": {builder_params}
201
+ }}
202
+ }}');
203
+ """
204
+ ).format(
205
+ table_name=sql.Identifier(self.table_name),
206
+ algorithm=sql.SQL(self.case_config.algorithm()),
207
+ distance_method=sql.SQL(self.case_config.distance_method()),
208
+ builder_params=sql.SQL(json.dumps(self.case_config.builder_params())),
209
+ )
210
+
211
+ log.info(f"{self.name} client create index on table : {self.table_name}, with sql: {sql_index.as_string()}")
212
+ try:
213
+ self.cursor.execute(sql_index)
214
+ self.conn.commit()
215
+ except Exception as e:
216
+ log.warning(f"Failed to create index on table: {self.table_name} error: {e}")
217
+ raise e from None
218
+
219
+ def _set_replica_count(self, replica_count: int = 2):
220
+ assert self.conn is not None, "Connection is not initialized"
221
+ assert self.cursor is not None, "Cursor is not initialized"
222
+
223
+ try:
224
+ # non-warehouse mode by default
225
+ sql_tg_replica = sql.SQL(
226
+ f"CALL hg_set_table_group_property('{self._tg_name}', 'replica_count', '{replica_count}');"
227
+ )
228
+
229
+ # check warehouse mode
230
+ sql_check = sql.SQL("select count(*) from hologres.hg_warehouses;")
231
+ log.info(f"check warehouse mode with sql: {sql_check}")
232
+ self.cursor.execute(sql_check)
233
+ result_check = self.cursor.fetchone()[0]
234
+ if result_check > 0:
235
+ # get warehouse name
236
+ sql_get_warehouse_name = sql.SQL("select current_warehouse();")
237
+ log.info(f"get warehouse name with sql: {sql_get_warehouse_name}")
238
+ self.cursor.execute(sql_get_warehouse_name)
239
+ sql_tg_replica = sql.SQL(
240
+ """
241
+ CALL hg_table_group_set_warehouse_replica_count (
242
+ '{dbname}.{tg_name}',
243
+ {replica_count},
244
+ '{warehouse_name}'
245
+ );
246
+ """
247
+ ).format(
248
+ tg_name=sql.SQL(self._tg_name),
249
+ warehouse_name=sql.SQL(self.cursor.fetchone()[0]),
250
+ dbname=sql.SQL(self.db_config["dbname"]),
251
+ replica_count=replica_count,
252
+ )
253
+ log.info(f"{self.name} client set table group replica: {self._tg_name}, with sql: {sql_tg_replica}")
254
+ self.cursor.execute(sql_tg_replica)
255
+ except Exception as e:
256
+ log.warning(f"Failed to set replica count, error: {e}, ignore")
257
+ finally:
258
+ self.conn.commit()
259
+
260
+ def _create_table(self, dim: int):
261
+ assert self.conn is not None, "Connection is not initialized"
262
+ assert self.cursor is not None, "Cursor is not initialized"
263
+
264
+ sql_tg = sql.SQL(f"CALL HG_CREATE_TABLE_GROUP ('{self._tg_name}', 1);")
265
+ log.info(f"{self.name} client create table group : {self._tg_name}, with sql: {sql_tg}")
266
+ try:
267
+ self.cursor.execute(sql_tg)
268
+ except Exception as e:
269
+ log.warning(f"Failed to create table group : {self._tg_name} error: {e}, ignore")
270
+ finally:
271
+ self.conn.commit()
272
+
273
+ self._set_replica_count(replica_count=2)
274
+
275
+ sql_table = sql.SQL(
276
+ """
277
+ CREATE TABLE IF NOT EXISTS {table_name} (
278
+ id BIGINT PRIMARY KEY,
279
+ embedding FLOAT4[] CHECK (array_ndims(embedding) = 1 AND array_length(embedding, 1) = {dim})
280
+ )
281
+ WITH (table_group = {tg_name});
282
+ """
283
+ ).format(
284
+ table_name=sql.Identifier(self.table_name),
285
+ dim=dim,
286
+ tg_name=sql.SQL(self._tg_name),
287
+ )
288
+ log.info(f"{self.name} client create table : {self.table_name}, with sql: {sql_table.as_string()}")
289
+ try:
290
+ self.cursor.execute(sql_table)
291
+ self.conn.commit()
292
+ except Exception as e:
293
+ log.warning(f"Failed to create table : {self.table_name} error: {e}")
294
+ raise e from None
295
+
296
+ def insert_embeddings(
297
+ self,
298
+ embeddings: list[list[float]],
299
+ metadata: list[int],
300
+ **kwargs: Any,
301
+ ) -> tuple[int, Exception | None]:
302
+ assert self.conn is not None, "Connection is not initialized"
303
+ assert self.cursor is not None, "Cursor is not initialized"
304
+
305
+ try:
306
+ buffer = StringIO()
307
+ for i in range(len(metadata)):
308
+ buffer.write("%d\t%s\n" % (metadata[i], "{" + ",".join("%f" % x for x in embeddings[i]) + "}"))
309
+ buffer.seek(0)
310
+
311
+ with self.cursor.copy(
312
+ sql.SQL("COPY {table_name} FROM STDIN").format(table_name=sql.Identifier(self.table_name))
313
+ ) as copy:
314
+ copy.write(buffer.getvalue())
315
+ self.conn.commit()
316
+
317
+ return len(metadata), None
318
+ except Exception as e:
319
+ log.warning(f"Failed to insert data into table ({self.table_name}), error: {e}")
320
+ return 0, e
321
+
322
+ def _compose_query_and_params(self, vec: list[float], topk: int, ge_id: int | None = None):
323
+ params = []
324
+
325
+ where_clause = sql.SQL("")
326
+ if ge_id is not None:
327
+ where_clause = sql.SQL(" WHERE id >= %s ")
328
+ params.append(ge_id)
329
+
330
+ vec_float4 = [psycopg._wrappers.Float4(i) for i in vec]
331
+ params.append(vec_float4)
332
+ params.append(topk)
333
+
334
+ query = sql.SQL(
335
+ """
336
+ SELECT id
337
+ FROM {table_name}
338
+ {where_clause}
339
+ ORDER BY {distance_function}(embedding, %b)
340
+ {order_direction}
341
+ LIMIT %s;
342
+ """
343
+ ).format(
344
+ table_name=sql.Identifier(self.table_name),
345
+ distance_function=sql.SQL(self.case_config.distance_function()),
346
+ where_clause=where_clause,
347
+ order_direction=sql.SQL(self.case_config.order_direction()),
348
+ )
349
+
350
+ return query, params
351
+
352
+ def search_embedding(
353
+ self,
354
+ query: list[float],
355
+ k: int = 100,
356
+ filters: dict | None = None,
357
+ timeout: int | None = None,
358
+ ) -> list[int]:
359
+ assert self.conn is not None, "Connection is not initialized"
360
+ assert self.cursor is not None, "Cursor is not initialized"
361
+
362
+ ge = filters.get("id") if filters else None
363
+ q, params = self._compose_query_and_params(query, k, ge)
364
+ result = self.cursor.execute(q, params, prepare=True, binary=True)
365
+ return [int(i[0]) for i in result.fetchall()]
@@ -65,6 +65,7 @@ class LanceDB(VectorDB):
65
65
  self,
66
66
  embeddings: list[list[float]],
67
67
  metadata: list[int],
68
+ **kwargs,
68
69
  ) -> tuple[int, Exception | None]:
69
70
  try:
70
71
  data = [{"id": meta, "vector": emb} for meta, emb in zip(metadata, embeddings, strict=False)]
@@ -40,6 +40,17 @@ class MilvusTypedDict(TypedDict):
40
40
  show_default=True,
41
41
  ),
42
42
  ]
43
+ replica_number: Annotated[
44
+ int,
45
+ click.option(
46
+ "--replica-number",
47
+ type=int,
48
+ help="Number of replicas",
49
+ required=False,
50
+ default=1,
51
+ show_default=True,
52
+ ),
53
+ ]
43
54
 
44
55
 
45
56
  class MilvusAutoIndexTypedDict(CommonTypedDict, MilvusTypedDict): ...
@@ -58,6 +69,7 @@ def MilvusAutoIndex(**parameters: Unpack[MilvusAutoIndexTypedDict]):
58
69
  user=parameters["user_name"],
59
70
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
60
71
  num_shards=int(parameters["num_shards"]),
72
+ replica_number=int(parameters["replica_number"]),
61
73
  ),
62
74
  db_case_config=AutoIndexConfig(),
63
75
  **parameters,
@@ -77,6 +89,7 @@ def MilvusFlat(**parameters: Unpack[MilvusAutoIndexTypedDict]):
77
89
  user=parameters["user_name"],
78
90
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
79
91
  num_shards=int(parameters["num_shards"]),
92
+ replica_number=int(parameters["replica_number"]),
80
93
  ),
81
94
  db_case_config=FLATConfig(),
82
95
  **parameters,
@@ -99,6 +112,7 @@ def MilvusHNSW(**parameters: Unpack[MilvusHNSWTypedDict]):
99
112
  user=parameters["user_name"],
100
113
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
101
114
  num_shards=int(parameters["num_shards"]),
115
+ replica_number=int(parameters["replica_number"]),
102
116
  ),
103
117
  db_case_config=HNSWConfig(
104
118
  M=parameters["m"],
@@ -139,19 +153,14 @@ class MilvusRefineTypedDict(TypedDict):
139
153
  ]
140
154
 
141
155
 
142
- class MilvusHNSWPQTypedDict(
143
- CommonTypedDict,
144
- MilvusTypedDict,
145
- MilvusHNSWTypedDict,
146
- MilvusRefineTypedDict
147
- ):
156
+ class MilvusHNSWPQTypedDict(CommonTypedDict, MilvusTypedDict, MilvusHNSWTypedDict, MilvusRefineTypedDict):
148
157
  nbits: Annotated[
149
158
  int,
150
159
  click.option(
151
160
  "--nbits",
152
161
  type=int,
153
162
  required=True,
154
- )
163
+ ),
155
164
  ]
156
165
 
157
166
 
@@ -168,6 +177,7 @@ def MilvusHNSWPQ(**parameters: Unpack[MilvusHNSWPQTypedDict]):
168
177
  user=parameters["user_name"],
169
178
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
170
179
  num_shards=int(parameters["num_shards"]),
180
+ replica_number=int(parameters["replica_number"]),
171
181
  ),
172
182
  db_case_config=HNSWPQConfig(
173
183
  M=parameters["m"],
@@ -194,7 +204,7 @@ class MilvusHNSWPRQTypedDict(
194
204
  type=int,
195
205
  help="The number of residual subquantizers.",
196
206
  required=True,
197
- )
207
+ ),
198
208
  ]
199
209
 
200
210
 
@@ -211,6 +221,7 @@ def MilvusHNSWPRQ(**parameters: Unpack[MilvusHNSWPRQTypedDict]):
211
221
  user=parameters["user_name"],
212
222
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
213
223
  num_shards=int(parameters["num_shards"]),
224
+ replica_number=int(parameters["replica_number"]),
214
225
  ),
215
226
  db_case_config=HNSWPRQConfig(
216
227
  M=parameters["m"],
@@ -220,7 +231,7 @@ def MilvusHNSWPRQ(**parameters: Unpack[MilvusHNSWPRQTypedDict]):
220
231
  refine=parameters["refine"],
221
232
  refine_type=parameters["refine_type"],
222
233
  refine_k=parameters["refine_k"],
223
- nrq=parameters["nrq"]
234
+ nrq=parameters["nrq"],
224
235
  ),
225
236
  **parameters,
226
237
  )
@@ -251,6 +262,7 @@ def MilvusHNSWSQ(**parameters: Unpack[MilvusHNSWSQTypedDict]):
251
262
  user=parameters["user_name"],
252
263
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
253
264
  num_shards=int(parameters["num_shards"]),
265
+ replica_number=int(parameters["replica_number"]),
254
266
  ),
255
267
  db_case_config=HNSWSQConfig(
256
268
  M=parameters["m"],
@@ -281,6 +293,7 @@ def MilvusIVFFlat(**parameters: Unpack[MilvusIVFFlatTypedDict]):
281
293
  user=parameters["user_name"],
282
294
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
283
295
  num_shards=int(parameters["num_shards"]),
296
+ replica_number=int(parameters["replica_number"]),
284
297
  ),
285
298
  db_case_config=IVFFlatConfig(
286
299
  nlist=parameters["nlist"],
@@ -303,6 +316,7 @@ def MilvusIVFSQ8(**parameters: Unpack[MilvusIVFFlatTypedDict]):
303
316
  user=parameters["user_name"],
304
317
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
305
318
  num_shards=int(parameters["num_shards"]),
319
+ replica_number=int(parameters["replica_number"]),
306
320
  ),
307
321
  db_case_config=IVFSQ8Config(
308
322
  nlist=parameters["nlist"],
@@ -364,6 +378,7 @@ def MilvusIVFRabitQ(**parameters: Unpack[MilvusIVFRABITQTypedDict]):
364
378
  user=parameters["user_name"],
365
379
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
366
380
  num_shards=int(parameters["num_shards"]),
381
+ replica_number=int(parameters["replica_number"]),
367
382
  ),
368
383
  db_case_config=IVFRABITQConfig(
369
384
  nlist=parameters["nlist"],
@@ -394,6 +409,7 @@ def MilvusDISKANN(**parameters: Unpack[MilvusDISKANNTypedDict]):
394
409
  user=parameters["user_name"],
395
410
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
396
411
  num_shards=int(parameters["num_shards"]),
412
+ replica_number=int(parameters["replica_number"]),
397
413
  ),
398
414
  db_case_config=DISKANNConfig(
399
415
  search_list=parameters["search_list"],
@@ -423,6 +439,7 @@ def MilvusGPUIVFFlat(**parameters: Unpack[MilvusGPUIVFTypedDict]):
423
439
  user=parameters["user_name"],
424
440
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
425
441
  num_shards=int(parameters["num_shards"]),
442
+ replica_number=int(parameters["replica_number"]),
426
443
  ),
427
444
  db_case_config=GPUIVFFlatConfig(
428
445
  nlist=parameters["nlist"],
@@ -458,6 +475,7 @@ def MilvusGPUBruteForce(**parameters: Unpack[MilvusGPUBruteForceTypedDict]):
458
475
  user=parameters["user_name"],
459
476
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
460
477
  num_shards=int(parameters["num_shards"]),
478
+ replica_number=int(parameters["replica_number"]),
461
479
  ),
462
480
  db_case_config=GPUBruteForceConfig(
463
481
  metric_type=parameters["metric_type"],
@@ -490,6 +508,7 @@ def MilvusGPUIVFPQ(**parameters: Unpack[MilvusGPUIVFPQTypedDict]):
490
508
  user=parameters["user_name"],
491
509
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
492
510
  num_shards=int(parameters["num_shards"]),
511
+ replica_number=int(parameters["replica_number"]),
493
512
  ),
494
513
  db_case_config=GPUIVFPQConfig(
495
514
  nlist=parameters["nlist"],
@@ -530,6 +549,7 @@ def MilvusGPUCAGRA(**parameters: Unpack[MilvusGPUCAGRATypedDict]):
530
549
  user=parameters["user_name"],
531
550
  password=SecretStr(parameters["password"]) if parameters["password"] else None,
532
551
  num_shards=int(parameters["num_shards"]),
552
+ replica_number=int(parameters["replica_number"]),
533
553
  ),
534
554
  db_case_config=GPUCAGRAConfig(
535
555
  intermediate_graph_degree=parameters["intermediate_graph_degree"],
@@ -8,6 +8,7 @@ class MilvusConfig(DBConfig):
8
8
  user: str | None = None
9
9
  password: SecretStr | None = None
10
10
  num_shards: int = 1
11
+ replica_number: int = 1
11
12
 
12
13
  def to_dict(self) -> dict:
13
14
  return {
@@ -15,6 +16,7 @@ class MilvusConfig(DBConfig):
15
16
  "user": self.user if self.user else None,
16
17
  "password": self.password.get_secret_value() if self.password else None,
17
18
  "num_shards": self.num_shards,
19
+ "replica_number": self.replica_number,
18
20
  }
19
21
 
20
22
  @validator("*")
@@ -92,7 +92,7 @@ class Milvus(VectorDB):
92
92
  )
93
93
 
94
94
  self.create_index()
95
- col.load()
95
+ col.load(replica_number=self.db_config.get("replica_number", 1))
96
96
 
97
97
  connections.disconnect("default")
98
98
 
@@ -93,6 +93,7 @@ def OceanBaseIVF(**parameters: Unpack[OceanBaseIVFTypedDict]):
93
93
  m=input_m,
94
94
  nlist=parameters["nlist"],
95
95
  sample_per_nlist=parameters["sample_per_nlist"],
96
+ nbits=parameters["nbits"],
96
97
  index=input_index_type,
97
98
  ivf_nprobes=parameters["ivf_nprobes"],
98
99
  ),