beaver-db 0.5.3__py3-none-any.whl → 0.6.0__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.
Potentially problematic release.
This version of beaver-db might be problematic. Click here for more details.
- beaver/core.py +14 -39
- beaver/dicts.py +101 -0
- {beaver_db-0.5.3.dist-info → beaver_db-0.6.0.dist-info}/METADATA +12 -8
- beaver_db-0.6.0.dist-info/RECORD +10 -0
- beaver_db-0.5.3.dist-info/RECORD +0 -9
- {beaver_db-0.5.3.dist-info → beaver_db-0.6.0.dist-info}/WHEEL +0 -0
- {beaver_db-0.5.3.dist-info → beaver_db-0.6.0.dist-info}/top_level.txt +0 -0
beaver/core.py
CHANGED
|
@@ -3,6 +3,7 @@ import sqlite3
|
|
|
3
3
|
import time
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
from .dicts import DictWrapper
|
|
6
7
|
from .lists import ListWrapper
|
|
7
8
|
from .subscribers import SubWrapper
|
|
8
9
|
from .collections import CollectionWrapper
|
|
@@ -30,22 +31,24 @@ class BeaverDB:
|
|
|
30
31
|
|
|
31
32
|
def _create_all_tables(self):
|
|
32
33
|
"""Initializes all required tables in the database file."""
|
|
33
|
-
self._create_kv_table()
|
|
34
34
|
self._create_pubsub_table()
|
|
35
35
|
self._create_list_table()
|
|
36
36
|
self._create_collections_table()
|
|
37
37
|
self._create_fts_table()
|
|
38
38
|
self._create_edges_table()
|
|
39
39
|
self._create_versions_table()
|
|
40
|
+
self._create_dict_table()
|
|
40
41
|
|
|
41
|
-
def
|
|
42
|
-
"""Creates the
|
|
42
|
+
def _create_dict_table(self):
|
|
43
|
+
"""Creates the namespaced dictionary table."""
|
|
43
44
|
with self._conn:
|
|
44
45
|
self._conn.execute(
|
|
45
46
|
"""
|
|
46
|
-
CREATE TABLE IF NOT EXISTS
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
CREATE TABLE IF NOT EXISTS beaver_dicts (
|
|
48
|
+
dict_name TEXT NOT NULL,
|
|
49
|
+
key TEXT NOT NULL,
|
|
50
|
+
value TEXT NOT NULL,
|
|
51
|
+
PRIMARY KEY (dict_name, key)
|
|
49
52
|
)
|
|
50
53
|
"""
|
|
51
54
|
)
|
|
@@ -148,39 +151,11 @@ class BeaverDB:
|
|
|
148
151
|
|
|
149
152
|
# --- Factory and Passthrough Methods ---
|
|
150
153
|
|
|
151
|
-
def
|
|
152
|
-
"""
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if not isinstance(key, str):
|
|
157
|
-
raise TypeError("Key must be a string.")
|
|
158
|
-
|
|
159
|
-
try:
|
|
160
|
-
json_value = json.dumps(value)
|
|
161
|
-
except TypeError as e:
|
|
162
|
-
raise TypeError("Value must be JSON-serializable.") from e
|
|
163
|
-
|
|
164
|
-
with self._conn:
|
|
165
|
-
self._conn.execute(
|
|
166
|
-
"INSERT OR REPLACE INTO _beaver_kv_store (key, value) VALUES (?, ?)",
|
|
167
|
-
(key, json_value),
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
def get(self, key: str) -> Any:
|
|
171
|
-
"""
|
|
172
|
-
Retrieves a value for a given key.
|
|
173
|
-
This operation is synchronous.
|
|
174
|
-
"""
|
|
175
|
-
if not isinstance(key, str):
|
|
176
|
-
raise TypeError("Key must be a string.")
|
|
177
|
-
|
|
178
|
-
cursor = self._conn.cursor()
|
|
179
|
-
cursor.execute("SELECT value FROM _beaver_kv_store WHERE key = ?", (key,))
|
|
180
|
-
result = cursor.fetchone()
|
|
181
|
-
cursor.close()
|
|
182
|
-
|
|
183
|
-
return json.loads(result["value"]) if result else None
|
|
154
|
+
def dict(self, name: str) -> DictWrapper:
|
|
155
|
+
"""Returns a wrapper object for interacting with a named dictionary."""
|
|
156
|
+
if not isinstance(name, str) or not name:
|
|
157
|
+
raise TypeError("Dictionary name must be a non-empty string.")
|
|
158
|
+
return DictWrapper(name, self._conn)
|
|
184
159
|
|
|
185
160
|
def list(self, name: str) -> ListWrapper:
|
|
186
161
|
"""Returns a wrapper object for interacting with a named list."""
|
beaver/dicts.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sqlite3
|
|
3
|
+
from typing import Any, Iterator, Tuple
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DictWrapper:
|
|
7
|
+
"""A wrapper providing a Pythonic interface to a dictionary in the database."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, name: str, conn: sqlite3.Connection):
|
|
10
|
+
self._name = name
|
|
11
|
+
self._conn = conn
|
|
12
|
+
|
|
13
|
+
def set(self, key: str, value: Any):
|
|
14
|
+
"""Sets a value for a key in the dictionary."""
|
|
15
|
+
self.__setitem__(key, value)
|
|
16
|
+
|
|
17
|
+
def __setitem__(self, key: str, value: Any):
|
|
18
|
+
"""Sets a value for a key (e.g., `my_dict[key] = value`)."""
|
|
19
|
+
with self._conn:
|
|
20
|
+
self._conn.execute(
|
|
21
|
+
"INSERT OR REPLACE INTO beaver_dicts (dict_name, key, value) VALUES (?, ?, ?)",
|
|
22
|
+
(self._name, key, json.dumps(value)),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
26
|
+
"""Gets a value for a key, with a default if it doesn't exist."""
|
|
27
|
+
try:
|
|
28
|
+
return self[key]
|
|
29
|
+
except KeyError:
|
|
30
|
+
return default
|
|
31
|
+
|
|
32
|
+
def __getitem__(self, key: str) -> Any:
|
|
33
|
+
"""Retrieves a value for a given key (e.g., `my_dict[key]`)."""
|
|
34
|
+
cursor = self._conn.cursor()
|
|
35
|
+
cursor.execute(
|
|
36
|
+
"SELECT value FROM beaver_dicts WHERE dict_name = ? AND key = ?",
|
|
37
|
+
(self._name, key),
|
|
38
|
+
)
|
|
39
|
+
result = cursor.fetchone()
|
|
40
|
+
cursor.close()
|
|
41
|
+
if result is None:
|
|
42
|
+
raise KeyError(f"Key '{key}' not found in dictionary '{self._name}'")
|
|
43
|
+
return json.loads(result["value"])
|
|
44
|
+
|
|
45
|
+
def __delitem__(self, key: str):
|
|
46
|
+
"""Deletes a key-value pair (e.g., `del my_dict[key]`)."""
|
|
47
|
+
with self._conn:
|
|
48
|
+
cursor = self._conn.cursor()
|
|
49
|
+
cursor.execute(
|
|
50
|
+
"DELETE FROM beaver_dicts WHERE dict_name = ? AND key = ?",
|
|
51
|
+
(self._name, key),
|
|
52
|
+
)
|
|
53
|
+
if cursor.rowcount == 0:
|
|
54
|
+
raise KeyError(f"Key '{key}' not found in dictionary '{self._name}'")
|
|
55
|
+
|
|
56
|
+
def __len__(self) -> int:
|
|
57
|
+
"""Returns the number of items in the dictionary."""
|
|
58
|
+
cursor = self._conn.cursor()
|
|
59
|
+
cursor.execute(
|
|
60
|
+
"SELECT COUNT(*) FROM beaver_dicts WHERE dict_name = ?", (self._name,)
|
|
61
|
+
)
|
|
62
|
+
count = cursor.fetchone()[0]
|
|
63
|
+
cursor.close()
|
|
64
|
+
return count
|
|
65
|
+
|
|
66
|
+
def __iter__(self) -> Iterator[str]:
|
|
67
|
+
"""Returns an iterator over the keys of the dictionary."""
|
|
68
|
+
return self.keys()
|
|
69
|
+
|
|
70
|
+
def keys(self) -> Iterator[str]:
|
|
71
|
+
"""Returns an iterator over the dictionary's keys."""
|
|
72
|
+
cursor = self._conn.cursor()
|
|
73
|
+
cursor.execute(
|
|
74
|
+
"SELECT key FROM beaver_dicts WHERE dict_name = ?", (self._name,)
|
|
75
|
+
)
|
|
76
|
+
for row in cursor:
|
|
77
|
+
yield row["key"]
|
|
78
|
+
cursor.close()
|
|
79
|
+
|
|
80
|
+
def values(self) -> Iterator[Any]:
|
|
81
|
+
"""Returns an iterator over the dictionary's values."""
|
|
82
|
+
cursor = self._conn.cursor()
|
|
83
|
+
cursor.execute(
|
|
84
|
+
"SELECT value FROM beaver_dicts WHERE dict_name = ?", (self._name,)
|
|
85
|
+
)
|
|
86
|
+
for row in cursor:
|
|
87
|
+
yield json.loads(row["value"])
|
|
88
|
+
cursor.close()
|
|
89
|
+
|
|
90
|
+
def items(self) -> Iterator[Tuple[str, Any]]:
|
|
91
|
+
"""Returns an iterator over the dictionary's items (key-value pairs)."""
|
|
92
|
+
cursor = self._conn.cursor()
|
|
93
|
+
cursor.execute(
|
|
94
|
+
"SELECT key, value FROM beaver_dicts WHERE dict_name = ?", (self._name,)
|
|
95
|
+
)
|
|
96
|
+
for row in cursor:
|
|
97
|
+
yield (row["key"], json.loads(row["value"]))
|
|
98
|
+
cursor.close()
|
|
99
|
+
|
|
100
|
+
def __repr__(self) -> str:
|
|
101
|
+
return f"DictWrapper(name='{self._name}')"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: beaver-db
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Fast, embedded, and multi-modal DB based on SQLite for AI-powered applications.
|
|
5
5
|
Requires-Python: >=3.13
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -26,7 +26,7 @@ A fast, single-file, multi-modal database for Python, built with the standard `s
|
|
|
26
26
|
## Core Features
|
|
27
27
|
|
|
28
28
|
- **Synchronous Pub/Sub**: A simple, thread-safe, Redis-like publish-subscribe system for real-time messaging.
|
|
29
|
-
- **
|
|
29
|
+
- **Namespaced Key-Value Dictionaries**: A Pythonic, dictionary-like interface for storing any JSON-serializable object within separate namespaces.
|
|
30
30
|
- **Pythonic List Management**: A fluent, Redis-like interface for managing persistent, ordered lists.
|
|
31
31
|
- **Efficient Vector Storage & Search**: Store vector embeddings and perform fast approximate nearest neighbor searches using an in-memory k-d tree.
|
|
32
32
|
- **Full-Text Search**: Automatically index and search through document metadata using SQLite's powerful FTS5 engine.
|
|
@@ -51,17 +51,21 @@ from beaver import BeaverDB, Document
|
|
|
51
51
|
db = BeaverDB("my_application.db")
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
###
|
|
54
|
+
### Namespaced Dictionaries
|
|
55
55
|
|
|
56
|
-
Use `
|
|
56
|
+
Use `db.dict()` to get a dictionary-like object for a specific namespace. The value can be any JSON-encodable object.
|
|
57
57
|
|
|
58
58
|
```python
|
|
59
|
-
#
|
|
60
|
-
db.
|
|
59
|
+
# Get a handle to the 'app_config' namespace
|
|
60
|
+
config = db.dict("app_config")
|
|
61
|
+
|
|
62
|
+
# Set values using standard dictionary syntax
|
|
63
|
+
config["theme"] = "dark"
|
|
64
|
+
config["user_id"] = 123
|
|
61
65
|
|
|
62
66
|
# Get a value
|
|
63
|
-
|
|
64
|
-
print(f"Theme: {
|
|
67
|
+
theme = config.get("theme")
|
|
68
|
+
print(f"Theme: {theme}") # Output: Theme: dark
|
|
65
69
|
```
|
|
66
70
|
|
|
67
71
|
### List Management
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
beaver/__init__.py,sha256=-z5Gj6YKMOswpJOOn5Gej8z5i6k3c0Xs00DIYLA-bMI,75
|
|
2
|
+
beaver/collections.py,sha256=II26aeXelbtfs0zkcHImvPDtoyDhPCCP8bsosEqoKvw,15064
|
|
3
|
+
beaver/core.py,sha256=CDAnIF1InFLb_JxMiO_bWo8coXIemhB68_WBkSTTKzU,6624
|
|
4
|
+
beaver/dicts.py,sha256=XgkflJegQAY0YnkxnwWw_LGAeRtGFHzGtuaLAK2GfZg,3558
|
|
5
|
+
beaver/lists.py,sha256=JG1JOkaYCUldADUzPJhaNi93w-k3S8mUzcCw574uht4,5915
|
|
6
|
+
beaver/subscribers.py,sha256=tCty2iDbeE9IXcPicbxj2CB5gqfLufMB9-nLQwqNBUU,1944
|
|
7
|
+
beaver_db-0.6.0.dist-info/METADATA,sha256=TwpfIsOef1GXpRdCpVEaQLVWtK1Ql11M9L7QLpSkphk,6265
|
|
8
|
+
beaver_db-0.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
beaver_db-0.6.0.dist-info/top_level.txt,sha256=FxA4XnX5Qm5VudEXCduFriqi4dQmDWpQ64d7g69VQKI,7
|
|
10
|
+
beaver_db-0.6.0.dist-info/RECORD,,
|
beaver_db-0.5.3.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
beaver/__init__.py,sha256=-z5Gj6YKMOswpJOOn5Gej8z5i6k3c0Xs00DIYLA-bMI,75
|
|
2
|
-
beaver/collections.py,sha256=II26aeXelbtfs0zkcHImvPDtoyDhPCCP8bsosEqoKvw,15064
|
|
3
|
-
beaver/core.py,sha256=sk0Z_k7EcORe6bN8CfPukGX7eAfmCGSX_B37KpJmQJ4,7279
|
|
4
|
-
beaver/lists.py,sha256=JG1JOkaYCUldADUzPJhaNi93w-k3S8mUzcCw574uht4,5915
|
|
5
|
-
beaver/subscribers.py,sha256=tCty2iDbeE9IXcPicbxj2CB5gqfLufMB9-nLQwqNBUU,1944
|
|
6
|
-
beaver_db-0.5.3.dist-info/METADATA,sha256=MAtHrjLqdqBKTPzHGJiX4gORwcwQl38pZXr3mSFWsk4,6105
|
|
7
|
-
beaver_db-0.5.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
beaver_db-0.5.3.dist-info/top_level.txt,sha256=FxA4XnX5Qm5VudEXCduFriqi4dQmDWpQ64d7g69VQKI,7
|
|
9
|
-
beaver_db-0.5.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|