latticedb 0.2.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.
- latticedb-0.2.0/MANIFEST.in +4 -0
- latticedb-0.2.0/PKG-INFO +353 -0
- latticedb-0.2.0/README.md +321 -0
- latticedb-0.2.0/pyproject.toml +71 -0
- latticedb-0.2.0/setup.cfg +4 -0
- latticedb-0.2.0/src/latticedb/__init__.py +75 -0
- latticedb-0.2.0/src/latticedb/_bindings.py +810 -0
- latticedb-0.2.0/src/latticedb/database.py +505 -0
- latticedb-0.2.0/src/latticedb/embedding.py +144 -0
- latticedb-0.2.0/src/latticedb/py.typed +1 -0
- latticedb-0.2.0/src/latticedb/transaction.py +595 -0
- latticedb-0.2.0/src/latticedb/types.py +141 -0
- latticedb-0.2.0/src/latticedb.egg-info/PKG-INFO +353 -0
- latticedb-0.2.0/src/latticedb.egg-info/SOURCES.txt +17 -0
- latticedb-0.2.0/src/latticedb.egg-info/dependency_links.txt +1 -0
- latticedb-0.2.0/src/latticedb.egg-info/requires.txt +7 -0
- latticedb-0.2.0/src/latticedb.egg-info/top_level.txt +1 -0
- latticedb-0.2.0/tests/test_basic.py +227 -0
- latticedb-0.2.0/tests/test_integration.py +808 -0
latticedb-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: latticedb
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Embedded knowledge graph database for AI and RAG applications
|
|
5
|
+
Author-email: Jeff Hajewski <jeff@latticedb.dev>
|
|
6
|
+
Maintainer-email: Jeff Hajewski <jeff@latticedb.dev>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/jeffhajewski/latticedb
|
|
9
|
+
Project-URL: Documentation, https://latticedb.dev
|
|
10
|
+
Project-URL: Repository, https://github.com/jeffhajewski/latticedb
|
|
11
|
+
Project-URL: Issues, https://github.com/jeffhajewski/latticedb/issues
|
|
12
|
+
Keywords: database,graph,vector,embeddings,rag,ai,knowledge-graph
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Database
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
Requires-Dist: numpy>=1.20.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
32
|
+
|
|
33
|
+
# Lattice Python Bindings
|
|
34
|
+
|
|
35
|
+
Python bindings for [LatticeDB](https://github.com/latticedb/latticedb), an embedded knowledge graph database for AI/RAG applications.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
### From Source
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Build the native library first
|
|
43
|
+
cd /path/to/latticedb
|
|
44
|
+
zig build shared
|
|
45
|
+
|
|
46
|
+
# Install the Python package
|
|
47
|
+
cd bindings/python
|
|
48
|
+
pip install -e .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from lattice import Database
|
|
55
|
+
|
|
56
|
+
# Create a new database
|
|
57
|
+
with Database("mydb.ltdb", create=True) as db:
|
|
58
|
+
# Write transaction
|
|
59
|
+
with db.write() as txn:
|
|
60
|
+
# Create nodes with properties
|
|
61
|
+
alice = txn.create_node(
|
|
62
|
+
labels=["Person"],
|
|
63
|
+
properties={"name": "Alice", "age": 30}
|
|
64
|
+
)
|
|
65
|
+
bob = txn.create_node(
|
|
66
|
+
labels=["Person"],
|
|
67
|
+
properties={"name": "Bob", "age": 25}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Create relationships
|
|
71
|
+
txn.create_edge(alice.id, bob.id, "KNOWS")
|
|
72
|
+
|
|
73
|
+
txn.commit()
|
|
74
|
+
|
|
75
|
+
# Query with Cypher
|
|
76
|
+
result = db.query("MATCH (n:Person) RETURN n.name")
|
|
77
|
+
for row in result:
|
|
78
|
+
print(row) # {'n': 'Alice'}, {'n': 'Bob'}
|
|
79
|
+
|
|
80
|
+
# Query with parameters (safe from injection)
|
|
81
|
+
result = db.query(
|
|
82
|
+
"MATCH (n:Person) WHERE n.name = $name RETURN n",
|
|
83
|
+
parameters={"name": "Alice"}
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## API Reference
|
|
88
|
+
|
|
89
|
+
### Database
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
Database(
|
|
93
|
+
path: str | Path,
|
|
94
|
+
*,
|
|
95
|
+
create: bool = False, # Create if doesn't exist
|
|
96
|
+
read_only: bool = False, # Open in read-only mode
|
|
97
|
+
cache_size_mb: int = 100, # Page cache size
|
|
98
|
+
enable_vector: bool = False, # Enable vector storage
|
|
99
|
+
vector_dimensions: int = 128 # Vector dimensions
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Methods
|
|
104
|
+
|
|
105
|
+
- `open()` - Open the database connection
|
|
106
|
+
- `close()` - Close the database connection
|
|
107
|
+
- `read()` - Start a read-only transaction (context manager)
|
|
108
|
+
- `write()` - Start a read-write transaction (context manager)
|
|
109
|
+
- `query(cypher: str, parameters: dict = None)` - Execute a Cypher query with optional parameters
|
|
110
|
+
|
|
111
|
+
### Transaction
|
|
112
|
+
|
|
113
|
+
#### Read Operations
|
|
114
|
+
|
|
115
|
+
- `is_read_only` - True if read-only transaction
|
|
116
|
+
- `is_active` - True if transaction is still active
|
|
117
|
+
- `get_node(node_id: int)` - Get a node by ID, returns `Node` or `None`
|
|
118
|
+
- `get_property(node_id: int, key: str)` - Get a property value, returns value or `None`
|
|
119
|
+
- `node_exists(node_id: int)` - Check if a node exists, returns `True` or `False`
|
|
120
|
+
|
|
121
|
+
#### Write Operations
|
|
122
|
+
|
|
123
|
+
- `create_node(labels: list[str], properties: dict = None)` - Create a node with labels and optional properties
|
|
124
|
+
- `delete_node(node_id: int)` - Delete a node
|
|
125
|
+
- `set_property(node_id: int, key: str, value)` - Set a property on a node
|
|
126
|
+
- `set_vector(node_id: int, key: str, vector: np.ndarray)` - Set a vector embedding
|
|
127
|
+
- `create_edge(source_id: int, target_id: int, edge_type: str)` - Create an edge
|
|
128
|
+
- `delete_edge(source_id: int, target_id: int, edge_type: str)` - Delete an edge
|
|
129
|
+
- `commit()` - Commit the transaction
|
|
130
|
+
- `rollback()` - Rollback the transaction
|
|
131
|
+
|
|
132
|
+
### Querying
|
|
133
|
+
|
|
134
|
+
#### Basic Queries
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# Simple match
|
|
138
|
+
result = db.query("MATCH (n:Person) RETURN n")
|
|
139
|
+
|
|
140
|
+
# Return properties
|
|
141
|
+
result = db.query("MATCH (n:Person) RETURN n.name")
|
|
142
|
+
|
|
143
|
+
# With WHERE clause
|
|
144
|
+
result = db.query("MATCH (n:Person) WHERE n.age > 25 RETURN n.name")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Data Mutation
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# Create nodes and relationships
|
|
151
|
+
db.query("CREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})")
|
|
152
|
+
|
|
153
|
+
# Update properties
|
|
154
|
+
db.query("MATCH (n:Person {name: 'Alice'}) SET n.age = 31, n.city = 'NYC'")
|
|
155
|
+
|
|
156
|
+
# Add labels
|
|
157
|
+
db.query("MATCH (n:Person {name: 'Alice'}) SET n:Admin:Verified")
|
|
158
|
+
|
|
159
|
+
# Remove properties and labels
|
|
160
|
+
db.query("MATCH (n:Person {name: 'Alice'}) REMOVE n.city, n:Verified")
|
|
161
|
+
|
|
162
|
+
# Delete nodes (DETACH removes connected edges)
|
|
163
|
+
db.query("MATCH (n:Person {name: 'Bob'}) DETACH DELETE n")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Parameterized Queries
|
|
167
|
+
|
|
168
|
+
Use parameters to safely pass values into queries:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
# String parameter
|
|
172
|
+
result = db.query(
|
|
173
|
+
"MATCH (n:Person) WHERE n.name = $name RETURN n",
|
|
174
|
+
parameters={"name": "Alice"}
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Integer parameter
|
|
178
|
+
result = db.query(
|
|
179
|
+
"MATCH (n:Person) WHERE n.age = $age RETURN n.name",
|
|
180
|
+
parameters={"age": 30}
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Multiple parameters
|
|
184
|
+
result = db.query(
|
|
185
|
+
"MATCH (n:Person) WHERE n.name = $name AND n.age > $min_age RETURN n",
|
|
186
|
+
parameters={"name": "Alice", "min_age": 20}
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Vector parameter (requires numpy)
|
|
190
|
+
import numpy as np
|
|
191
|
+
query_vec = np.random.rand(384).astype(np.float32)
|
|
192
|
+
result = db.query(
|
|
193
|
+
"MATCH (n:Document) WHERE n.embedding <=> $vec < 0.5 RETURN n",
|
|
194
|
+
parameters={"vec": query_vec}
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Working with Results
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
result = db.query("MATCH (n:Person) RETURN n.name")
|
|
202
|
+
|
|
203
|
+
# Get column names
|
|
204
|
+
print(result.columns) # ['n']
|
|
205
|
+
|
|
206
|
+
# Iterate rows
|
|
207
|
+
for row in result:
|
|
208
|
+
print(row) # {'n': 'Alice'}
|
|
209
|
+
|
|
210
|
+
# Get all rows as list
|
|
211
|
+
rows = list(result)
|
|
212
|
+
|
|
213
|
+
# Get row count
|
|
214
|
+
print(len(result))
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Reading Node Data
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
with db.read() as txn:
|
|
221
|
+
# Get a node by ID
|
|
222
|
+
node = txn.get_node(node_id)
|
|
223
|
+
if node:
|
|
224
|
+
print(f"ID: {node.id}")
|
|
225
|
+
print(f"Labels: {node.labels}")
|
|
226
|
+
|
|
227
|
+
# Get individual properties
|
|
228
|
+
name = txn.get_property(node_id, "name")
|
|
229
|
+
age = txn.get_property(node_id, "age")
|
|
230
|
+
|
|
231
|
+
# Returns None if property doesn't exist
|
|
232
|
+
unknown = txn.get_property(node_id, "nonexistent") # None
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Vector Operations
|
|
236
|
+
|
|
237
|
+
To use vector embeddings, enable vector storage when opening the database:
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
import numpy as np
|
|
241
|
+
|
|
242
|
+
with Database("mydb.ltdb", create=True, enable_vector=True, vector_dimensions=384) as db:
|
|
243
|
+
# Store vectors
|
|
244
|
+
with db.write() as txn:
|
|
245
|
+
node1 = txn.create_node(labels=["Document"])
|
|
246
|
+
txn.set_property(node1.id, "title", "Introduction to ML")
|
|
247
|
+
embedding1 = np.random.rand(384).astype(np.float32)
|
|
248
|
+
txn.set_vector(node1.id, "embedding", embedding1)
|
|
249
|
+
|
|
250
|
+
node2 = txn.create_node(labels=["Document"])
|
|
251
|
+
txn.set_property(node2.id, "title", "Deep Learning Guide")
|
|
252
|
+
embedding2 = np.random.rand(384).astype(np.float32)
|
|
253
|
+
txn.set_vector(node2.id, "embedding", embedding2)
|
|
254
|
+
|
|
255
|
+
txn.commit()
|
|
256
|
+
|
|
257
|
+
# Search for similar vectors (HNSW approximate nearest neighbor)
|
|
258
|
+
query_vector = np.random.rand(384).astype(np.float32)
|
|
259
|
+
results = db.vector_search(query_vector, k=10, ef_search=64)
|
|
260
|
+
|
|
261
|
+
for result in results:
|
|
262
|
+
print(f"Node {result.node_id}: distance={result.distance:.4f}")
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Vector Search Parameters
|
|
266
|
+
|
|
267
|
+
- `vector`: Query vector (numpy array of float32)
|
|
268
|
+
- `k`: Number of nearest neighbors to return (default: 10)
|
|
269
|
+
- `ef_search`: HNSW exploration factor - higher values are slower but more accurate (default: 64)
|
|
270
|
+
|
|
271
|
+
### Full-Text Search
|
|
272
|
+
|
|
273
|
+
Index text content and search with BM25 scoring:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
with Database("mydb.ltdb", create=True) as db:
|
|
277
|
+
# Index documents
|
|
278
|
+
with db.write() as txn:
|
|
279
|
+
doc1 = txn.create_node(labels=["Document"])
|
|
280
|
+
txn.set_property(doc1.id, "title", "Introduction to ML")
|
|
281
|
+
txn.fts_index(doc1.id, "Machine learning is a subset of artificial intelligence")
|
|
282
|
+
|
|
283
|
+
doc2 = txn.create_node(labels=["Document"])
|
|
284
|
+
txn.set_property(doc2.id, "title", "Deep Learning Guide")
|
|
285
|
+
txn.fts_index(doc2.id, "Deep learning uses neural networks")
|
|
286
|
+
|
|
287
|
+
txn.commit()
|
|
288
|
+
|
|
289
|
+
# Search for documents
|
|
290
|
+
results = db.fts_search("machine learning", limit=10)
|
|
291
|
+
|
|
292
|
+
for result in results:
|
|
293
|
+
print(f"Node {result.node_id}: score={result.score:.4f}")
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### FTS Search Parameters
|
|
297
|
+
|
|
298
|
+
- `query`: Search query text
|
|
299
|
+
- `limit`: Maximum number of results to return (default: 10)
|
|
300
|
+
|
|
301
|
+
## Supported Property Types
|
|
302
|
+
|
|
303
|
+
- `None` - Null value
|
|
304
|
+
- `bool` - Boolean
|
|
305
|
+
- `int` - 64-bit integer
|
|
306
|
+
- `float` - 64-bit float
|
|
307
|
+
- `str` - UTF-8 string
|
|
308
|
+
- `bytes` - Binary data
|
|
309
|
+
|
|
310
|
+
## Error Handling
|
|
311
|
+
|
|
312
|
+
The library raises typed exceptions:
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
from lattice import LatticeError, LatticeNotFoundError, LatticeIOError
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
with Database("nonexistent.ltdb") as db:
|
|
319
|
+
pass
|
|
320
|
+
except LatticeNotFoundError:
|
|
321
|
+
print("Database not found")
|
|
322
|
+
except LatticeIOError:
|
|
323
|
+
print("I/O error")
|
|
324
|
+
except LatticeError as e:
|
|
325
|
+
print(f"Error: {e}")
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Utilities
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
from lattice import version, library_available
|
|
332
|
+
|
|
333
|
+
# Check if the native library is available
|
|
334
|
+
if library_available():
|
|
335
|
+
print("Library found")
|
|
336
|
+
|
|
337
|
+
# Get the native library version
|
|
338
|
+
print(f"Lattice version: {version()}")
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Requirements
|
|
342
|
+
|
|
343
|
+
- Python 3.9+
|
|
344
|
+
- NumPy (optional, for vector operations)
|
|
345
|
+
- The native LatticeDB library (`liblattice.dylib` / `liblattice.so`)
|
|
346
|
+
|
|
347
|
+
## Building from Source
|
|
348
|
+
|
|
349
|
+
See [CONTRIBUTING.md](../../CONTRIBUTING.md) for build instructions.
|
|
350
|
+
|
|
351
|
+
## License
|
|
352
|
+
|
|
353
|
+
Same license as LatticeDB.
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# Lattice Python Bindings
|
|
2
|
+
|
|
3
|
+
Python bindings for [LatticeDB](https://github.com/latticedb/latticedb), an embedded knowledge graph database for AI/RAG applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### From Source
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Build the native library first
|
|
11
|
+
cd /path/to/latticedb
|
|
12
|
+
zig build shared
|
|
13
|
+
|
|
14
|
+
# Install the Python package
|
|
15
|
+
cd bindings/python
|
|
16
|
+
pip install -e .
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from lattice import Database
|
|
23
|
+
|
|
24
|
+
# Create a new database
|
|
25
|
+
with Database("mydb.ltdb", create=True) as db:
|
|
26
|
+
# Write transaction
|
|
27
|
+
with db.write() as txn:
|
|
28
|
+
# Create nodes with properties
|
|
29
|
+
alice = txn.create_node(
|
|
30
|
+
labels=["Person"],
|
|
31
|
+
properties={"name": "Alice", "age": 30}
|
|
32
|
+
)
|
|
33
|
+
bob = txn.create_node(
|
|
34
|
+
labels=["Person"],
|
|
35
|
+
properties={"name": "Bob", "age": 25}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Create relationships
|
|
39
|
+
txn.create_edge(alice.id, bob.id, "KNOWS")
|
|
40
|
+
|
|
41
|
+
txn.commit()
|
|
42
|
+
|
|
43
|
+
# Query with Cypher
|
|
44
|
+
result = db.query("MATCH (n:Person) RETURN n.name")
|
|
45
|
+
for row in result:
|
|
46
|
+
print(row) # {'n': 'Alice'}, {'n': 'Bob'}
|
|
47
|
+
|
|
48
|
+
# Query with parameters (safe from injection)
|
|
49
|
+
result = db.query(
|
|
50
|
+
"MATCH (n:Person) WHERE n.name = $name RETURN n",
|
|
51
|
+
parameters={"name": "Alice"}
|
|
52
|
+
)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## API Reference
|
|
56
|
+
|
|
57
|
+
### Database
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
Database(
|
|
61
|
+
path: str | Path,
|
|
62
|
+
*,
|
|
63
|
+
create: bool = False, # Create if doesn't exist
|
|
64
|
+
read_only: bool = False, # Open in read-only mode
|
|
65
|
+
cache_size_mb: int = 100, # Page cache size
|
|
66
|
+
enable_vector: bool = False, # Enable vector storage
|
|
67
|
+
vector_dimensions: int = 128 # Vector dimensions
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Methods
|
|
72
|
+
|
|
73
|
+
- `open()` - Open the database connection
|
|
74
|
+
- `close()` - Close the database connection
|
|
75
|
+
- `read()` - Start a read-only transaction (context manager)
|
|
76
|
+
- `write()` - Start a read-write transaction (context manager)
|
|
77
|
+
- `query(cypher: str, parameters: dict = None)` - Execute a Cypher query with optional parameters
|
|
78
|
+
|
|
79
|
+
### Transaction
|
|
80
|
+
|
|
81
|
+
#### Read Operations
|
|
82
|
+
|
|
83
|
+
- `is_read_only` - True if read-only transaction
|
|
84
|
+
- `is_active` - True if transaction is still active
|
|
85
|
+
- `get_node(node_id: int)` - Get a node by ID, returns `Node` or `None`
|
|
86
|
+
- `get_property(node_id: int, key: str)` - Get a property value, returns value or `None`
|
|
87
|
+
- `node_exists(node_id: int)` - Check if a node exists, returns `True` or `False`
|
|
88
|
+
|
|
89
|
+
#### Write Operations
|
|
90
|
+
|
|
91
|
+
- `create_node(labels: list[str], properties: dict = None)` - Create a node with labels and optional properties
|
|
92
|
+
- `delete_node(node_id: int)` - Delete a node
|
|
93
|
+
- `set_property(node_id: int, key: str, value)` - Set a property on a node
|
|
94
|
+
- `set_vector(node_id: int, key: str, vector: np.ndarray)` - Set a vector embedding
|
|
95
|
+
- `create_edge(source_id: int, target_id: int, edge_type: str)` - Create an edge
|
|
96
|
+
- `delete_edge(source_id: int, target_id: int, edge_type: str)` - Delete an edge
|
|
97
|
+
- `commit()` - Commit the transaction
|
|
98
|
+
- `rollback()` - Rollback the transaction
|
|
99
|
+
|
|
100
|
+
### Querying
|
|
101
|
+
|
|
102
|
+
#### Basic Queries
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Simple match
|
|
106
|
+
result = db.query("MATCH (n:Person) RETURN n")
|
|
107
|
+
|
|
108
|
+
# Return properties
|
|
109
|
+
result = db.query("MATCH (n:Person) RETURN n.name")
|
|
110
|
+
|
|
111
|
+
# With WHERE clause
|
|
112
|
+
result = db.query("MATCH (n:Person) WHERE n.age > 25 RETURN n.name")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### Data Mutation
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Create nodes and relationships
|
|
119
|
+
db.query("CREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})")
|
|
120
|
+
|
|
121
|
+
# Update properties
|
|
122
|
+
db.query("MATCH (n:Person {name: 'Alice'}) SET n.age = 31, n.city = 'NYC'")
|
|
123
|
+
|
|
124
|
+
# Add labels
|
|
125
|
+
db.query("MATCH (n:Person {name: 'Alice'}) SET n:Admin:Verified")
|
|
126
|
+
|
|
127
|
+
# Remove properties and labels
|
|
128
|
+
db.query("MATCH (n:Person {name: 'Alice'}) REMOVE n.city, n:Verified")
|
|
129
|
+
|
|
130
|
+
# Delete nodes (DETACH removes connected edges)
|
|
131
|
+
db.query("MATCH (n:Person {name: 'Bob'}) DETACH DELETE n")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Parameterized Queries
|
|
135
|
+
|
|
136
|
+
Use parameters to safely pass values into queries:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# String parameter
|
|
140
|
+
result = db.query(
|
|
141
|
+
"MATCH (n:Person) WHERE n.name = $name RETURN n",
|
|
142
|
+
parameters={"name": "Alice"}
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Integer parameter
|
|
146
|
+
result = db.query(
|
|
147
|
+
"MATCH (n:Person) WHERE n.age = $age RETURN n.name",
|
|
148
|
+
parameters={"age": 30}
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Multiple parameters
|
|
152
|
+
result = db.query(
|
|
153
|
+
"MATCH (n:Person) WHERE n.name = $name AND n.age > $min_age RETURN n",
|
|
154
|
+
parameters={"name": "Alice", "min_age": 20}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Vector parameter (requires numpy)
|
|
158
|
+
import numpy as np
|
|
159
|
+
query_vec = np.random.rand(384).astype(np.float32)
|
|
160
|
+
result = db.query(
|
|
161
|
+
"MATCH (n:Document) WHERE n.embedding <=> $vec < 0.5 RETURN n",
|
|
162
|
+
parameters={"vec": query_vec}
|
|
163
|
+
)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Working with Results
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
result = db.query("MATCH (n:Person) RETURN n.name")
|
|
170
|
+
|
|
171
|
+
# Get column names
|
|
172
|
+
print(result.columns) # ['n']
|
|
173
|
+
|
|
174
|
+
# Iterate rows
|
|
175
|
+
for row in result:
|
|
176
|
+
print(row) # {'n': 'Alice'}
|
|
177
|
+
|
|
178
|
+
# Get all rows as list
|
|
179
|
+
rows = list(result)
|
|
180
|
+
|
|
181
|
+
# Get row count
|
|
182
|
+
print(len(result))
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Reading Node Data
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
with db.read() as txn:
|
|
189
|
+
# Get a node by ID
|
|
190
|
+
node = txn.get_node(node_id)
|
|
191
|
+
if node:
|
|
192
|
+
print(f"ID: {node.id}")
|
|
193
|
+
print(f"Labels: {node.labels}")
|
|
194
|
+
|
|
195
|
+
# Get individual properties
|
|
196
|
+
name = txn.get_property(node_id, "name")
|
|
197
|
+
age = txn.get_property(node_id, "age")
|
|
198
|
+
|
|
199
|
+
# Returns None if property doesn't exist
|
|
200
|
+
unknown = txn.get_property(node_id, "nonexistent") # None
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Vector Operations
|
|
204
|
+
|
|
205
|
+
To use vector embeddings, enable vector storage when opening the database:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
import numpy as np
|
|
209
|
+
|
|
210
|
+
with Database("mydb.ltdb", create=True, enable_vector=True, vector_dimensions=384) as db:
|
|
211
|
+
# Store vectors
|
|
212
|
+
with db.write() as txn:
|
|
213
|
+
node1 = txn.create_node(labels=["Document"])
|
|
214
|
+
txn.set_property(node1.id, "title", "Introduction to ML")
|
|
215
|
+
embedding1 = np.random.rand(384).astype(np.float32)
|
|
216
|
+
txn.set_vector(node1.id, "embedding", embedding1)
|
|
217
|
+
|
|
218
|
+
node2 = txn.create_node(labels=["Document"])
|
|
219
|
+
txn.set_property(node2.id, "title", "Deep Learning Guide")
|
|
220
|
+
embedding2 = np.random.rand(384).astype(np.float32)
|
|
221
|
+
txn.set_vector(node2.id, "embedding", embedding2)
|
|
222
|
+
|
|
223
|
+
txn.commit()
|
|
224
|
+
|
|
225
|
+
# Search for similar vectors (HNSW approximate nearest neighbor)
|
|
226
|
+
query_vector = np.random.rand(384).astype(np.float32)
|
|
227
|
+
results = db.vector_search(query_vector, k=10, ef_search=64)
|
|
228
|
+
|
|
229
|
+
for result in results:
|
|
230
|
+
print(f"Node {result.node_id}: distance={result.distance:.4f}")
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### Vector Search Parameters
|
|
234
|
+
|
|
235
|
+
- `vector`: Query vector (numpy array of float32)
|
|
236
|
+
- `k`: Number of nearest neighbors to return (default: 10)
|
|
237
|
+
- `ef_search`: HNSW exploration factor - higher values are slower but more accurate (default: 64)
|
|
238
|
+
|
|
239
|
+
### Full-Text Search
|
|
240
|
+
|
|
241
|
+
Index text content and search with BM25 scoring:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
with Database("mydb.ltdb", create=True) as db:
|
|
245
|
+
# Index documents
|
|
246
|
+
with db.write() as txn:
|
|
247
|
+
doc1 = txn.create_node(labels=["Document"])
|
|
248
|
+
txn.set_property(doc1.id, "title", "Introduction to ML")
|
|
249
|
+
txn.fts_index(doc1.id, "Machine learning is a subset of artificial intelligence")
|
|
250
|
+
|
|
251
|
+
doc2 = txn.create_node(labels=["Document"])
|
|
252
|
+
txn.set_property(doc2.id, "title", "Deep Learning Guide")
|
|
253
|
+
txn.fts_index(doc2.id, "Deep learning uses neural networks")
|
|
254
|
+
|
|
255
|
+
txn.commit()
|
|
256
|
+
|
|
257
|
+
# Search for documents
|
|
258
|
+
results = db.fts_search("machine learning", limit=10)
|
|
259
|
+
|
|
260
|
+
for result in results:
|
|
261
|
+
print(f"Node {result.node_id}: score={result.score:.4f}")
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### FTS Search Parameters
|
|
265
|
+
|
|
266
|
+
- `query`: Search query text
|
|
267
|
+
- `limit`: Maximum number of results to return (default: 10)
|
|
268
|
+
|
|
269
|
+
## Supported Property Types
|
|
270
|
+
|
|
271
|
+
- `None` - Null value
|
|
272
|
+
- `bool` - Boolean
|
|
273
|
+
- `int` - 64-bit integer
|
|
274
|
+
- `float` - 64-bit float
|
|
275
|
+
- `str` - UTF-8 string
|
|
276
|
+
- `bytes` - Binary data
|
|
277
|
+
|
|
278
|
+
## Error Handling
|
|
279
|
+
|
|
280
|
+
The library raises typed exceptions:
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
from lattice import LatticeError, LatticeNotFoundError, LatticeIOError
|
|
284
|
+
|
|
285
|
+
try:
|
|
286
|
+
with Database("nonexistent.ltdb") as db:
|
|
287
|
+
pass
|
|
288
|
+
except LatticeNotFoundError:
|
|
289
|
+
print("Database not found")
|
|
290
|
+
except LatticeIOError:
|
|
291
|
+
print("I/O error")
|
|
292
|
+
except LatticeError as e:
|
|
293
|
+
print(f"Error: {e}")
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Utilities
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
from lattice import version, library_available
|
|
300
|
+
|
|
301
|
+
# Check if the native library is available
|
|
302
|
+
if library_available():
|
|
303
|
+
print("Library found")
|
|
304
|
+
|
|
305
|
+
# Get the native library version
|
|
306
|
+
print(f"Lattice version: {version()}")
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Requirements
|
|
310
|
+
|
|
311
|
+
- Python 3.9+
|
|
312
|
+
- NumPy (optional, for vector operations)
|
|
313
|
+
- The native LatticeDB library (`liblattice.dylib` / `liblattice.so`)
|
|
314
|
+
|
|
315
|
+
## Building from Source
|
|
316
|
+
|
|
317
|
+
See [CONTRIBUTING.md](../../CONTRIBUTING.md) for build instructions.
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
Same license as LatticeDB.
|