0
# Transactions & Results
1
2
Transaction management and result handling supporting auto-commit transactions, explicit transactions with full ACID properties, and flexible result consumption patterns. The system provides both streaming and eager result consumption with comprehensive metadata access.
3
4
## Capabilities
5
6
### Transaction Classes
7
8
Explicit transaction management providing full control over transaction lifecycle with commit and rollback capabilities.
9
10
```python { .api }
11
class Transaction:
12
def run(
13
self,
14
query: str,
15
parameters: dict = None,
16
**kwparameters
17
) -> Result:
18
"""
19
Run a Cypher query within the transaction.
20
21
Parameters:
22
- query: Cypher query string
23
- parameters: Query parameters dictionary
24
- **kwparameters: Additional parameters as keyword arguments
25
26
Returns:
27
Result stream for the query execution
28
"""
29
30
def commit(self) -> None:
31
"""
32
Commit the transaction, making all changes permanent.
33
After commit, the transaction cannot be used further.
34
"""
35
36
def rollback(self) -> None:
37
"""
38
Roll back the transaction, discarding all changes.
39
After rollback, the transaction cannot be used further.
40
"""
41
42
def close(self) -> None:
43
"""
44
Close the transaction.
45
If not committed, changes will be rolled back.
46
"""
47
```
48
49
```python { .api }
50
class AsyncTransaction:
51
async def run(
52
self,
53
query: str,
54
parameters: dict = None,
55
**kwparameters
56
) -> AsyncResult:
57
"""
58
Run a Cypher query within the async transaction.
59
60
Parameters:
61
- query: Cypher query string
62
- parameters: Query parameters dictionary
63
- **kwparameters: Additional parameters as keyword arguments
64
65
Returns:
66
AsyncResult stream for the query execution
67
"""
68
69
async def commit(self) -> None:
70
"""
71
Asynchronously commit the transaction.
72
"""
73
74
async def rollback(self) -> None:
75
"""
76
Asynchronously roll back the transaction.
77
"""
78
79
async def close(self) -> None:
80
"""
81
Asynchronously close the transaction.
82
"""
83
```
84
85
Example transaction usage:
86
87
```python
88
from neo4j import GraphDatabase, basic_auth
89
90
driver = GraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
91
92
# Manual transaction control
93
with driver.session() as session:
94
tx = session.begin_transaction()
95
try:
96
# Execute multiple operations in transaction
97
tx.run("CREATE (n:Person {name: $name})", name="Alice")
98
tx.run("CREATE (n:Person {name: $name})", name="Bob")
99
100
# Create relationship between them
101
result = tx.run("""
102
MATCH (a:Person {name: $name1}), (b:Person {name: $name2})
103
CREATE (a)-[:KNOWS]->(b)
104
RETURN a.name, b.name
105
""", name1="Alice", name2="Bob")
106
107
# Process results
108
for record in result:
109
print(f"{record['a.name']} knows {record['b.name']}")
110
111
# Commit all changes
112
tx.commit()
113
print("Transaction committed successfully")
114
115
except Exception as e:
116
# Rollback on error
117
tx.rollback()
118
print(f"Transaction rolled back due to error: {e}")
119
raise
120
```
121
122
### Result Classes
123
124
Result handling providing flexible consumption patterns for query results with both streaming and eager loading capabilities.
125
126
```python { .api }
127
class Result:
128
def keys(self) -> list[str]:
129
"""
130
Get the column keys/names from the query result.
131
132
Returns:
133
List of column names as strings
134
"""
135
136
def consume(self) -> ResultSummary:
137
"""
138
Consume all remaining records and return execution summary.
139
After calling consume(), no more records can be retrieved.
140
141
Returns:
142
ResultSummary containing execution metadata
143
"""
144
145
def single(self, strict: bool = True) -> Record | None:
146
"""
147
Return the single record from the result.
148
149
Parameters:
150
- strict: If True, raise exception if not exactly one record
151
152
Returns:
153
Single Record if exactly one exists, None if zero (when strict=False)
154
155
Raises:
156
Exception if zero records (when strict=True) or multiple records
157
"""
158
159
def peek(self) -> Record:
160
"""
161
Return the next record without consuming it from the stream.
162
163
Returns:
164
Next Record in the stream (can be retrieved again)
165
166
Raises:
167
StopIteration if no more records available
168
"""
169
170
def data(self, *args) -> list[dict]:
171
"""
172
Return all records as a list of dictionaries.
173
174
Parameters:
175
- *args: Optional column names to include (all if not specified)
176
177
Returns:
178
List of dictionaries representing records
179
"""
180
181
def value(self, item: int | str = 0, default=None):
182
"""
183
Return a single column from a single record.
184
185
Parameters:
186
- item: Column index or name to retrieve
187
- default: Value to return if not found
188
189
Returns:
190
Single value from the specified column
191
"""
192
193
def values(self, *items) -> list:
194
"""
195
Return specified columns from all records as lists.
196
197
Parameters:
198
- *items: Column indices or names to retrieve
199
200
Returns:
201
List of column values for each record
202
"""
203
204
def to_df(self, expand: bool = False) -> pandas.DataFrame:
205
"""
206
Convert result to pandas DataFrame.
207
208
Parameters:
209
- expand: Whether to expand nested structures
210
211
Returns:
212
pandas DataFrame containing all records
213
214
Requires:
215
pandas package to be installed
216
"""
217
218
def to_eager_result(self) -> EagerResult:
219
"""
220
Convert to an EagerResult containing all records in memory.
221
222
Returns:
223
EagerResult with records, summary, and keys
224
"""
225
226
def __iter__(self) -> Iterator[Record]:
227
"""Iterator interface for consuming records one by one."""
228
229
def __next__(self) -> Record:
230
"""Get next record from the stream."""
231
```
232
233
```python { .api }
234
class AsyncResult:
235
async def keys(self) -> list[str]:
236
"""Get the column keys asynchronously."""
237
238
async def consume(self) -> ResultSummary:
239
"""
240
Asynchronously consume all records and return summary.
241
242
Returns:
243
ResultSummary containing execution metadata
244
"""
245
246
async def single(self, strict: bool = True) -> Record | None:
247
"""
248
Return the single record asynchronously.
249
250
Parameters:
251
- strict: If True, raise exception if not exactly one record
252
253
Returns:
254
Single Record or None
255
"""
256
257
async def peek(self) -> Record:
258
"""
259
Peek at next record asynchronously.
260
261
Returns:
262
Next Record without consuming it
263
"""
264
265
async def data(self, *args) -> list[dict]:
266
"""
267
Return all records as dictionaries asynchronously.
268
269
Returns:
270
List of dictionaries representing records
271
"""
272
273
async def to_eager_result(self) -> EagerResult:
274
"""
275
Convert to EagerResult asynchronously.
276
277
Returns:
278
EagerResult with all data loaded
279
"""
280
281
def __aiter__(self) -> AsyncIterator[Record]:
282
"""Async iterator interface."""
283
284
async def __anext__(self) -> Record:
285
"""Get next record asynchronously."""
286
```
287
288
Example result usage:
289
290
```python
291
# Streaming consumption
292
with driver.session() as session:
293
result = session.run("MATCH (n:Person) RETURN n.name AS name, n.age AS age")
294
295
# Iterate over results one by one
296
for record in result:
297
print(f"Name: {record['name']}, Age: {record['age']}")
298
299
# Alternative: consume all at once as dictionaries
300
result = session.run("MATCH (n:Person) RETURN n.name AS name")
301
data = result.data()
302
print(data) # [{'name': 'Alice'}, {'name': 'Bob'}]
303
304
# Get single result
305
result = session.run("MATCH (n:Person {name: $name}) RETURN n", name="Alice")
306
record = result.single()
307
print(record["n"]["name"])
308
309
# Get single value
310
result = session.run("MATCH (n:Person) RETURN count(n)")
311
count = result.value()
312
print(f"Total persons: {count}")
313
```
314
315
### EagerResult
316
317
In-memory result container providing immediate access to all query results and metadata.
318
319
```python { .api }
320
class EagerResult:
321
"""
322
NamedTuple containing complete query results in memory.
323
"""
324
records: list[Record]
325
"""List of all records returned by the query."""
326
327
summary: ResultSummary
328
"""Query execution summary and metadata."""
329
330
keys: list[str]
331
"""List of column names/keys."""
332
```
333
334
Example usage:
335
336
```python
337
with driver.session() as session:
338
result = session.run("MATCH (n:Person) RETURN n.name AS name")
339
eager = result.to_eager_result()
340
341
# Access all records
342
for record in eager.records:
343
print(record["name"])
344
345
# Access summary information
346
print(f"Query completed in {eager.summary.result_consumed_after}ms")
347
348
# Access column keys
349
print(f"Columns: {eager.keys}")
350
```
351
352
### ResultSummary
353
354
Comprehensive metadata about query execution including performance statistics, database changes, and server information.
355
356
```python { .api }
357
class ResultSummary:
358
@property
359
def query(self) -> str:
360
"""The Cypher query that was executed."""
361
362
@property
363
def parameters(self) -> dict:
364
"""Parameters that were passed to the query."""
365
366
@property
367
def query_type(self) -> str:
368
"""Type of query (r for read, w for write, etc.)."""
369
370
@property
371
def plan(self) -> dict | None:
372
"""Query execution plan (if requested)."""
373
374
@property
375
def profile(self) -> dict | None:
376
"""Query execution profile (if requested)."""
377
378
@property
379
def notifications(self) -> list[SummaryNotification]:
380
"""Server notifications about the query."""
381
382
@property
383
def counters(self) -> SummaryCounters:
384
"""Statistics about database changes made."""
385
386
@property
387
def result_available_after(self) -> int:
388
"""Time in milliseconds until first record was available."""
389
390
@property
391
def result_consumed_after(self) -> int:
392
"""Time in milliseconds to consume all records."""
393
394
@property
395
def server(self) -> Address:
396
"""Address of the server that executed the query."""
397
398
@property
399
def database(self) -> str:
400
"""Name of the database where query was executed."""
401
```
402
403
### SummaryCounters
404
405
Statistics about database modifications made by write operations.
406
407
```python { .api }
408
class SummaryCounters:
409
@property
410
def nodes_created(self) -> int:
411
"""Number of nodes created."""
412
413
@property
414
def nodes_deleted(self) -> int:
415
"""Number of nodes deleted."""
416
417
@property
418
def relationships_created(self) -> int:
419
"""Number of relationships created."""
420
421
@property
422
def relationships_deleted(self) -> int:
423
"""Number of relationships deleted."""
424
425
@property
426
def properties_set(self) -> int:
427
"""Number of properties set."""
428
429
@property
430
def labels_added(self) -> int:
431
"""Number of labels added to nodes."""
432
433
@property
434
def labels_removed(self) -> int:
435
"""Number of labels removed from nodes."""
436
437
@property
438
def indexes_added(self) -> int:
439
"""Number of indexes created."""
440
441
@property
442
def indexes_removed(self) -> int:
443
"""Number of indexes dropped."""
444
445
@property
446
def constraints_added(self) -> int:
447
"""Number of constraints created."""
448
449
@property
450
def constraints_removed(self) -> int:
451
"""Number of constraints dropped."""
452
453
@property
454
def system_updates(self) -> int:
455
"""Number of system updates performed."""
456
```
457
458
### SummaryNotification
459
460
Server notifications providing information about query execution, warnings, and optimization opportunities.
461
462
```python { .api }
463
class SummaryNotification:
464
@property
465
def code(self) -> str:
466
"""Notification code identifier."""
467
468
@property
469
def title(self) -> str:
470
"""Human-readable notification title."""
471
472
@property
473
def description(self) -> str:
474
"""Detailed description of the notification."""
475
476
@property
477
def severity(self) -> str:
478
"""Severity level (WARNING, INFORMATION, etc.)."""
479
480
@property
481
def category(self) -> str:
482
"""Notification category."""
483
484
@property
485
def position(self) -> SummaryInputPosition | None:
486
"""Position in query where notification applies."""
487
488
class SummaryInputPosition:
489
@property
490
def offset(self) -> int:
491
"""Character offset in the query."""
492
493
@property
494
def line(self) -> int:
495
"""Line number in the query."""
496
497
@property
498
def column(self) -> int:
499
"""Column number in the query."""
500
```
501
502
## Usage Examples
503
504
### Async Results
505
506
```python
507
import asyncio
508
from neo4j import AsyncGraphDatabase, basic_auth
509
510
async def async_example():
511
driver = AsyncGraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
512
513
async with driver.session() as session:
514
result = await session.run("MATCH (n:Person) RETURN n.name AS name")
515
516
# Async iteration
517
async for record in result:
518
print(record["name"])
519
520
# Async single result
521
result = await session.run("MATCH (n:Person {name: $name}) RETURN n", name="Alice")
522
record = await result.single()
523
524
# Async summary
525
result = await session.run("CREATE (n:Person {name: $name})", name="Charlie")
526
summary = await result.consume()
527
print(f"Nodes created: {summary.counters.nodes_created}")
528
529
await driver.close()
530
531
asyncio.run(async_example())
532
```
533
534
### Performance Analysis
535
536
```python
537
with driver.session() as session:
538
result = session.run("EXPLAIN MATCH (n:Person) RETURN n")
539
summary = result.consume()
540
541
if summary.plan:
542
print("Query execution plan:")
543
print(summary.plan)
544
545
print(f"Query type: {summary.query_type}")
546
print(f"Time to first record: {summary.result_available_after}ms")
547
print(f"Total execution time: {summary.result_consumed_after}ms")
548
549
# Check for notifications
550
for notification in summary.notifications:
551
print(f"Notification: {notification.title} - {notification.description}")
552
```