0
# Data Types
1
2
Rich data type system including records, queries with metadata, bookmarks for causal consistency, and comprehensive support for Neo4j's type system. The data types provide flexible access to query results and enable advanced features like causal consistency and transaction metadata.
3
4
## Capabilities
5
6
### Record Class
7
8
Immutable key-value collection representing a single result record from a Cypher query, providing dictionary-like access to column values.
9
10
```python { .api }
11
class Record:
12
def keys(self) -> tuple[str, ...]:
13
"""
14
Get field names from the record.
15
16
Returns:
17
Tuple of field names as strings
18
"""
19
20
def values(self) -> tuple:
21
"""
22
Get field values from the record.
23
24
Returns:
25
Tuple containing all field values in order
26
"""
27
28
def items(self) -> list[tuple[str, Any]]:
29
"""
30
Get key-value pairs from the record.
31
32
Returns:
33
List of (key, value) tuples
34
"""
35
36
def get(self, key: str, default=None):
37
"""
38
Get value by key with optional default.
39
40
Parameters:
41
- key: Field name to retrieve
42
- default: Value to return if key not found
43
44
Returns:
45
Field value or default if key doesn't exist
46
"""
47
48
def data(self, *keys: str) -> dict:
49
"""
50
Return record as dictionary, optionally filtering by keys.
51
52
Parameters:
53
- *keys: Optional field names to include (all if not specified)
54
55
Returns:
56
Dictionary representation of record
57
"""
58
59
def to_dict(self) -> dict:
60
"""
61
Convert record to dictionary.
62
63
Returns:
64
Dictionary containing all key-value pairs
65
"""
66
67
def __getitem__(self, key: str | int) -> Any:
68
"""
69
Access field by name or index.
70
71
Parameters:
72
- key: Field name (string) or index (integer)
73
74
Returns:
75
Field value
76
"""
77
78
def __getattr__(self, name: str) -> Any:
79
"""
80
Access field as attribute (dot notation).
81
82
Parameters:
83
- name: Field name
84
85
Returns:
86
Field value
87
"""
88
89
def __len__(self) -> int:
90
"""Get number of fields in the record."""
91
92
def __iter__(self) -> Iterator[str]:
93
"""Iterator over field names."""
94
```
95
96
Example record usage:
97
98
```python
99
from neo4j import GraphDatabase, basic_auth
100
101
driver = GraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
102
103
with driver.session() as session:
104
result = session.run("""
105
MATCH (n:Person)
106
RETURN n.name AS name, n.age AS age, n
107
LIMIT 1
108
""")
109
110
record = result.single()
111
112
# Dictionary-style access
113
print(record["name"])
114
print(record["age"])
115
116
# Attribute-style access
117
print(record.name)
118
print(record.age)
119
120
# Get with default
121
email = record.get("email", "no-email@example.com")
122
123
# Access by index
124
first_value = record[0] # First column value
125
126
# Iterate over keys and values
127
for key in record:
128
print(f"{key}: {record[key]}")
129
130
# Convert to dictionary
131
person_dict = record.data()
132
print(person_dict) # {'name': 'Alice', 'age': 30, 'n': Node(...)}
133
134
# Partial conversion
135
basic_info = record.data("name", "age")
136
print(basic_info) # {'name': 'Alice', 'age': 30}
137
```
138
139
### Query Class
140
141
Query container with attached metadata and configuration for advanced transaction control.
142
143
```python { .api }
144
class Query:
145
def __init__(
146
self,
147
text: str,
148
metadata: dict | None = None,
149
timeout: float | None = None
150
):
151
"""
152
Create a query with metadata and timeout configuration.
153
154
Parameters:
155
- text: Cypher query string
156
- metadata: Transaction metadata dictionary
157
- timeout: Transaction timeout in seconds
158
"""
159
160
@property
161
def text(self) -> str:
162
"""
163
The Cypher query text.
164
165
Returns:
166
Query string
167
"""
168
169
@property
170
def metadata(self) -> dict | None:
171
"""
172
Transaction metadata attached to the query.
173
174
Returns:
175
Metadata dictionary or None
176
"""
177
178
@property
179
def timeout(self) -> float | None:
180
"""
181
Transaction timeout in seconds.
182
183
Returns:
184
Timeout value or None for default
185
"""
186
```
187
188
Example query usage:
189
190
```python
191
from neo4j import Query, GraphDatabase, basic_auth
192
193
# Query with metadata for monitoring
194
query = Query(
195
"CREATE (n:Person {name: $name}) RETURN n",
196
metadata={"application": "user_service", "version": "1.2.3"},
197
timeout=30.0
198
)
199
200
driver = GraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
201
202
with driver.session() as session:
203
result = session.run(query, name="Alice")
204
record = result.single()
205
print(record["n"]["name"])
206
207
# Using @unit_of_work decorator with Query
208
from neo4j import unit_of_work
209
210
@unit_of_work(metadata={"operation": "batch_create"}, timeout=60.0)
211
def create_users(tx, users):
212
for user in users:
213
tx.run("CREATE (n:Person {name: $name})", name=user)
214
215
with driver.session() as session:
216
session.execute_write(create_users, ["Alice", "Bob", "Charlie"])
217
```
218
219
### Bookmark Classes
220
221
Bookmarks enable causal consistency, ensuring read operations can see the effects of previous write operations across different sessions.
222
223
```python { .api }
224
class Bookmark:
225
"""
226
Represents a bookmark for causal consistency.
227
Bookmarks are opaque values returned by transactions to track causality.
228
"""
229
230
class Bookmarks:
231
"""
232
Collection of bookmarks for causal consistency across multiple transactions.
233
"""
234
```
235
236
Bookmark usage for causal consistency:
237
238
```python
239
from neo4j import GraphDatabase, basic_auth
240
241
driver = GraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
242
243
# Write operation in one session
244
with driver.session() as write_session:
245
write_session.run("CREATE (n:Person {name: 'Alice'})")
246
# Capture bookmarks after write
247
bookmarks = write_session.last_bookmarks
248
249
# Read operation in another session using bookmarks
250
with driver.session(bookmarks=bookmarks) as read_session:
251
result = read_session.run("MATCH (n:Person {name: 'Alice'}) RETURN n")
252
record = result.single()
253
# This read is guaranteed to see the Alice node created above
254
print(f"Found: {record['n']['name']}")
255
256
# Bookmark manager for automatic bookmark handling
257
bookmark_manager = GraphDatabase.bookmark_manager()
258
259
# Sessions with bookmark manager automatically handle causal consistency
260
with driver.session(bookmark_manager=bookmark_manager) as session1:
261
session1.run("CREATE (n:Person {name: 'Bob'})")
262
263
with driver.session(bookmark_manager=bookmark_manager) as session2:
264
# This session automatically sees changes from session1
265
result = session2.run("MATCH (n:Person) RETURN count(n) AS count")
266
count = result.single()["count"]
267
print(f"Total persons: {count}")
268
```
269
270
### BookmarkManager Classes
271
272
Bookmark managers automate causal consistency management across sessions and transactions.
273
274
```python { .api }
275
class BookmarkManager:
276
"""
277
Abstract base class for managing bookmarks automatically.
278
Handles bookmark storage, retrieval, and causal consistency.
279
"""
280
281
class AsyncBookmarkManager:
282
"""
283
Abstract base class for async bookmark management.
284
Provides the same functionality as BookmarkManager for async operations.
285
"""
286
```
287
288
Custom bookmark manager implementation:
289
290
```python
291
from neo4j import BookmarkManager, GraphDatabase, basic_auth
292
293
class CustomBookmarkManager(BookmarkManager):
294
def __init__(self):
295
self._bookmarks = []
296
297
def get_bookmarks(self):
298
return self._bookmarks
299
300
def update_bookmarks(self, previous_bookmarks, new_bookmarks):
301
# Custom logic for bookmark management
302
self._bookmarks = new_bookmarks
303
# Could store in database, cache, etc.
304
305
# Use custom bookmark manager
306
custom_manager = CustomBookmarkManager()
307
driver = GraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
308
309
with driver.session(bookmark_manager=custom_manager) as session:
310
session.run("CREATE (n:Person {name: 'Charlie'})")
311
# Bookmarks automatically managed by custom_manager
312
```
313
314
### Address Classes
315
316
Network address handling for server connections and cluster routing.
317
318
```python { .api }
319
class Address:
320
"""Base class for server addresses."""
321
322
@classmethod
323
def parse(
324
cls,
325
address: str,
326
default_host: str = None,
327
default_port: int = None
328
) -> Address:
329
"""
330
Parse address string to Address instance.
331
332
Parameters:
333
- address: Address string to parse
334
- default_host: Default host if not specified
335
- default_port: Default port if not specified
336
337
Returns:
338
Address instance (IPv4Address or IPv6Address)
339
"""
340
341
@classmethod
342
def parse_list(
343
cls,
344
addresses: str | list[str],
345
default_host: str = None,
346
default_port: int = None
347
) -> list[Address]:
348
"""
349
Parse multiple addresses.
350
351
Parameters:
352
- addresses: Address string or list of address strings
353
- default_host: Default host for addresses without host
354
- default_port: Default port for addresses without port
355
356
Returns:
357
List of Address instances
358
"""
359
360
class IPv4Address(Address):
361
"""
362
IPv4 address representation.
363
Contains host and port information.
364
"""
365
366
class IPv6Address(Address):
367
"""
368
IPv6 address representation.
369
Contains host, port, flow_info, and scope_id information.
370
"""
371
```
372
373
### Graph Data Types
374
375
Neo4j's rich graph type system including nodes, relationships, and paths returned from Cypher queries.
376
377
```python { .api }
378
class Node:
379
"""
380
Self-contained graph node with labels and properties.
381
"""
382
@property
383
def element_id(self) -> str:
384
"""The unique identifier for this node."""
385
386
@property
387
def labels(self) -> frozenset[str]:
388
"""The set of labels attached to this node."""
389
390
def get(self, name: str, default: object = None) -> Any:
391
"""Get a property value by name, optionally with a default."""
392
393
def keys(self) -> KeysView[str]:
394
"""Return an iterable of all property names."""
395
396
def values(self) -> ValuesView[Any]:
397
"""Return an iterable of all property values."""
398
399
def items(self) -> ItemsView[str, Any]:
400
"""Return an iterable of all property name-value pairs."""
401
402
def __getitem__(self, name: str) -> Any:
403
"""Access property by name."""
404
405
def __contains__(self, name: object) -> bool:
406
"""Check if property exists."""
407
408
class Relationship:
409
"""
410
Self-contained graph relationship connecting two nodes.
411
"""
412
@property
413
def element_id(self) -> str:
414
"""The unique identifier for this relationship."""
415
416
@property
417
def start_node(self) -> Node | None:
418
"""Get the start node of this relationship."""
419
420
@property
421
def end_node(self) -> Node | None:
422
"""Get the end node of this relationship."""
423
424
@property
425
def type(self) -> str:
426
"""Get the type name of this relationship."""
427
428
@property
429
def nodes(self) -> tuple[Node | None, Node | None]:
430
"""Get the pair of nodes which this relationship connects."""
431
432
def get(self, name: str, default: object = None) -> Any:
433
"""Get a property value by name, optionally with a default."""
434
435
def keys(self) -> KeysView[str]:
436
"""Return an iterable of all property names."""
437
438
def values(self) -> ValuesView[Any]:
439
"""Return an iterable of all property values."""
440
441
def items(self) -> ItemsView[str, Any]:
442
"""Return an iterable of all property name-value pairs."""
443
444
class Path:
445
"""
446
Self-contained graph path representing a sequence of connected nodes and relationships.
447
"""
448
@property
449
def start_node(self) -> Node:
450
"""The first Node in this path."""
451
452
@property
453
def end_node(self) -> Node:
454
"""The last Node in this path."""
455
456
@property
457
def nodes(self) -> tuple[Node, ...]:
458
"""The sequence of Node objects in this path."""
459
460
@property
461
def relationships(self) -> tuple[Relationship, ...]:
462
"""The sequence of Relationship objects in this path."""
463
464
def __len__(self) -> int:
465
"""Get the number of relationships in the path."""
466
467
def __iter__(self) -> Iterator[Relationship]:
468
"""Iterate over relationships in the path."""
469
```
470
471
### Temporal Data Types
472
473
Neo4j's temporal types for date, time, and duration handling with nanosecond precision.
474
475
```python { .api }
476
class Date:
477
"""
478
Idealized date representation with year, month, and day.
479
Years between 0001 and 9999 are supported.
480
"""
481
def __init__(self, year: int, month: int, day: int):
482
"""
483
Create a new Date.
484
485
Parameters:
486
- year: Year (1-9999)
487
- month: Month (1-12)
488
- day: Day of month (1-31)
489
"""
490
491
@property
492
def year(self) -> int:
493
"""The year of the date."""
494
495
@property
496
def month(self) -> int:
497
"""The month of the date."""
498
499
@property
500
def day(self) -> int:
501
"""The day of the date."""
502
503
@classmethod
504
def today(cls, tz: tzinfo | None = None) -> Date:
505
"""Get the current date."""
506
507
@classmethod
508
def from_iso_format(cls, s: str) -> Date:
509
"""Parse ISO formatted date string (YYYY-MM-DD)."""
510
511
def iso_format(self) -> str:
512
"""Return the Date as ISO formatted string."""
513
514
def to_native(self) -> date:
515
"""Convert to a native Python datetime.date value."""
516
517
class DateTime:
518
"""
519
A point in time with nanosecond precision, combining date and time components.
520
"""
521
def __init__(
522
self,
523
year: int,
524
month: int,
525
day: int,
526
hour: int = 0,
527
minute: int = 0,
528
second: int = 0,
529
nanosecond: int = 0,
530
tzinfo: tzinfo | None = None
531
):
532
"""
533
Create a new DateTime.
534
535
Parameters:
536
- year, month, day: Date components
537
- hour, minute, second, nanosecond: Time components
538
- tzinfo: Timezone information
539
"""
540
541
@property
542
def year(self) -> int:
543
"""The year of the DateTime."""
544
545
@property
546
def month(self) -> int:
547
"""The month of the DateTime."""
548
549
@property
550
def day(self) -> int:
551
"""The day of the DateTime."""
552
553
@property
554
def hour(self) -> int:
555
"""The hour of the DateTime."""
556
557
@property
558
def minute(self) -> int:
559
"""The minute of the DateTime."""
560
561
@property
562
def second(self) -> int:
563
"""The second of the DateTime."""
564
565
@property
566
def nanosecond(self) -> int:
567
"""The nanosecond of the DateTime."""
568
569
@property
570
def tzinfo(self) -> tzinfo | None:
571
"""The timezone information."""
572
573
@classmethod
574
def now(cls, tz: tzinfo | None = None) -> DateTime:
575
"""Get the current date and time."""
576
577
@classmethod
578
def from_iso_format(cls, s: str) -> DateTime:
579
"""Parse ISO formatted datetime string."""
580
581
def iso_format(self, sep: str = "T") -> str:
582
"""Return the DateTime as ISO formatted string."""
583
584
def to_native(self) -> datetime:
585
"""Convert to a native Python datetime.datetime value."""
586
587
class Time:
588
"""
589
Time of day with nanosecond precision.
590
"""
591
def __init__(
592
self,
593
hour: int = 0,
594
minute: int = 0,
595
second: int = 0,
596
nanosecond: int = 0,
597
tzinfo: tzinfo | None = None
598
):
599
"""
600
Create a new Time.
601
602
Parameters:
603
- hour: Hour (0-23)
604
- minute: Minute (0-59)
605
- second: Second (0-59)
606
- nanosecond: Nanosecond (0-999999999)
607
- tzinfo: Timezone information
608
"""
609
610
@property
611
def hour(self) -> int:
612
"""The hour of the time."""
613
614
@property
615
def minute(self) -> int:
616
"""The minute of the time."""
617
618
@property
619
def second(self) -> int:
620
"""The second of the time."""
621
622
@property
623
def nanosecond(self) -> int:
624
"""The nanosecond of the time."""
625
626
@property
627
def tzinfo(self) -> tzinfo | None:
628
"""The timezone information."""
629
630
@classmethod
631
def now(cls, tz: tzinfo | None = None) -> Time:
632
"""Get the current time."""
633
634
def to_native(self) -> time:
635
"""Convert to a native Python datetime.time value."""
636
637
class Duration:
638
"""
639
A difference between two points in time with months, days, seconds, and nanoseconds.
640
"""
641
def __init__(
642
self,
643
years: float = 0,
644
months: float = 0,
645
weeks: float = 0,
646
days: float = 0,
647
hours: float = 0,
648
minutes: float = 0,
649
seconds: float = 0,
650
milliseconds: float = 0,
651
microseconds: float = 0,
652
nanoseconds: float = 0
653
):
654
"""
655
Create a new Duration.
656
All parameters are optional and default to 0.
657
"""
658
659
@property
660
def months(self) -> int:
661
"""The months component of the Duration."""
662
663
@property
664
def days(self) -> int:
665
"""The days component of the Duration."""
666
667
@property
668
def seconds(self) -> int:
669
"""The seconds component of the Duration."""
670
671
@property
672
def nanoseconds(self) -> int:
673
"""The nanoseconds component of the Duration."""
674
675
@classmethod
676
def from_iso_format(cls, s: str) -> Duration:
677
"""Parse ISO formatted duration string."""
678
679
def iso_format(self, sep: str = "T") -> str:
680
"""Return the Duration as ISO formatted string."""
681
```
682
683
## Neo4j Type System Examples
684
685
Working with graph and temporal types in practice:
686
687
```python
688
# Working with nodes and relationships
689
from neo4j import GraphDatabase, basic_auth
690
691
driver = GraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
692
693
with driver.session() as session:
694
result = session.run("""
695
CREATE (a:Person {name: 'Alice', age: 30})
696
CREATE (b:Person {name: 'Bob', age: 25})
697
CREATE (a)-[r:KNOWS {since: date('2020-01-01')}]->(b)
698
RETURN a, b, r
699
""")
700
701
record = result.single()
702
703
# Access node properties
704
alice = record["a"]
705
print(f"Name: {alice['name']}, Age: {alice['age']}")
706
print(f"Labels: {alice.labels}")
707
print(f"Element ID: {alice.element_id}")
708
709
# Access relationship properties
710
relationship = record["r"]
711
print(f"Type: {relationship.type}")
712
print(f"Since: {relationship['since']}")
713
print(f"Start: {relationship.start_node.element_id}")
714
print(f"End: {relationship.end_node.element_id}")
715
716
# Working with temporal types
717
from neo4j.time import DateTime, Date, Time, Duration
718
719
with driver.session() as session:
720
# Neo4j temporal types
721
result = session.run("""
722
CREATE (e:Event {
723
date: date('2023-12-25'),
724
time: time('14:30:00'),
725
datetime: datetime('2023-12-25T14:30:00'),
726
duration: duration('P1Y2M3DT4H5M6S')
727
})
728
RETURN e
729
""")
730
731
event = result.single()["e"]
732
print(f"Date: {event['date']}")
733
print(f"Time: {event['time']}")
734
print(f"DateTime: {event['datetime']}")
735
print(f"Duration: {event['duration']}")
736
737
# Python datetime integration
738
python_datetime = datetime.now()
739
session.run(
740
"CREATE (e:Event {created: $dt})",
741
dt=python_datetime
742
)
743
```
744
745
### Spatial Data Types
746
747
Neo4j's spatial types for geometric data with coordinate reference systems (SRID).
748
749
```python { .api }
750
class Point:
751
"""
752
Base class for spatial data representing a point in geometric space.
753
Contains coordinates and spatial reference identifier (SRID).
754
"""
755
def __init__(self, iterable: Iterable[float]):
756
"""
757
Create a Point from coordinates.
758
759
Parameters:
760
- iterable: Sequence of coordinate values as floats
761
"""
762
763
@property
764
def srid(self) -> int | None:
765
"""The spatial reference identifier for this point."""
766
767
@property
768
def x(self) -> float:
769
"""The x coordinate."""
770
771
@property
772
def y(self) -> float:
773
"""The y coordinate."""
774
775
@property
776
def z(self) -> float:
777
"""The z coordinate (if 3D)."""
778
779
class CartesianPoint(Point):
780
"""
781
Point in Cartesian coordinate system.
782
Uses x, y, z coordinates with SRIDs 7203 (2D) or 9157 (3D).
783
"""
784
@property
785
def x(self) -> float:
786
"""The x coordinate."""
787
788
@property
789
def y(self) -> float:
790
"""The y coordinate."""
791
792
@property
793
def z(self) -> float:
794
"""The z coordinate (3D only)."""
795
796
class WGS84Point(Point):
797
"""
798
Point in WGS84 geographic coordinate system (GPS coordinates).
799
Uses longitude, latitude, height with SRIDs 4326 (2D) or 4979 (3D).
800
"""
801
@property
802
def longitude(self) -> float:
803
"""The longitude coordinate."""
804
805
@property
806
def latitude(self) -> float:
807
"""The latitude coordinate."""
808
809
@property
810
def height(self) -> float:
811
"""The height coordinate (3D only)."""
812
```
813
814
### Exception Hierarchy
815
816
Neo4j driver exception hierarchy for error handling and troubleshooting.
817
818
```python { .api }
819
class Neo4jError(Exception):
820
"""
821
Base class for all Neo4j related errors.
822
Represents errors returned by the Neo4j database.
823
"""
824
@property
825
def code(self) -> str:
826
"""The Neo4j error code."""
827
828
@property
829
def message(self) -> str:
830
"""The error message."""
831
832
class ClientError(Neo4jError):
833
"""
834
Client errors indicate problems with the request.
835
These are not retriable and indicate user errors.
836
"""
837
838
class DatabaseError(Neo4jError):
839
"""
840
Database errors indicate problems within the database.
841
These may be retriable depending on the specific error.
842
"""
843
844
class TransientError(Neo4jError):
845
"""
846
Transient errors are temporary and operations should be retried.
847
Indicates temporary issues that may resolve themselves.
848
"""
849
850
class DriverError(Exception):
851
"""
852
Base class for all driver-related errors.
853
Represents errors in the driver itself, not from the database.
854
"""
855
856
class ServiceUnavailable(DriverError):
857
"""
858
Raised when the driver cannot establish connection to any server.
859
This may indicate network issues or database unavailability.
860
"""
861
862
class SessionExpired(DriverError):
863
"""
864
Raised when a session is no longer valid.
865
Sessions can expire due to network issues or server restart.
866
"""
867
868
class TransactionError(DriverError):
869
"""
870
Base class for transaction-related errors.
871
"""
872
873
class ResultError(DriverError):
874
"""
875
Base class for result-related errors.
876
"""
877
878
class ResultConsumedError(ResultError):
879
"""
880
Raised when attempting to access an already consumed result.
881
"""
882
883
class ResultNotSingleError(ResultError):
884
"""
885
Raised when calling single() on a result with zero or multiple records.
886
"""
887
888
class AuthError(ClientError):
889
"""
890
Authentication errors indicate invalid credentials or auth issues.
891
"""
892
893
class CypherSyntaxError(ClientError):
894
"""
895
Raised for Cypher syntax errors in queries.
896
"""
897
898
class CypherTypeError(ClientError):
899
"""
900
Raised for Cypher type errors in queries.
901
"""
902
903
class ConstraintError(ClientError):
904
"""
905
Raised when a constraint violation occurs.
906
"""
907
908
class Forbidden(ClientError):
909
"""
910
Raised when access is denied to a resource or operation.
911
"""
912
913
class DatabaseUnavailable(TransientError):
914
"""
915
Raised when the requested database is unavailable.
916
"""
917
918
class NotALeader(TransientError):
919
"""
920
Raised when a write operation is attempted on a follower in a cluster.
921
"""
922
```
923
924
## Exception Handling Examples
925
926
Working with Neo4j exceptions for robust error handling:
927
928
```python
929
from neo4j import GraphDatabase, basic_auth
930
from neo4j.exceptions import (
931
Neo4jError, ClientError, TransientError, DriverError,
932
ServiceUnavailable, AuthError, CypherSyntaxError,
933
ResultNotSingleError, SessionExpired
934
)
935
936
driver = GraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
937
938
# Handle different error types appropriately
939
def run_query_with_error_handling():
940
try:
941
with driver.session() as session:
942
result = session.run("INVALID CYPHER QUERY")
943
return result.single()
944
945
except CypherSyntaxError as e:
946
print(f"Query syntax error: {e.message}")
947
# Fix the query and retry
948
949
except AuthError as e:
950
print(f"Authentication failed: {e.message}")
951
# Check credentials
952
953
except ServiceUnavailable as e:
954
print(f"Database unavailable: {e}")
955
# Implement retry logic or fallback
956
957
except TransientError as e:
958
print(f"Temporary error: {e.message}")
959
# Retry the operation
960
961
except ClientError as e:
962
print(f"Client error (do not retry): {e.code} - {e.message}")
963
# Fix the client-side issue
964
965
except Neo4jError as e:
966
print(f"Database error: {e.code} - {e.message}")
967
# Handle database-specific errors
968
969
except DriverError as e:
970
print(f"Driver error: {e}")
971
# Handle driver-related issues
972
973
# Retry logic for transient errors
974
import time
975
from random import uniform
976
977
def run_with_retry(session, query, max_retries=3):
978
for attempt in range(max_retries):
979
try:
980
return session.run(query)
981
982
except TransientError as e:
983
if attempt == max_retries - 1:
984
raise # Re-raise if last attempt
985
986
# Exponential backoff with jitter
987
wait_time = (2 ** attempt) + uniform(0, 1)
988
print(f"Transient error, retrying in {wait_time:.2f}s: {e.message}")
989
time.sleep(wait_time)
990
991
except (ClientError, DatabaseError):
992
# Don't retry client or database errors
993
raise
994
995
# Handle result errors
996
def get_single_user(session, name):
997
try:
998
result = session.run("MATCH (u:User {name: $name}) RETURN u", name=name)
999
return result.single()["u"]
1000
1001
except ResultNotSingleError as e:
1002
if "no records" in str(e).lower():
1003
print(f"User '{name}' not found")
1004
return None
1005
else:
1006
print(f"Multiple users found with name '{name}'")
1007
raise
1008
```
1009
1010
## Spatial Types Examples
1011
1012
```python
1013
# Working with spatial data
1014
from neo4j import GraphDatabase, basic_auth
1015
from neo4j.spatial import CartesianPoint, WGS84Point
1016
1017
driver = GraphDatabase.driver("bolt://localhost:7687", auth=basic_auth("neo4j", "password"))
1018
1019
with driver.session() as session:
1020
result = session.run("""
1021
CREATE (p:Place {
1022
location: point({latitude: 37.7749, longitude: -122.4194}),
1023
name: 'San Francisco'
1024
})
1025
RETURN p
1026
""")
1027
1028
place = result.single()["p"]
1029
location = place["location"] # WGS84Point instance
1030
print(f"Coordinates: {location.latitude}, {location.longitude}")
1031
print(f"SRID: {location.srid}")
1032
1033
# Create Cartesian point
1034
cartesian = CartesianPoint([10.0, 20.0])
1035
session.run(
1036
"CREATE (p:Point {location: $point})",
1037
point=cartesian
1038
)
1039
```
1040
1041
## Utility Functions
1042
1043
```python { .api }
1044
def unit_of_work(
1045
metadata: dict | None = None,
1046
timeout: float | None = None
1047
) -> Callable:
1048
"""
1049
Decorator for transaction functions with metadata and timeout.
1050
1051
Parameters:
1052
- metadata: Transaction metadata dictionary
1053
- timeout: Transaction timeout in seconds
1054
1055
Returns:
1056
Decorated function that passes metadata and timeout to transactions
1057
"""
1058
```
1059
1060
Example decorator usage:
1061
1062
```python
1063
from neo4j import unit_of_work
1064
1065
@unit_of_work(metadata={"operation": "user_creation"}, timeout=30.0)
1066
def create_user_with_friends(tx, user_name, friend_names):
1067
# Create user
1068
user_result = tx.run(
1069
"CREATE (u:User {name: $name}) RETURN u",
1070
name=user_name
1071
)
1072
user_id = user_result.single()["u"].id
1073
1074
# Create friends and relationships
1075
for friend_name in friend_names:
1076
tx.run("""
1077
MATCH (u:User) WHERE id(u) = $user_id
1078
MERGE (f:User {name: $friend_name})
1079
CREATE (u)-[:FRIENDS_WITH]->(f)
1080
""", user_id=user_id, friend_name=friend_name)
1081
1082
return user_id
1083
1084
# Use the decorated function
1085
with driver.session() as session:
1086
user_id = session.execute_write(
1087
create_user_with_friends,
1088
"Alice",
1089
["Bob", "Charlie", "Diana"]
1090
)
1091
print(f"Created user with ID: {user_id}")
1092
```