pyseekdb 0.1.0.dev3__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.
pyseekdb/__init__.py ADDED
@@ -0,0 +1,90 @@
1
+ """
2
+ SeekDBClient - Unified vector database client wrapper
3
+
4
+ Based on seekdb and pymysql, providing a simple and unified API.
5
+
6
+ Supports three modes:
7
+ 1. Embedded mode - using local seekdb
8
+ 2. Server mode - connecting to remote seekdb via pymysql
9
+ 3. OceanBase mode - connecting to OceanBase via pymysql
10
+
11
+ Examples:
12
+ >>> import seekdbclient
13
+
14
+ >>> # Embedded mode - Collection management
15
+ >>> client = seekdbclient.Client(path="./seekdb", database="test")
16
+
17
+ >>> # Server mode - Collection management
18
+ >>> client = seekdbclient.Client(
19
+ ... host='localhost',
20
+ ... port=2881,
21
+ ... database="test",
22
+ ... user="root",
23
+ ... password="pass"
24
+ ... )
25
+
26
+ >>> # OceanBase mode - Collection management
27
+ >>> ob_client = seekdbclient.OBClient(
28
+ ... host='localhost',
29
+ ... port=2881,
30
+ ... tenant="test",
31
+ ... database="test",
32
+ ... user="root",
33
+ ... password=""
34
+ ... )
35
+
36
+ >>> # Admin client - Database management
37
+ >>> admin = seekdbclient.AdminClient(path="./seekdb")
38
+ >>> admin.create_database("new_db")
39
+ >>> databases = admin.list_databases()
40
+ """
41
+ import logging
42
+ import importlib.metadata
43
+
44
+ from .client import (
45
+ BaseConnection,
46
+ BaseClient,
47
+ ClientAPI,
48
+ SeekdbEmbeddedClient,
49
+ SeekdbServerClient,
50
+ OceanBaseServerClient,
51
+ Client,
52
+ OBClient,
53
+ AdminAPI,
54
+ AdminClient,
55
+ OBAdminClient,
56
+ Database,
57
+ )
58
+ from .client.collection import Collection
59
+ from .client.query_result import QueryResult
60
+
61
+ try:
62
+ __version__ = importlib.metadata.version("seekdbclient")
63
+ except importlib.metadata.PackageNotFoundError:
64
+ __version__ = "0.0.1.dev1"
65
+
66
+ __author__ = "SeekDBClient Team"
67
+
68
+ __all__ = [
69
+ 'BaseConnection',
70
+ 'BaseClient',
71
+ 'ClientAPI',
72
+ 'SeekdbEmbeddedClient',
73
+ 'SeekdbServerClient',
74
+ 'OceanBaseServerClient',
75
+ 'Client',
76
+ 'OBClient',
77
+ 'Collection',
78
+ 'QueryResult',
79
+ 'AdminAPI',
80
+ 'AdminClient',
81
+ 'OBAdminClient',
82
+ 'Database',
83
+ ]
84
+
85
+ # Configure logging
86
+ logging.basicConfig(
87
+ level=logging.INFO,
88
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
89
+ )
90
+
@@ -0,0 +1,324 @@
1
+ """
2
+ SeekDBClient client module
3
+
4
+ Provides client and admin factory functions with strict separation:
5
+
6
+ Collection Management (ClientAPI):
7
+ - Client() - Smart factory for Embedded/Server mode
8
+ - OBClient() - OceanBase client factory
9
+ - Returns: _ClientProxy (collection operations only)
10
+
11
+ Database Management (AdminAPI):
12
+ - AdminClient() - Smart factory for Embedded/Server mode
13
+ - OBAdminClient() - OceanBase admin factory
14
+ - Returns: _AdminClientProxy (database operations only)
15
+
16
+ All factories use the underlying ServerAPI implementations:
17
+ - SeekdbEmbeddedClient - Local seekdb
18
+ - SeekdbServerClient - Remote seekdb via pymysql
19
+ - OceanBaseServerClient - OceanBase via pymysql
20
+ """
21
+ import logging
22
+ from typing import Optional, Union
23
+
24
+ from .base_connection import BaseConnection
25
+ from .client_base import BaseClient, ClientAPI
26
+ from .client_seekdb_embedded import SeekdbEmbeddedClient
27
+ from .client_seekdb_server import SeekdbServerClient
28
+ from .client_oceanbase_server import OceanBaseServerClient
29
+ from .admin_client import AdminAPI, _AdminClientProxy, _ClientProxy
30
+ from .database import Database
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+ __all__ = [
35
+ 'BaseConnection',
36
+ 'BaseClient',
37
+ 'ClientAPI',
38
+ 'SeekdbEmbeddedClient',
39
+ 'SeekdbServerClient',
40
+ 'OceanBaseServerClient',
41
+ 'Client',
42
+ 'OBClient',
43
+ 'AdminAPI',
44
+ 'AdminClient',
45
+ 'OBAdminClient',
46
+ 'Database',
47
+ ]
48
+
49
+
50
+ def Client(
51
+ path: Optional[str] = None,
52
+ host: Optional[str] = None,
53
+ port: Optional[int] = None,
54
+ database: str = "test",
55
+ user: Optional[str] = None,
56
+ password: str = "",
57
+ **kwargs
58
+ ) -> _ClientProxy:
59
+ """
60
+ Smart client factory function (returns ClientProxy for collection operations only)
61
+
62
+ Automatically selects embedded or server mode based on parameters:
63
+ - If path is provided, uses embedded mode
64
+ - If host/port is provided, uses server mode
65
+
66
+ Returns a ClientProxy that only exposes collection operations.
67
+ For database management, use AdminClient().
68
+
69
+ Args:
70
+ path: seekdb data directory path (embedded mode)
71
+ host: server address (server mode)
72
+ port: server port (server mode)
73
+ database: database name
74
+ user: username (server mode)
75
+ password: password (server mode)
76
+ **kwargs: other parameters
77
+
78
+ Returns:
79
+ _ClientProxy: A proxy that only exposes collection operations
80
+
81
+ Examples:
82
+ >>> # Embedded mode
83
+ >>> client = Client(path="/path/to/seekdb", database="db1")
84
+ >>> client.create_collection("my_collection") # ✅ Available
85
+ >>> # client.create_database("new_db") # ❌ Not available
86
+
87
+ >>> # Server mode
88
+ >>> client = Client(
89
+ ... host='localhost',
90
+ ... port=2882,
91
+ ... database="db1",
92
+ ... user="u01",
93
+ ... password="pass"
94
+ ... )
95
+ """
96
+ # Determine mode and create appropriate server
97
+ if path is not None:
98
+ # Embedded mode
99
+ logger.info(f"Creating embedded client: path={path}, database={database}")
100
+ server = SeekdbEmbeddedClient(
101
+ path=path,
102
+ database=database,
103
+ **kwargs
104
+ )
105
+
106
+ elif host is not None:
107
+ # Server mode
108
+ if port is None:
109
+ port = 2882 # Default port
110
+ if user is None:
111
+ user = "root"
112
+
113
+ logger.info(
114
+ f"Creating server mode client: {user}@{host}:{port}/{database}"
115
+ )
116
+ server = SeekdbServerClient(
117
+ host=host,
118
+ port=port,
119
+ database=database,
120
+ user=user,
121
+ password=password,
122
+ **kwargs
123
+ )
124
+
125
+ else:
126
+ raise ValueError(
127
+ "Must provide either path (embedded mode) or host (server mode) parameter"
128
+ )
129
+
130
+ # Return ClientProxy (only exposes collection operations)
131
+ return _ClientProxy(server=server)
132
+
133
+
134
+ def OBClient(
135
+ host: str = "localhost",
136
+ port: int = 2881,
137
+ tenant: str = "test",
138
+ database: str = "test",
139
+ user: str = "root",
140
+ password: str = "",
141
+ **kwargs
142
+ ) -> _ClientProxy:
143
+ """
144
+ OceanBase client factory function (returns ClientProxy for collection operations only)
145
+
146
+ Returns a ClientProxy that only exposes collection operations.
147
+ For database management, use OBAdminClient().
148
+
149
+ Args:
150
+ host: server address
151
+ port: server port (default 2881)
152
+ tenant: tenant name
153
+ database: database name
154
+ user: username (without tenant suffix)
155
+ password: password
156
+ **kwargs: other parameters
157
+
158
+ Returns:
159
+ _ClientProxy: A proxy that only exposes collection operations
160
+
161
+ Examples:
162
+ >>> client = OBClient(
163
+ ... host='localhost',
164
+ ... port=2881,
165
+ ... tenant="tenant1",
166
+ ... database="db1",
167
+ ... user="u01",
168
+ ... password="pass"
169
+ ... )
170
+ >>> client.create_collection("my_collection") # ✅ Available
171
+ >>> # client.create_database("new_db") # ❌ Not available
172
+ """
173
+ logger.info(
174
+ f"Creating OceanBase client: {user}@{tenant}@{host}:{port}/{database}"
175
+ )
176
+
177
+ server = OceanBaseServerClient(
178
+ host=host,
179
+ port=port,
180
+ tenant=tenant,
181
+ database=database,
182
+ user=user,
183
+ password=password,
184
+ **kwargs
185
+ )
186
+
187
+ # Return ClientProxy (only exposes collection operations)
188
+ return _ClientProxy(server=server)
189
+
190
+
191
+ def AdminClient(
192
+ path: Optional[str] = None,
193
+ host: Optional[str] = None,
194
+ port: Optional[int] = None,
195
+ user: Optional[str] = None,
196
+ password: str = "",
197
+ **kwargs
198
+ ) -> _AdminClientProxy:
199
+ """
200
+ Smart admin client factory function (proxy pattern)
201
+
202
+ Automatically selects embedded or server mode based on parameters:
203
+ - If path is provided, uses embedded mode
204
+ - If host/port is provided, uses server mode
205
+
206
+ Returns a lightweight AdminClient proxy that only exposes database operations.
207
+ For collection management, use Client().
208
+
209
+ Args:
210
+ path: seekdb data directory path (embedded mode)
211
+ host: server address (server mode)
212
+ port: server port (server mode)
213
+ user: username (server mode)
214
+ password: password (server mode)
215
+ **kwargs: other parameters
216
+
217
+ Returns:
218
+ _AdminClientProxy: A proxy that only exposes database operations
219
+
220
+ Examples:
221
+ >>> # Embedded mode
222
+ >>> admin = AdminClient(path="/path/to/seekdb")
223
+ >>> admin.create_database("new_db") # ✅ Available
224
+ >>> # admin.create_collection("coll") # ❌ Not available
225
+
226
+ >>> # Server mode
227
+ >>> admin = AdminClient(
228
+ ... host='localhost',
229
+ ... port=2882,
230
+ ... user="root",
231
+ ... password="pass"
232
+ ... )
233
+ """
234
+ # Determine mode and create appropriate server
235
+ if path is not None:
236
+ # Embedded mode
237
+ logger.info(f"Creating embedded admin client: path={path}")
238
+ server = SeekdbEmbeddedClient(
239
+ path=path,
240
+ database="information_schema", # Use system database for admin operations
241
+ **kwargs
242
+ )
243
+
244
+ elif host is not None:
245
+ # Server mode
246
+ if port is None:
247
+ port = 2882 # Default port
248
+ if user is None:
249
+ user = "root"
250
+
251
+ logger.info(
252
+ f"Creating server mode admin client: {user}@{host}:{port}"
253
+ )
254
+ server = SeekdbServerClient(
255
+ host=host,
256
+ port=port,
257
+ database="information_schema", # Use system database
258
+ user=user,
259
+ password=password,
260
+ **kwargs
261
+ )
262
+
263
+ else:
264
+ raise ValueError(
265
+ "Must provide either path (embedded mode) or host (server mode) parameter"
266
+ )
267
+
268
+ # Return AdminClient proxy (only exposes database operations)
269
+ return _AdminClientProxy(server=server)
270
+
271
+
272
+ def OBAdminClient(
273
+ host: str = "localhost",
274
+ port: int = 2881,
275
+ tenant: str = "test",
276
+ user: str = "root",
277
+ password: str = "",
278
+ **kwargs
279
+ ) -> _AdminClientProxy:
280
+ """
281
+ OceanBase admin client factory function (proxy pattern)
282
+
283
+ Returns a lightweight AdminClient proxy that only exposes database operations.
284
+ For collection management, use OBClient().
285
+
286
+ Args:
287
+ host: server address
288
+ port: server port (default 2881)
289
+ tenant: tenant name
290
+ user: username (without tenant suffix)
291
+ password: password
292
+ **kwargs: other parameters
293
+
294
+ Returns:
295
+ _AdminClientProxy: A proxy that only exposes database operations
296
+
297
+ Examples:
298
+ >>> admin = OBAdminClient(
299
+ ... host='localhost',
300
+ ... port=2881,
301
+ ... tenant="tenant1",
302
+ ... user="root",
303
+ ... password="pass"
304
+ ... )
305
+ >>> admin.create_database("new_db") # ✅ Available
306
+ >>> # admin.create_collection("coll") # ❌ Not available
307
+ """
308
+ logger.info(
309
+ f"Creating OceanBase admin client: {user}@{tenant}@{host}:{port}"
310
+ )
311
+
312
+ server = OceanBaseServerClient(
313
+ host=host,
314
+ port=port,
315
+ tenant=tenant,
316
+ database="information_schema", # Use system database
317
+ user=user,
318
+ password=password,
319
+ **kwargs
320
+ )
321
+
322
+ # Return AdminClient proxy (only exposes database operations)
323
+ return _AdminClientProxy(server=server)
324
+
@@ -0,0 +1,202 @@
1
+ """
2
+ Admin client interface and implementation for database management
3
+
4
+ Also includes ClientProxy for strict separation of Collection vs Database operations
5
+ """
6
+ from abc import ABC, abstractmethod
7
+ from typing import List, Optional, Sequence, TYPE_CHECKING
8
+
9
+ from .database import Database
10
+
11
+ if TYPE_CHECKING:
12
+ from .client_base import BaseClient, ClientAPI
13
+ from .collection import Collection
14
+
15
+ DEFAULT_TENANT = "test"
16
+
17
+
18
+ class AdminAPI(ABC):
19
+ """
20
+ Abstract admin API interface for database management.
21
+ Defines the contract for database operations.
22
+ """
23
+
24
+ @abstractmethod
25
+ def create_database(self, name: str, tenant: str = DEFAULT_TENANT) -> None:
26
+ """
27
+ Create database
28
+
29
+ Args:
30
+ name: database name
31
+ tenant: tenant name (for OceanBase)
32
+ """
33
+ pass
34
+
35
+ @abstractmethod
36
+ def get_database(self, name: str, tenant: str = DEFAULT_TENANT) -> Database:
37
+ """
38
+ Get database object
39
+
40
+ Args:
41
+ name: database name
42
+ tenant: tenant name (for OceanBase)
43
+
44
+ Returns:
45
+ Database object
46
+ """
47
+ pass
48
+
49
+ @abstractmethod
50
+ def delete_database(self, name: str, tenant: str = DEFAULT_TENANT) -> None:
51
+ """
52
+ Delete database
53
+
54
+ Args:
55
+ name: database name
56
+ tenant: tenant name (for OceanBase)
57
+ """
58
+ pass
59
+
60
+ @abstractmethod
61
+ def list_databases(
62
+ self,
63
+ limit: Optional[int] = None,
64
+ offset: Optional[int] = None,
65
+ tenant: str = DEFAULT_TENANT
66
+ ) -> Sequence[Database]:
67
+ """
68
+ List all databases
69
+
70
+ Args:
71
+ limit: maximum number of results to return
72
+ offset: number of results to skip
73
+ tenant: tenant name (for OceanBase)
74
+
75
+ Returns:
76
+ Sequence of Database objects
77
+ """
78
+ pass
79
+
80
+
81
+ class _AdminClientProxy(AdminAPI):
82
+ """
83
+ A lightweight facade that delegates all operations to the underlying ServerAPI (BaseClient).
84
+ The actual logic is in the specific client implementations (Embedded/Server/OceanBase).
85
+
86
+ Note: This is an internal class. Users should use the AdminClient() factory function.
87
+ """
88
+
89
+ _server: "BaseClient"
90
+
91
+ def __init__(self, server: "BaseClient") -> None:
92
+ """
93
+ Initialize admin client with a server implementation
94
+
95
+ Args:
96
+ server: The underlying client that implements the actual logic
97
+ """
98
+ self._server = server
99
+
100
+ def create_database(self, name: str, tenant: str = DEFAULT_TENANT) -> None:
101
+ """Proxy to server implementation"""
102
+ return self._server.create_database(name=name, tenant=tenant)
103
+
104
+ def get_database(self, name: str, tenant: str = DEFAULT_TENANT) -> Database:
105
+ """Proxy to server implementation"""
106
+ return self._server.get_database(name=name, tenant=tenant)
107
+
108
+ def delete_database(self, name: str, tenant: str = DEFAULT_TENANT) -> None:
109
+ """Proxy to server implementation"""
110
+ return self._server.delete_database(name=name, tenant=tenant)
111
+
112
+ def list_databases(
113
+ self,
114
+ limit: Optional[int] = None,
115
+ offset: Optional[int] = None,
116
+ tenant: str = DEFAULT_TENANT
117
+ ) -> Sequence[Database]:
118
+ """Proxy to server implementation"""
119
+ return self._server.list_databases(limit=limit, offset=offset, tenant=tenant)
120
+
121
+ def __repr__(self):
122
+ return f"<AdminClient server={self._server}>"
123
+
124
+ def __enter__(self):
125
+ """Context manager support - delegate to server"""
126
+ self._server.__enter__()
127
+ return self
128
+
129
+ def __exit__(self, exc_type, exc_val, exc_tb):
130
+ """Context manager support - delegate to server"""
131
+ return self._server.__exit__(exc_type, exc_val, exc_tb)
132
+
133
+
134
+ class _ClientProxy:
135
+ """
136
+ Internal client proxy for collection operations only.
137
+ Strictly separates collection management from database management.
138
+
139
+ Note: This is an internal class. Users should use the Client() factory function.
140
+ """
141
+
142
+ _server: "BaseClient"
143
+
144
+ def __init__(self, server: "BaseClient") -> None:
145
+ """
146
+ Initialize client with a server implementation
147
+
148
+ Args:
149
+ server: The underlying client that implements the actual logic
150
+ """
151
+ self._server = server
152
+
153
+ def create_collection(
154
+ self,
155
+ name: str,
156
+ dimension: Optional[int] = None,
157
+ **kwargs
158
+ ) -> "Collection":
159
+ """Proxy to server implementation - collection operations only"""
160
+ return self._server.create_collection(name=name, dimension=dimension, **kwargs)
161
+
162
+ def get_collection(self, name: str) -> "Collection":
163
+ """Proxy to server implementation - collection operations only"""
164
+ return self._server.get_collection(name=name)
165
+
166
+ def delete_collection(self, name: str) -> None:
167
+ """Proxy to server implementation - collection operations only"""
168
+ return self._server.delete_collection(name=name)
169
+
170
+ def list_collections(self) -> List["Collection"]:
171
+ """Proxy to server implementation - collection operations only"""
172
+ return self._server.list_collections()
173
+
174
+ def has_collection(self, name: str) -> bool:
175
+ """Proxy to server implementation - collection operations only"""
176
+ return self._server.has_collection(name=name)
177
+
178
+ def get_or_create_collection(
179
+ self,
180
+ name: str,
181
+ dimension: Optional[int] = None,
182
+ **kwargs
183
+ ) -> "Collection":
184
+ """Proxy to server implementation - collection operations only"""
185
+ return self._server.get_or_create_collection(name=name, dimension=dimension, **kwargs)
186
+
187
+ def count_collection(self) -> int:
188
+ """Proxy to server implementation - collection operations only"""
189
+ return self._server.count_collection()
190
+
191
+ def __repr__(self):
192
+ return f"<Client server={self._server}>"
193
+
194
+ def __enter__(self):
195
+ """Context manager support - delegate to server"""
196
+ self._server.__enter__()
197
+ return self
198
+
199
+ def __exit__(self, exc_type, exc_val, exc_tb):
200
+ """Context manager support - delegate to server"""
201
+ return self._server.__exit__(exc_type, exc_val, exc_tb)
202
+
@@ -0,0 +1,82 @@
1
+ """
2
+ Base connection interface definition
3
+ """
4
+ from abc import ABC, abstractmethod
5
+ from typing import Any
6
+
7
+ class _Transaction:
8
+ """
9
+ Internal transaction object
10
+ """
11
+ def __init__(self, connection: "BaseConnection"):
12
+ self._connection = connection
13
+
14
+ def __enter__(self):
15
+ return self
16
+
17
+ def __exit__(self, exc_type, exc_val, exc_tb):
18
+ if exc_type is not None:
19
+ self._connection.rollback()
20
+ else:
21
+ self._connection.commit()
22
+
23
+ class BaseConnection(ABC):
24
+ """
25
+ Abstract base class for connection management.
26
+ Defines unified connection interface for all clients.
27
+ """
28
+
29
+ # ==================== Connection Management ====================
30
+
31
+ @abstractmethod
32
+ def _ensure_connection(self) -> Any:
33
+ """Ensure connection is established (internal method)"""
34
+ pass
35
+
36
+ @abstractmethod
37
+ def is_connected(self) -> bool:
38
+ """Check connection status"""
39
+ pass
40
+
41
+ @abstractmethod
42
+ def _cleanup(self):
43
+ """Internal cleanup method to close connection and release resources"""
44
+ pass
45
+
46
+ @abstractmethod
47
+ def execute(self, sql: str) -> Any:
48
+ """Execute SQL statement (basic functionality)"""
49
+ pass
50
+
51
+
52
+ @abstractmethod
53
+ def get_raw_connection(self) -> Any:
54
+ """Get raw connection object"""
55
+ pass
56
+
57
+ @property
58
+ @abstractmethod
59
+ def mode(self) -> str:
60
+ """Return client mode (e.g., 'SeekdbEmbeddedClient', 'SeekdbServerClient', 'OceanBaseServerClient')"""
61
+ pass
62
+
63
+ # ==================== Context Manager ====================
64
+
65
+ def __enter__(self):
66
+ """Context manager support"""
67
+ return self
68
+
69
+ def __exit__(self, exc_type, exc_val, exc_tb):
70
+ """Context manager support: automatic resource cleanup"""
71
+ self._cleanup()
72
+
73
+ def __del__(self):
74
+ """Destructor: ensure connection is closed to prevent resource leaks"""
75
+ try:
76
+ if hasattr(self, '_connection') and self.is_connected():
77
+ self._cleanup()
78
+ except Exception:
79
+ # Ignore all exceptions in destructor
80
+ # Avoid issues during interpreter shutdown
81
+ pass
82
+