beaver-db 0.5.2__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/collections.py +2 -2
- beaver/core.py +14 -39
- beaver/dicts.py +101 -0
- {beaver_db-0.5.2.dist-info → beaver_db-0.6.0.dist-info}/METADATA +18 -10
- beaver_db-0.6.0.dist-info/RECORD +10 -0
- beaver_db-0.5.2.dist-info/RECORD +0 -9
- {beaver_db-0.5.2.dist-info → beaver_db-0.6.0.dist-info}/WHEEL +0 -0
- {beaver_db-0.5.2.dist-info → beaver_db-0.6.0.dist-info}/top_level.txt +0 -0
beaver/collections.py
CHANGED
|
@@ -331,7 +331,7 @@ class CollectionWrapper:
|
|
|
331
331
|
|
|
332
332
|
|
|
333
333
|
def rerank(
|
|
334
|
-
results: list[
|
|
334
|
+
*results: list[Document],
|
|
335
335
|
weights: list[float] | None = None,
|
|
336
336
|
k: int = 60
|
|
337
337
|
) -> list[Document]:
|
|
@@ -340,7 +340,7 @@ def rerank(
|
|
|
340
340
|
This function is specifically designed to work with beaver.collections.Document objects.
|
|
341
341
|
|
|
342
342
|
Args:
|
|
343
|
-
results (list[
|
|
343
|
+
results (sequence of list[Document]): A sequence of search result lists, where each
|
|
344
344
|
inner list contains Document objects.
|
|
345
345
|
weights (list[float], optional): A list of weights corresponding to each
|
|
346
346
|
result list. If None, all lists are weighted equally. Defaults to None.
|
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
|
|
@@ -94,14 +98,18 @@ docs.index(doc)
|
|
|
94
98
|
|
|
95
99
|
# 1. Perform a vector search to find semantically similar documents
|
|
96
100
|
query_vector = [0.7, 0.2, 0.2]
|
|
97
|
-
vector_results = docs.search(vector=query_vector, top_k=
|
|
101
|
+
vector_results = docs.search(vector=query_vector, top_k=3)
|
|
98
102
|
top_doc, distance = vector_results[0]
|
|
99
103
|
print(f"Vector Search Result: {top_doc.content} (distance: {distance:.2f})")
|
|
100
104
|
|
|
101
105
|
# 2. Perform a full-text search to find documents with specific words
|
|
102
|
-
text_results = docs.match(query="database", top_k=
|
|
106
|
+
text_results = docs.match(query="database", top_k=3)
|
|
103
107
|
top_doc, rank = text_results[0]
|
|
104
108
|
print(f"Full-Text Search Result: {top_doc.content} (rank: {rank:.2f})")
|
|
109
|
+
|
|
110
|
+
# 3. Combine both vector and text search for refined results
|
|
111
|
+
from beaver.collections import rerank
|
|
112
|
+
combined_results = rerank([d for d,_ in vector_results], [d for d,_ in text_results], weights=[2,1])
|
|
105
113
|
```
|
|
106
114
|
|
|
107
115
|
### Graph Traversal
|
|
@@ -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.2.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
beaver/__init__.py,sha256=-z5Gj6YKMOswpJOOn5Gej8z5i6k3c0Xs00DIYLA-bMI,75
|
|
2
|
-
beaver/collections.py,sha256=4rdGMTD7ex4SQUH52WIHOZhOdeWe7Nqvm9TPg7flv_g,15059
|
|
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.2.dist-info/METADATA,sha256=ij6szwN6Ee0MutGlFgszF2vqddR5y8OlA9O4R5Y4Nbo,5904
|
|
7
|
-
beaver_db-0.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
beaver_db-0.5.2.dist-info/top_level.txt,sha256=FxA4XnX5Qm5VudEXCduFriqi4dQmDWpQ64d7g69VQKI,7
|
|
9
|
-
beaver_db-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|