or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdconfiguration.mddata-types.mddrivers.mdindex.mdsessions.mdtransactions-results.md

data-types.mddocs/

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

```