Skip to content

Commit 0e1cb9b

Browse files
committed
Refactor documentation and examples to promote chunked transactions over batch_context for embedded workloads
1 parent b5bbb56 commit 0e1cb9b

13 files changed

Lines changed: 274 additions & 150 deletions

File tree

bindings/python/docs/api/batch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ The `BatchContext` class simplifies bulk data operations by:
1212
- **Context Manager**: Automatic cleanup and completion waiting
1313
- **High Performance**: 50,000-200,000 records/sec throughput
1414

15+
> **Embedded note:** For embedded workloads, explicit chunked transactions (`with
16+
> db.transaction():` in fixed-size slices) currently outperform `batch_context`. Use
17+
> `batch_context` primarily for API demonstrations or tests until performance parity is
18+
> improved.
19+
1520
## Class: BatchContext
1621

1722
High-level batch processing context manager.

bindings/python/docs/api/database.md

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -518,32 +518,6 @@ db.async_executor() -> AsyncExecutor
518518

519519
---
520520

521-
### batch_context
522-
523-
```python
524-
db.batch_context(
525-
batch_size: int = 5000,
526-
parallel: int = 4,
527-
use_wal: bool = True,
528-
back_pressure: int = 50,
529-
progress: bool = False,
530-
progress_desc: str = "Processing",
531-
)
532-
```
533-
534-
Convenience context for bulk ingestion built on the async executor (auto-commit, back-pressure, optional progress).
535-
536-
**Example:**
537-
538-
```python
539-
with db.batch_context(batch_size=5000, parallel=8, progress=True) as batch:
540-
batch.set_total(len(users))
541-
for user in users:
542-
batch.create_vertex("User", **user)
543-
```
544-
545-
---
546-
547521
### export_database
548522

549523
```python

bindings/python/docs/development/testing/test-exporter.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[View source code](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/tests/test_exporter.py){ .md-button }
44

5-
These notes mirror the Python tests in [test_exporter.py](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/tests/test_exporter.py). There are 5 test classes with 12+ tests covering JSONL (with type/edge filters), GraphML/GraphSON (skipped if Gremlin unavailable), CSV, round-trip (export→import), batch-context integration, and all data types.
5+
These notes mirror the Python tests in [test_exporter.py](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/tests/test_exporter.py). There are 5 test classes with 12+ tests covering JSONL (with type/edge filters), GraphML/GraphSON (skipped if Gremlin unavailable), CSV, round-trip (export→import), bulk insert (chunked transactions), and all data types.
66

77
## Test Classes & Cases
88

@@ -42,9 +42,9 @@ Fixture `sample_db` creates 20 users, 15 movies, 10 actors, 50 Rated edges, 30 A
4242

4343
- **jsonl_export_import_roundtrip**: Exports sample_db to JSONL, closes original, creates new DB, imports via `IMPORT DATABASE file://...`, then verifies counts (User 20, Movie 15, Actor 10, LogEntry 10, Config 5) and data integrity (first user name is "User0"). See [test_exporter.py#L508-L567](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/tests/test_exporter.py#L508-L567).
4444

45-
### TestExportWithBatchContext
45+
### TestExportWithBulkInsert
4646

47-
- **export_after_batch_insert**: Uses `batch_context(batch_size=100, parallel=2)` to create 500 Product vertices, exports to JSONL, asserts `vertices == 500`. See [test_exporter.py#L570-L593](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/tests/test_exporter.py#L570-L593).
47+
- **export_after_chunked_insert**: Uses chunked transactions (no `batch_context`) to create 500 Product vertices, exports to JSONL, asserts `vertices == 500`. See [test_exporter.py#L567-L605](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/tests/test_exporter.py#L567-L605).
4848

4949
### TestAllDataTypes
5050

bindings/python/docs/development/testing/test-vector.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ What the tests cover:
1818
-**Overquery factor** tuning (`overquery_factor`)
1919
-**Distance functions** (cosine default, euclidean variants)
2020
-**Persistence & size checks** (index files survive reopen)
21-
-**Batch inserts** through `BatchContext`
21+
-**Chunked inserts** via explicit transactions (preferred for embedded)
2222

2323
## Test Coverage (high level)
2424

@@ -85,27 +85,32 @@ with arcadedb.create_database("./test_db") as db:
8585
)
8686
```
8787

88-
### Batch insert vectors
88+
### Chunked insert vectors (preferred)
8989
```python
9090
with arcadedb.create_database("./test_db") as db:
9191
# Schema operations are auto-transactional
9292
db.schema.create_vertex_type("Doc")
9393
db.schema.create_property("Doc", "docId", "INTEGER")
9494
db.schema.create_property("Doc", "embedding", "ARRAY_OF_FLOATS")
9595

96-
# Batch insert (auto-transactional)
96+
# Prefer chunked transactions for embedded (avoids batch_context overhead)
9797
vectors = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
98-
with db.batch_context(batch_size=1000, parallel=4) as batch:
99-
for i, vec in enumerate(vectors):
100-
batch.create_vertex("Doc", docId=i, embedding=vec)
98+
chunk_size = 100
99+
for start in range(0, len(vectors), chunk_size):
100+
with db.transaction():
101+
for idx, vec in enumerate(vectors[start : start + chunk_size]):
102+
doc = db.new_vertex("Doc")
103+
doc.set("docId", start + idx)
104+
doc.set("embedding", vec)
105+
doc.save()
101106
```
102107

103108
## Key Takeaways
104109

105110
1. JVector is fully Java-native and LSM-backed; no legacy hnswlib path remains.
106111
2. Use `allowed_rids` for pre-filtered searches and `overquery_factor` for recall/speed trade-offs.
107112
3. `max_connections` and `beam_width` map to JVector graph degree and search beam; tune per workload.
108-
4. All tests run through the Python bindings to ensure parity with the Java engine.
113+
4. Prefer chunked `db.transaction()` inserts for embedded workloads; reserve `batch_context` for legacy/tests that explicitly need it.
109114

110115
## See Also
111116

bindings/python/docs/guide/core/queries.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,23 @@ with db.transaction():
4646
bob.save()
4747
```
4848

49-
### Bulk Inserts with BatchContext
49+
### Bulk Inserts (preferred: chunked transactions)
5050

5151
```python
52-
# Efficient bulk insertion
53-
from jpype import JClass
54-
LocalDate = JClass("java.time.LocalDate")
55-
56-
with db.batch_context(batch_size=100, parallel=2) as batch:
57-
for name, age, city in people_data:
58-
batch.create_vertex("Person",
59-
name=name,
60-
age=age,
61-
city=city,
62-
joined_date=LocalDate.parse("2024-01-15")
63-
)
64-
# Automatically waits for completion
52+
# Efficient bulk insertion (embedded-friendly): use chunked transactions
53+
chunk_size = 500
54+
for start in range(0, len(people_data), chunk_size):
55+
with db.transaction():
56+
for name, age, city in people_data[start : start + chunk_size]:
57+
person = db.new_vertex("Person")
58+
person.set("name", name)
59+
person.set("age", age)
60+
person.set("city", city)
61+
person.save()
62+
63+
# If you specifically need the Java batching API (e.g., remote/server workflows),
64+
# you can still use db.batch_context(...), but embedded users should prefer
65+
# explicit chunked transactions to avoid batch_context overhead.
6566
```
6667

6768
## SQL for Queries

bindings/python/docs/guide/core/transactions.md

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# Transactions
22

3-
Prefer the Pythonic wrappers (`new_vertex`, `new_document`, `new_edge`, `modify`,
4-
`batch_context`) over raw SQL. When you see `temp_db_path`, substitute your own path if
5-
you are not in the test harness.
3+
Prefer the Pythonic wrappers (`new_vertex`, `new_document`, `new_edge`, `modify`) over
4+
raw SQL. When you see `temp_db_path`, substitute your own path if you are not in the
5+
test harness.
6+
7+
> **Embedded note:** For bulk ingest in embedded mode, explicit chunked transactions are
8+
> currently faster and more stable. Use `with db.transaction():` in fixed-size slices for
9+
> production ingest.
610
711
## Basic commit and rollback
812

@@ -113,30 +117,6 @@ with arcadedb.create_database(temp_db_path) as db:
113117
assert updated.get("country") == "USA"
114118
```
115119

116-
## Batch context: edge creation must be inside a transaction
117-
118-
```python
119-
with db.transaction():
120-
with db.batch_context(batch_size=10) as batch:
121-
batch.create_edge(alice, bob, "KNOWS", since=2020)
122-
batch.create_edge(bob, charlie, "KNOWS", since=2021)
123-
batch.create_edge(charlie, alice, "KNOWS", since=2022)
124-
```
125-
126-
## Batch updates with `modify()` + `batch_context`
127-
128-
```python
129-
counters = list(db.query("sql", "SELECT FROM Counter"))
130-
131-
with db.transaction():
132-
with db.batch_context(batch_size=50) as batch:
133-
for counter in counters:
134-
vertex = counter.get_vertex()
135-
mutable_vertex = vertex.modify()
136-
mutable_vertex.set("value", counter.get("value") * 2)
137-
batch.update_record(mutable_vertex._java_document)
138-
```
139-
140120
## Chunked bulk inserts with manual commit/renew
141121

142122
```python

bindings/python/examples/02_social_network_graph.py

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -257,57 +257,64 @@ def create_sample_data(db):
257257
]
258258

259259
print(" 📝 Creating people...")
260-
print(" 💡 Using BatchContext for efficient bulk insertion")
260+
print(" 💡 Using chunked transactions for efficient bulk insertion")
261261

262262
# Parse date strings to Java dates once (for reuse)
263263
from jpype import JClass
264264

265265
LocalDate = JClass("java.time.LocalDate")
266266

267-
# Create people using BatchContext for cleaner, more efficient code
268-
with db.batch_context(batch_size=100, parallel=2) as batch:
269-
for person in people_data:
270-
name, age, city, joined_date, email, phone, verified, reputation = (
271-
person
272-
)
273-
274-
# Parse date string to Java date
275-
date_obj = LocalDate.parse(joined_date)
276-
277-
# Create vertex properties dict
278-
# (BatchContext handles None/NULL automatically)
279-
properties = {
280-
"name": name,
281-
"age": age,
282-
"city": city,
283-
"joined_date": date_obj,
284-
"verified": verified,
285-
}
286-
287-
# Add optional fields only if present
288-
if email:
289-
properties["email"] = email
290-
if phone:
291-
properties["phone"] = phone
292-
if reputation is not None:
293-
properties["reputation"] = reputation
294-
295-
# Create vertex using batch context
296-
# (automatically queued for async insertion)
297-
batch.create_vertex("Person", **properties)
298-
299-
# Show which fields are NULL
300-
null_fields = []
301-
if not email:
302-
null_fields.append("email")
303-
if not phone:
304-
null_fields.append("phone")
305-
if reputation is None:
306-
null_fields.append("reputation")
307-
308-
null_str = f" (NULL: {', '.join(null_fields)})" if null_fields else ""
309-
print(f" ✓ Queued person: {name} ({age}, {city}){null_str}")
310-
# BatchContext automatically waits for completion on exit
267+
# Create people using chunked transactions (more stable for embedded)
268+
chunk_size = 100
269+
for start in range(0, len(people_data), chunk_size):
270+
end = start + chunk_size
271+
with db.transaction():
272+
for person in people_data[start:end]:
273+
(
274+
name,
275+
age,
276+
city,
277+
joined_date,
278+
email,
279+
phone,
280+
verified,
281+
reputation,
282+
) = person
283+
284+
date_obj = LocalDate.parse(joined_date)
285+
286+
properties = {
287+
"name": name,
288+
"age": age,
289+
"city": city,
290+
"joined_date": date_obj,
291+
"verified": verified,
292+
}
293+
294+
if email:
295+
properties["email"] = email
296+
if phone:
297+
properties["phone"] = phone
298+
if reputation is not None:
299+
properties["reputation"] = reputation
300+
301+
v = db.new_vertex("Person")
302+
for k, vval in properties.items():
303+
v.set(k, vval)
304+
v.save()
305+
306+
null_fields = []
307+
if not email:
308+
null_fields.append("email")
309+
if not phone:
310+
null_fields.append("phone")
311+
if reputation is None:
312+
null_fields.append("reputation")
313+
314+
null_str = (
315+
f" (NULL: {', '.join(null_fields)})" if null_fields else ""
316+
)
317+
print(f" ✓ Inserted person: {name} ({age}, {city}){null_str}")
311318

312319
print(" ✅ All people created successfully")
313320

bindings/python/examples/benchmark-vector/benchmark_vector_params.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,17 @@ def setup_database(db, data):
222222
db.schema.create_property("VectorData", "id", "INTEGER")
223223
db.schema.create_property("VectorData", "vector", "ARRAY_OF_FLOATS")
224224

225-
with db.batch_context(batch_size=10000) as batch:
226-
for i, vec in enumerate(data):
227-
batch.create_vertex("VectorData", id=i, vector=vec)
225+
# Use explicit chunked transactions for ingest (faster than batch_context in
226+
# embedded mode)
227+
chunk_size = 10000
228+
for start in range(0, len(data), chunk_size):
229+
end = start + chunk_size
230+
with db.transaction():
231+
for i, vec in enumerate(data[start:end], start=start):
232+
v = db.new_vertex("VectorData")
233+
v.set("id", i)
234+
v.set("vector", vec)
235+
v.save()
228236

229237
setup_time = time.perf_counter() - start_setup
230238
print(f" Database setup completed in {setup_time:.4f}s")

0 commit comments

Comments
 (0)