neo4j-ruby-driver 5.7.0.alpha.4 → 6.0.0.alpha.0
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.
- checksums.yaml +4 -4
- data/README.md +440 -40
- data/lib/neo4j/driver/{auto_closable.rb → auto_closeable.rb} +6 -6
- data/lib/neo4j/driver/internal/deprecator.rb +15 -0
- data/ruby/neo4j/driver/bookmark.rb +2 -2
- data/ruby/neo4j/driver/graph_database.rb +2 -2
- data/ruby/neo4j/driver/internal/async/immutable_connection_context.rb +5 -5
- data/ruby/neo4j/driver/internal/async/internal_async_session.rb +1 -1
- data/ruby/neo4j/driver/internal/async/network_session.rb +7 -7
- data/ruby/neo4j/driver/internal/async/unmanaged_transaction.rb +3 -3
- data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +1 -1
- data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +2 -2
- data/ruby/neo4j/driver/internal/delegating_transaction.rb +13 -0
- data/ruby/neo4j/driver/internal/eager_result_value.rb +5 -0
- data/ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb +1 -3
- data/ruby/neo4j/driver/internal/handlers/pulln/auto_pull_response_handler.rb +3 -2
- data/ruby/neo4j/driver/internal/handlers/session_pull_response_completion_listener.rb +1 -1
- data/ruby/neo4j/driver/internal/internal_bookmark.rb +2 -29
- data/ruby/neo4j/driver/internal/internal_driver.rb +11 -2
- data/ruby/neo4j/driver/internal/internal_session.rb +17 -3
- data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +1 -1
- data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +2 -2
- data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +2 -2
- data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +2 -2
- data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +3 -3
- data/ruby/neo4j/driver/internal/packstream/pack_stream.rb +0 -2
- data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +4 -4
- data/ruby/neo4j/driver/internal/session_factory_impl.rb +1 -1
- data/ruby/neo4j/driver/internal/summary/internal_notification.rb +2 -2
- data/ruby/neo4j/driver/internal/util/metadata_extractor.rb +1 -7
- data/ruby/neo4j/driver/version.rb +1 -1
- data/ruby/neo4j/driver.rb +2 -0
- metadata +26 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2cb3ba5c3c1dda74bf82e77866ac67ad834c3cc0fbe34c60c6f94d15040f56f2
|
|
4
|
+
data.tar.gz: 50a8a7f3c771de94889c3edfc28e2c0d3f26fc80c39bae330d81b5d34aedf4ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2011130ffcc499091ded7d02ff85484ec8f170a4d7adca828bf7d125eaa9d4b3b1ec0d1c93b53b0d1379ac8b378bb5c77ef7f869e0d1ffe4a2fca4442672a1b
|
|
7
|
+
data.tar.gz: bae2f8340186a2f0ad94da8e24c193dfdea98bee1a814ea78df481b35e9f4e4ea93c1827b662a46a9a742eaf94d82b8e09b9721754aad2a26dfb77196429ce5d
|
data/README.md
CHANGED
|
@@ -1,12 +1,50 @@
|
|
|
1
|
-
# Neo4j
|
|
1
|
+
# Neo4j Ruby Driver
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This repository contains 2 implementation of a Neo4j driver for Ruby:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
-
|
|
7
|
-
- pure ruby implmementation. Available on all ruby versions >= 3.1.
|
|
5
|
+
- based on official Java implementation. It provides a thin wrapper over the Java driver (only on jruby).
|
|
6
|
+
- pure Ruby implementation. Available on all Ruby versions >= 3.1.
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
Network communication is handled using [Bolt Protocol](https://7687.org/).
|
|
9
|
+
|
|
10
|
+
<details>
|
|
11
|
+
<summary>Table of Contents</summary>
|
|
12
|
+
|
|
13
|
+
* [Getting started](#getting-started)
|
|
14
|
+
* [Installation](#installation)
|
|
15
|
+
* [Getting a Neo4j instance](#getting-a-neo4j-instance)
|
|
16
|
+
* [Quick start example](#quick-start-example)
|
|
17
|
+
* [Server Compatibility](#server-compatibility)
|
|
18
|
+
* [Usage](#usage)
|
|
19
|
+
* [Connecting to a database](#connecting-to-a-database)
|
|
20
|
+
* [URI schemes](#uri-schemes)
|
|
21
|
+
* [Authentication](#authentication)
|
|
22
|
+
* [Configuration](#configuration)
|
|
23
|
+
* [Connectivity check](#connectivity-check)
|
|
24
|
+
* [Sessions & transactions](#sessions--transactions)
|
|
25
|
+
* [Session](#session)
|
|
26
|
+
* [Auto-commit transactions](#auto-commit-transactions)
|
|
27
|
+
* [Explicit transactions](#explicit-transactions)
|
|
28
|
+
* [Read transactions](#read-transactions)
|
|
29
|
+
* [Write transactions](#write-transactions)
|
|
30
|
+
* [Working with results](#working-with-results)
|
|
31
|
+
* [Accessing Node and Relationship data](#accessing-node-and-relationship-data)
|
|
32
|
+
* [Working with Paths](#working-with-paths)
|
|
33
|
+
* [Working with temporal types](#working-with-temporal-types)
|
|
34
|
+
* [Type mapping](#type-mapping)
|
|
35
|
+
* [Advanced](#advanced)
|
|
36
|
+
* [Connection pooling](#connection-pooling)
|
|
37
|
+
* [Logging](#logging)
|
|
38
|
+
* [For Driver Engineers](#for-driver-engineers)
|
|
39
|
+
* [Testing](#testing)
|
|
40
|
+
* [Contributing](#contributing)
|
|
41
|
+
* [License](#license)
|
|
42
|
+
|
|
43
|
+
</details>
|
|
44
|
+
|
|
45
|
+
## Getting started
|
|
46
|
+
|
|
47
|
+
### Installation
|
|
10
48
|
|
|
11
49
|
Add this line to your application's Gemfile:
|
|
12
50
|
|
|
@@ -16,68 +54,430 @@ gem 'neo4j-ruby-driver'
|
|
|
16
54
|
|
|
17
55
|
And then execute:
|
|
18
56
|
|
|
19
|
-
|
|
57
|
+
```bash
|
|
58
|
+
bundle install
|
|
59
|
+
```
|
|
20
60
|
|
|
21
61
|
Or install it yourself as:
|
|
22
62
|
|
|
23
|
-
|
|
63
|
+
```bash
|
|
64
|
+
gem install neo4j-ruby-driver
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Getting a Neo4j instance
|
|
68
|
+
|
|
69
|
+
You need a running Neo4j database in order to use the driver with it. The easiest way to spin up a **local instance** is
|
|
70
|
+
through a Docker container.
|
|
71
|
+
|
|
72
|
+
The command below runs the latest Neo4j version in Docker, setting the admin username and password to `neo4j` and
|
|
73
|
+
`password` respectively:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
docker run \
|
|
77
|
+
-p7474:7474 \
|
|
78
|
+
-p7687:7687 \
|
|
79
|
+
-d \
|
|
80
|
+
-e NEO4J_AUTH=neo4j/password \
|
|
81
|
+
neo4j:latest
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Quick start example
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
require 'neo4j/driver'
|
|
88
|
+
|
|
89
|
+
Neo4j::Driver::GraphDatabase.driver(
|
|
90
|
+
'bolt://localhost:7687',
|
|
91
|
+
Neo4j::Driver::AuthTokens.basic('neo4j', 'password')
|
|
92
|
+
) do |driver|
|
|
93
|
+
driver.session(database: 'neo4j') do |session|
|
|
94
|
+
query_result = session.run('RETURN 2+2 AS value')
|
|
95
|
+
puts "2+2 equals #{query_result.single['value']}"
|
|
96
|
+
|
|
97
|
+
# consume gives the execution summary
|
|
98
|
+
create_result = session.run('CREATE (n)').consume
|
|
99
|
+
puts "Nodes created: #{create_result.counters.nodes_created}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
```
|
|
24
103
|
|
|
25
104
|
## Server Compatibility
|
|
26
105
|
|
|
27
|
-
The compatibility with Neo4j Server versions is documented in
|
|
106
|
+
The compatibility with Neo4j Server versions is documented in
|
|
107
|
+
the [Neo4j Knowledge Base](https://neo4j.com/developer/kb/neo4j-supported-versions/).
|
|
28
108
|
|
|
29
109
|
## Usage
|
|
30
110
|
|
|
31
|
-
The API is to highest possible degree consistent with the official
|
|
32
|
-
|
|
111
|
+
The API is to highest possible degree consistent with the official Java driver. Please refer to
|
|
112
|
+
the [Neo4j Java Driver Manual](https://neo4j.com/docs/java-manual/current/), [examples in Ruby](https://github.com/neo4jrb/neo4j-ruby-driver/blob/master/docs/dev_manual_examples.rb),
|
|
113
|
+
and code snippets below to understand how to use it.
|
|
114
|
+
[Neo4j Java Driver API Docs](https://neo4j.com/docs/api/java-driver/current/) can be helpful as well.
|
|
115
|
+
|
|
116
|
+
### Connecting to a database
|
|
117
|
+
|
|
118
|
+
#### URI schemes
|
|
119
|
+
|
|
120
|
+
The driver supports the following URI schemes:
|
|
121
|
+
|
|
122
|
+
| URI Scheme | Description |
|
|
123
|
+
|----------------|---------------------------------------------------------------------------------|
|
|
124
|
+
| `neo4j://` | Connect using routing to a cluster/causal cluster. |
|
|
125
|
+
| `neo4j+s://` | Same as `neo4j://` but with full TLS encryption. |
|
|
126
|
+
| `neo4j+ssc://` | Same as `neo4j://` but with full TLS encryption, without hostname verification. |
|
|
127
|
+
| `bolt://` | Connect directly to a server using the Bolt protocol. |
|
|
128
|
+
| `bolt+s://` | Same as `bolt://` but with full TLS encryption. |
|
|
129
|
+
| `bolt+ssc://` | Same as `bolt://` but with full TLS encryption, without hostname verification. |
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
# Connect to a single instance
|
|
135
|
+
driver = Neo4j::Driver::GraphDatabase.driver(
|
|
136
|
+
'bolt://localhost:7687',
|
|
137
|
+
Neo4j::Driver::AuthTokens.basic('neo4j', 'password')
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Connect to a cluster
|
|
141
|
+
driver = Neo4j::Driver::GraphDatabase.driver(
|
|
142
|
+
'neo4j://graph.example.com:7687',
|
|
143
|
+
Neo4j::Driver::AuthTokens.basic('neo4j', 'password')
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Authentication
|
|
148
|
+
|
|
149
|
+
The driver provides multiple authentication methods:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
# Basic authentication
|
|
153
|
+
auth = Neo4j::Driver::AuthTokens.basic('neo4j', 'password')
|
|
154
|
+
|
|
155
|
+
# With realm specification
|
|
156
|
+
auth = Neo4j::Driver::AuthTokens.basic('neo4j', 'password', 'realm')
|
|
157
|
+
|
|
158
|
+
# Kerberos authentication
|
|
159
|
+
auth = Neo4j::Driver::AuthTokens.kerberos('ticket')
|
|
160
|
+
|
|
161
|
+
# Bearer authentication
|
|
162
|
+
auth = Neo4j::Driver::AuthTokens.bearer('token')
|
|
163
|
+
|
|
164
|
+
# Custom authentication
|
|
165
|
+
auth = Neo4j::Driver::AuthTokens.custom('principal', 'credentials', 'realm', 'scheme')
|
|
166
|
+
|
|
167
|
+
# No authentication
|
|
168
|
+
auth = Neo4j::Driver::AuthTokens.none
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### Configuration
|
|
172
|
+
|
|
173
|
+
You can configure the driver with additional options:
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
config = {
|
|
177
|
+
connection_timeout: 15.seconds,
|
|
178
|
+
connection_acquisition_timeout: 1.minute,
|
|
179
|
+
max_transaction_retry_time: 30.seconds,
|
|
180
|
+
encryption: true,
|
|
181
|
+
trust_strategy: :trust_all_certificates
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
driver = Neo4j::Driver::GraphDatabase.driver(
|
|
185
|
+
'neo4j://localhost:7687',
|
|
186
|
+
Neo4j::Driver::AuthTokens.basic('neo4j', 'password'),
|
|
187
|
+
**config
|
|
188
|
+
)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### Connectivity check
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
if driver.verify_connectivity
|
|
195
|
+
puts "Driver is connected to the database"
|
|
196
|
+
else
|
|
197
|
+
puts "Driver cannot connect to the database"
|
|
198
|
+
end
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Sessions & transactions
|
|
202
|
+
|
|
203
|
+
The driver provides sessions to interact with the database and to execute queries.
|
|
204
|
+
|
|
205
|
+
#### Session
|
|
206
|
+
|
|
207
|
+
Sessions are lightweight and disposable database connections. Always close your sessions when done:
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
session = driver.session(database: 'neo4j')
|
|
211
|
+
begin
|
|
212
|
+
session.run('MATCH (n) RETURN n LIMIT 10')
|
|
213
|
+
ensure
|
|
214
|
+
session.close
|
|
215
|
+
end
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Or use a block that automatically closes the session:
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
driver.session(database: 'neo4j') do |session|
|
|
222
|
+
session.run('MATCH (n) RETURN n LIMIT 10')
|
|
223
|
+
end
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Session options:
|
|
227
|
+
|
|
228
|
+
```ruby
|
|
229
|
+
# Default database
|
|
230
|
+
session = driver.session
|
|
231
|
+
|
|
232
|
+
# Specific database
|
|
233
|
+
session = driver.session(database: 'neo4j')
|
|
234
|
+
|
|
235
|
+
# With access mode
|
|
236
|
+
session = driver.session(database: 'neo4j', default_access_mode: Neo4j::Driver::AccessMode::READ)
|
|
237
|
+
|
|
238
|
+
# With bookmarks for causal consistency
|
|
239
|
+
session = driver.session(
|
|
240
|
+
database: 'neo4j',
|
|
241
|
+
bookmarks: [Neo4j::Driver::Bookmark.from('bookmark-1')]
|
|
242
|
+
)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### Auto-commit transactions
|
|
246
|
+
|
|
247
|
+
For simple, one-off queries, use auto-commit transactions:
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
session.run('CREATE (n:Person {name: $name})', name: 'Alice')
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### Explicit transactions
|
|
254
|
+
|
|
255
|
+
For multiple queries that need to be executed as a unit, use explicit transactions:
|
|
256
|
+
|
|
257
|
+
```ruby
|
|
258
|
+
tx = session.begin_transaction
|
|
259
|
+
begin
|
|
260
|
+
tx.run('CREATE (n:Person {name: $name})', name: 'Alice')
|
|
261
|
+
tx.run('CREATE (n:Person {name: $name})', name: 'Bob')
|
|
262
|
+
tx.commit
|
|
263
|
+
rescue
|
|
264
|
+
tx.rollback
|
|
265
|
+
raise
|
|
266
|
+
end
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### Read transactions
|
|
270
|
+
|
|
271
|
+
Specifically for read operations:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
result = session.read_transaction do |tx|
|
|
275
|
+
tx.run('MATCH (n:Person) RETURN n.name').map { |record| record['n.name'] }
|
|
276
|
+
end
|
|
277
|
+
puts result
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
#### Write transactions
|
|
281
|
+
|
|
282
|
+
Specifically for write operations:
|
|
283
|
+
|
|
284
|
+
```ruby
|
|
285
|
+
session.write_transaction do |tx|
|
|
286
|
+
tx.run('CREATE (n:Person {name: $name})', name: 'Charlie')
|
|
287
|
+
end
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Working with results
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
result = session.run('MATCH (n:Person) RETURN n.name AS name, n.age AS age')
|
|
294
|
+
|
|
295
|
+
# Process results
|
|
296
|
+
result.each do |record|
|
|
297
|
+
puts "#{record['name']} is #{record['age']} years old"
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Check if there are more results
|
|
301
|
+
puts "Has more results: #{result.has_next?}"
|
|
302
|
+
|
|
303
|
+
# Get a single record
|
|
304
|
+
single = result.single
|
|
305
|
+
puts single['name'] if single
|
|
306
|
+
|
|
307
|
+
# Get keys available in the result
|
|
308
|
+
puts "Keys: #{result.keys}"
|
|
309
|
+
|
|
310
|
+
# Access by field index
|
|
311
|
+
result.each do |record|
|
|
312
|
+
puts "First field: #{record[0]}"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Convert to array
|
|
316
|
+
records = result.to_a
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Accessing Node and Relationship data
|
|
320
|
+
|
|
321
|
+
Working with graph entities:
|
|
322
|
+
|
|
323
|
+
```ruby
|
|
324
|
+
result = session.run('MATCH (p:Person)-[r:KNOWS]->(friend) RETURN p, r, friend')
|
|
325
|
+
|
|
326
|
+
result.each do |record|
|
|
327
|
+
# Working with nodes
|
|
328
|
+
person = record['p']
|
|
329
|
+
puts "Node ID: #{person.id}"
|
|
330
|
+
puts "Labels: #{person.labels.join(', ')}"
|
|
331
|
+
puts "Properties: #{person.properties}"
|
|
332
|
+
puts "Name property: #{person.properties['name']}"
|
|
333
|
+
|
|
334
|
+
# Working with relationships
|
|
335
|
+
relationship = record['r']
|
|
336
|
+
puts "Relationship ID: #{relationship.id}"
|
|
337
|
+
puts "Type: #{relationship.type}"
|
|
338
|
+
puts "Properties: #{relationship.properties}"
|
|
339
|
+
|
|
340
|
+
# Start and end nodes of the relationship
|
|
341
|
+
puts "Relationship: #{relationship.start_node_id} -> #{relationship.end_node_id}"
|
|
342
|
+
end
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Working with Paths
|
|
346
|
+
|
|
347
|
+
Processing paths returned from Cypher:
|
|
348
|
+
|
|
349
|
+
```ruby
|
|
350
|
+
result = session.run('MATCH p = (:Person)-[:KNOWS*]->(:Person) RETURN p')
|
|
351
|
+
|
|
352
|
+
result.each do |record|
|
|
353
|
+
path = record['p']
|
|
354
|
+
|
|
355
|
+
# Get all nodes in the path
|
|
356
|
+
nodes = path.nodes
|
|
357
|
+
puts "Nodes in path: #{nodes.map { |n| n.properties['name'] }.join(' -> ')}"
|
|
358
|
+
|
|
359
|
+
# Get all relationships in the path
|
|
360
|
+
relationships = path.relationships
|
|
361
|
+
puts "Relationship types: #{relationships.map(&:type).join(', ')}"
|
|
362
|
+
|
|
363
|
+
# Iterate through the path segments
|
|
364
|
+
path.each do |segment|
|
|
365
|
+
puts "#{segment.start_node.properties['name']} -[#{segment.relationship.type}]-> #{segment.end_node.properties['name']}"
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
#### Working with temporal types
|
|
371
|
+
|
|
372
|
+
Creating a node with properties of temporal types:
|
|
373
|
+
|
|
374
|
+
```ruby
|
|
375
|
+
session.run(
|
|
376
|
+
'CREATE (e:Event {datetime: $datetime, duration: $duration})',
|
|
377
|
+
datetime: DateTime.new(2025, 5, 5, 5, 55, 55), duration: 1.hour
|
|
378
|
+
)
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Querying temporal values:
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
session.run('MATCH (e:Event) LIMIT 1 RETURN e.datetime, e.duration').single.to_h
|
|
385
|
+
# => {"e.datetime": 2025-05-05 05:55:55 +0000, "e.duration": 3600 seconds}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Type mapping
|
|
33
389
|
|
|
34
|
-
|
|
390
|
+
The Neo4j Ruby Driver maps Cypher types to Ruby types:
|
|
35
391
|
|
|
36
|
-
|
|
392
|
+
| Cypher Type | Ruby Type |
|
|
393
|
+
|----------------|-----------------------------------------------|
|
|
394
|
+
| null | nil |
|
|
395
|
+
| List | Enumerable |
|
|
396
|
+
| Map | Hash (symbolized keys) |
|
|
397
|
+
| Boolean | TrueClass/FalseClass |
|
|
398
|
+
| Integer | Integer/String[^1] |
|
|
399
|
+
| Float | Float |
|
|
400
|
+
| String | String/Symbol[^2] (encoding: UTF-8) |
|
|
401
|
+
| ByteArray | String (encoding: BINARY) |
|
|
402
|
+
| Date | Date |
|
|
403
|
+
| Zoned Time | Neo4j::Driver::Types::OffsetTime |
|
|
404
|
+
| Local Time | Neo4j::Driver::Types::LocalTime |
|
|
405
|
+
| Zoned DateTime | Time/ActiveSupport::TimeWithZone/DateTime[^3] |
|
|
406
|
+
| Local DateTime | Neo4j::Driver::Types::LocalDateTime |
|
|
407
|
+
| Duration | ActiveSupport::Duration |
|
|
408
|
+
| Point | Neo4j::Driver::Types::Point |
|
|
409
|
+
| Node | Neo4j::Driver::Types::Node |
|
|
410
|
+
| Relationship | Neo4j::Driver::Types::Relationship |
|
|
411
|
+
| Path | Neo4j::Driver::Types::Path |
|
|
37
412
|
|
|
38
|
-
|
|
413
|
+
[^1]: An Integer smaller than -2 ** 63 or larger than 2 ** 63 will always be implicitly converted to String
|
|
414
|
+
[^2]: A Symbol passed as a parameter will always be implicitly converted to String. All Strings other than BINARY
|
|
415
|
+
encoded are converted to UTF-8 when stored in Neo4j
|
|
416
|
+
[^3]: A Ruby DateTime passed as a parameter will always be implicitly converted to Time
|
|
39
417
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
## Testing
|
|
418
|
+
### Advanced
|
|
43
419
|
|
|
44
|
-
|
|
420
|
+
#### Connection pooling
|
|
45
421
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
422
|
+
The driver handles connection pooling automatically. Configure the connection pool:
|
|
423
|
+
|
|
424
|
+
```ruby
|
|
425
|
+
config = {
|
|
426
|
+
max_connection_pool_size: 100,
|
|
427
|
+
max_connection_lifetime: 1.hour
|
|
428
|
+
}
|
|
52
429
|
|
|
53
|
-
|
|
54
|
-
```console
|
|
55
|
-
$ bin/setup
|
|
56
|
-
$ rspec spec
|
|
430
|
+
driver = Neo4j::Driver::GraphDatabase.driver('neo4j://localhost:7687', auth, **config)
|
|
57
431
|
```
|
|
58
432
|
|
|
59
|
-
|
|
433
|
+
#### Logging
|
|
60
434
|
|
|
61
|
-
|
|
435
|
+
Configure logging for the driver:
|
|
62
436
|
|
|
63
|
-
```
|
|
64
|
-
|
|
437
|
+
```ruby
|
|
438
|
+
config = {
|
|
439
|
+
logger: Logger.new(STDOUT).tap { |log| log.level = Logger::DEBUG }
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
driver = Neo4j::Driver::GraphDatabase.driver('neo4j://localhost:7687', auth, **config)
|
|
65
443
|
```
|
|
66
444
|
|
|
67
|
-
|
|
445
|
+
## For Driver Engineers
|
|
68
446
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
447
|
+
This gem includes 2 different implementations: a Java driver wrapper and a pure Ruby driver, so you will have to run
|
|
448
|
+
this command every time you switch the Ruby engine:
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
bin/setup
|
|
74
452
|
```
|
|
75
453
|
|
|
454
|
+
### Testing
|
|
455
|
+
|
|
456
|
+
There are two sets of tests for the driver. To run the specs placed in this repository, use a normal rspec command:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
rspec spec
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
To run the [Testkit](https://github.com/neo4j-drivers/testkit) that is used to test all Neo4j driver implementations,
|
|
463
|
+
use the following:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
git clone git@github.com:neo4j-drivers/testkit.git
|
|
467
|
+
cd testkit
|
|
468
|
+
export TEST_DRIVER_NAME=ruby
|
|
469
|
+
export TEST_DRIVER_REPO=`realpath ../neo4j-ruby-driver`
|
|
470
|
+
export TEST_NEO4J_PASS=password
|
|
471
|
+
python3 main.py --tests UNIT_TESTS --configs 4.3-enterprise
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Please refer to the [Testkit](https://github.com/neo4j-drivers/testkit) documentation to learn more about its features.
|
|
475
|
+
|
|
76
476
|
## Contributing
|
|
77
477
|
|
|
78
|
-
Suggestions, improvements, bug reports and pull requests are welcome on GitHub
|
|
478
|
+
Suggestions, improvements, bug reports and pull requests are welcome on GitHub
|
|
479
|
+
at https://github.com/neo4jrb/neo4j-ruby-driver.
|
|
79
480
|
|
|
80
481
|
## License
|
|
81
482
|
|
|
82
483
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
83
|
-
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Neo4j
|
|
4
4
|
module Driver
|
|
5
|
-
module
|
|
6
|
-
def
|
|
5
|
+
module AutoCloseable
|
|
6
|
+
def auto_closeable(*methods)
|
|
7
7
|
prepend with_block_definer(methods)
|
|
8
8
|
end
|
|
9
9
|
|
|
@@ -13,15 +13,15 @@ module Neo4j
|
|
|
13
13
|
Module.new do
|
|
14
14
|
methods.each do |method|
|
|
15
15
|
define_method(method) do |*args, **kwargs, &block|
|
|
16
|
-
|
|
16
|
+
closeable = super(*args, **kwargs)
|
|
17
17
|
if block
|
|
18
18
|
begin
|
|
19
|
-
block.arity.zero? ?
|
|
19
|
+
block.arity.zero? ? closeable.instance_eval(&block) : block.call(closeable)
|
|
20
20
|
ensure
|
|
21
|
-
|
|
21
|
+
closeable&.close
|
|
22
22
|
end
|
|
23
23
|
else
|
|
24
|
-
|
|
24
|
+
closeable
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Neo4j
|
|
2
|
+
module Driver
|
|
3
|
+
module Internal
|
|
4
|
+
module Deprecator
|
|
5
|
+
def self.deprecator
|
|
6
|
+
@deprecator ||= ActiveSupport::Deprecation.new('6.0', 'neo4j-ruby-driver')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.log_warning(old_method, new_method, version)
|
|
10
|
+
deprecator.deprecation_warning(old_method, new_method)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -13,8 +13,8 @@ module Neo4j
|
|
|
13
13
|
# Reconstruct bookmark from \bookmarks string values.
|
|
14
14
|
# @param values values obtained from a previous bookmark.
|
|
15
15
|
# @return A bookmark.
|
|
16
|
-
def self.from(
|
|
17
|
-
Internal::InternalBookmark.
|
|
16
|
+
def self.from(value)
|
|
17
|
+
Internal::InternalBookmark.new(value)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Neo4j::Driver
|
|
4
4
|
class GraphDatabase
|
|
5
5
|
class << self
|
|
6
|
-
extend
|
|
7
|
-
|
|
6
|
+
extend AutoCloseable
|
|
7
|
+
auto_closeable :driver, :routing_driver
|
|
8
8
|
|
|
9
9
|
def driver(uri, auth_token = nil, **config)
|
|
10
10
|
internal_driver(uri, auth_token, config)
|
|
@@ -2,16 +2,16 @@ module Neo4j::Driver
|
|
|
2
2
|
module Internal
|
|
3
3
|
module Async
|
|
4
4
|
class ImmutableConnectionContext
|
|
5
|
-
attr :database_name, :mode, :
|
|
5
|
+
attr :database_name, :mode, :rediscovery_bookmarks, :impersonated_user
|
|
6
6
|
|
|
7
|
-
def initialize(database_name,
|
|
7
|
+
def initialize(database_name, bookmarks, mode)
|
|
8
8
|
@database_name = database_name
|
|
9
|
-
@
|
|
9
|
+
@rediscovery_bookmarks = bookmarks
|
|
10
10
|
@mode = mode
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
SINGLE_DB_CONTEXT = new(DatabaseNameUtil::DEFAULT_DATABASE,
|
|
14
|
-
MULTI_DB_CONTEXT = new(DatabaseNameUtil::SYSTEM_DATABASE,
|
|
13
|
+
SINGLE_DB_CONTEXT = new(DatabaseNameUtil::DEFAULT_DATABASE, [], AccessMode::READ)
|
|
14
|
+
MULTI_DB_CONTEXT = new(DatabaseNameUtil::SYSTEM_DATABASE, [], AccessMode::READ)
|
|
15
15
|
|
|
16
16
|
# A simple context is used to test connectivity with a remote server/cluster. As long as there is a read only service, the connection shall be established
|
|
17
17
|
# successfully. Depending on whether multidb is supported or not, this method returns different context for routing table discovery.
|
|
@@ -6,7 +6,7 @@ module Neo4j::Driver
|
|
|
6
6
|
@session = session
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
delegate :
|
|
9
|
+
delegate :last_bookmarks, :close_async, to: :@session
|
|
10
10
|
|
|
11
11
|
def run_async(query, parameters = {}, config = {})
|
|
12
12
|
@session.run_async(org.neo4j.driver.Query.new(query, **parameters), **config)
|
|
@@ -11,7 +11,7 @@ module Neo4j::Driver
|
|
|
11
11
|
@log = Logging::PrefixedLogger.new("[#{hash}]", logger)
|
|
12
12
|
@bookmark_holder = bookmark_holder
|
|
13
13
|
# @database_name = database_name.database_name
|
|
14
|
-
@connection_context = NetworkSessionConnectionContext.new(database_name, @bookmark_holder.
|
|
14
|
+
@connection_context = NetworkSessionConnectionContext.new(database_name, @bookmark_holder.bookmarks, impersonated_user)
|
|
15
15
|
@fetch_size = fetch_size
|
|
16
16
|
@open = Concurrent::AtomicBoolean.new(true)
|
|
17
17
|
end
|
|
@@ -28,7 +28,7 @@ module Neo4j::Driver
|
|
|
28
28
|
acquire_connection(mode).then do |connection|
|
|
29
29
|
ImpersonationUtil.ensure_impersonation_support(connection, connection.impersonated_user)
|
|
30
30
|
tx = UnmanagedTransaction.new(connection, @bookmark_holder, @fetch_size)
|
|
31
|
-
tx.begin_async(@bookmark_holder.
|
|
31
|
+
tx.begin_async(@bookmark_holder.bookmarks, config)
|
|
32
32
|
end&.tap { |new_transaction| @transaction = new_transaction }
|
|
33
33
|
end
|
|
34
34
|
|
|
@@ -42,8 +42,8 @@ module Neo4j::Driver
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def
|
|
46
|
-
@bookmark_holder.
|
|
45
|
+
def last_bookmarks
|
|
46
|
+
@bookmark_holder.bookmarks
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def release_connection_async
|
|
@@ -131,11 +131,11 @@ module Neo4j::Driver
|
|
|
131
131
|
# It has to be the initial bookmark given at the creation of the session.
|
|
132
132
|
# As only that bookmark could carry extra system bookmarks
|
|
133
133
|
attr_accessor :database_name
|
|
134
|
-
attr :mode, :
|
|
134
|
+
attr :mode, :rediscovery_bookmarks, :impersonated_user
|
|
135
135
|
|
|
136
|
-
def initialize(database_name,
|
|
136
|
+
def initialize(database_name, bookmarks, impersonated_user)
|
|
137
137
|
@database_name = database_name
|
|
138
|
-
@
|
|
138
|
+
@rediscovery_bookmarks = bookmarks
|
|
139
139
|
@impersonated_user = impersonated_user
|
|
140
140
|
end
|
|
141
141
|
|
|
@@ -35,8 +35,8 @@ module Neo4j::Driver
|
|
|
35
35
|
@state = State::ACTIVE
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def begin_async(
|
|
39
|
-
@protocol.begin_transaction(@connection,
|
|
38
|
+
def begin_async(initial_bookmarks, config)
|
|
39
|
+
@protocol.begin_transaction(@connection, initial_bookmarks, config)
|
|
40
40
|
self
|
|
41
41
|
rescue Neo4j::Driver::Exceptions::AuthorizationExpiredException
|
|
42
42
|
@connection.terminate_and_release(Neo4j::Driver::Exceptions::AuthorizationExpiredException::DESCRIPTION)
|
|
@@ -175,7 +175,7 @@ module Neo4j::Driver
|
|
|
175
175
|
if exception
|
|
176
176
|
Util::ResultHolder.failed(exception)
|
|
177
177
|
else
|
|
178
|
-
@protocol.commit_transaction(@connection).then
|
|
178
|
+
@protocol.commit_transaction(@connection).then { @bookmark_holder.bookmarks = Set[it] }
|
|
179
179
|
end
|
|
180
180
|
end
|
|
181
181
|
|
|
@@ -33,7 +33,7 @@ module Neo4j::Driver
|
|
|
33
33
|
@log.debug("Routing table for database '#{@database_name.description}' is stale. #{@routing_table}")
|
|
34
34
|
|
|
35
35
|
fresh_cluster_composition_fetched(
|
|
36
|
-
@rediscovery.lookup_cluster_composition(@routing_table, @connection_pool, context.
|
|
36
|
+
@rediscovery.lookup_cluster_composition(@routing_table, @connection_pool, context.rediscovery_bookmarks,
|
|
37
37
|
nil))
|
|
38
38
|
else
|
|
39
39
|
# existing routing table is fresh, use it
|
|
@@ -8,9 +8,7 @@ module Neo4j::Driver
|
|
|
8
8
|
@result_holder = result_holder
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def on_success(metadata)
|
|
12
|
-
@result_holder.succeed(metadata[:bookmark]&.then(&InternalBookmark.method(:parse)))
|
|
13
|
-
end
|
|
11
|
+
def on_success(metadata) = @result_holder.succeed(Util::MetadataExtractor.extract_bookmark(metadata))
|
|
14
12
|
|
|
15
13
|
def on_failure(error)
|
|
16
14
|
@result_holder.fail(error)
|
|
@@ -3,7 +3,7 @@ module Neo4j::Driver
|
|
|
3
3
|
module Handlers
|
|
4
4
|
module Pulln
|
|
5
5
|
class AutoPullResponseHandler < BasicPullResponseHandler
|
|
6
|
-
delegate :signal, to: :@
|
|
6
|
+
delegate :signal, to: :@queue_notification
|
|
7
7
|
LONG_MAX_VALUE = 2 ** 63 - 1
|
|
8
8
|
|
|
9
9
|
def initialize(query, run_response_handler, connection, metadata_extractor, completion_listener, fetch_size)
|
|
@@ -19,7 +19,8 @@ module Neo4j::Driver
|
|
|
19
19
|
@low_record_watermark = fetch_size * 0.3
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
@
|
|
22
|
+
@queue_notification = ::Async::Notification.new
|
|
23
|
+
@records = ::Async::Queue.new(available: @queue_notification)
|
|
23
24
|
@auto_pull_enabled = true
|
|
24
25
|
|
|
25
26
|
install_record_and_summary_consumers
|
|
@@ -9,7 +9,7 @@ module Neo4j::Driver
|
|
|
9
9
|
|
|
10
10
|
def after_success(metadata)
|
|
11
11
|
release_connection
|
|
12
|
-
@bookmark_holder.
|
|
12
|
+
@bookmark_holder.bookmarks = Array(Util::MetadataExtractor.extract_bookmark(metadata)).to_set
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def after_failure(error)
|
|
@@ -1,36 +1,9 @@
|
|
|
1
1
|
module Neo4j::Driver
|
|
2
2
|
module Internal
|
|
3
|
-
class InternalBookmark
|
|
3
|
+
class InternalBookmark < String
|
|
4
4
|
include Bookmark
|
|
5
|
-
attr :values
|
|
6
|
-
delegate :hash, :empty?, to: :values
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
@values = values.to_set
|
|
10
|
-
end
|
|
11
|
-
EMPTY = new.freeze
|
|
12
|
-
|
|
13
|
-
def ==(other)
|
|
14
|
-
equal?(other) || self.class == other.class && values == other.values
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
alias eql? ==
|
|
18
|
-
|
|
19
|
-
def to_s
|
|
20
|
-
"Bookmark{values=#{values}}"
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
class << self
|
|
24
|
-
def empty
|
|
25
|
-
EMPTY
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def from(*bookmarks)
|
|
29
|
-
new(*bookmarks.reduce(Set.new) { |set, bookmark| set + bookmark.values })
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
alias parse new
|
|
33
|
-
end
|
|
6
|
+
def to_s = "Bookmark{value=#{super}}"
|
|
34
7
|
end
|
|
35
8
|
end
|
|
36
9
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module Neo4j::Driver
|
|
2
2
|
module Internal
|
|
3
3
|
class InternalDriver
|
|
4
|
-
extend
|
|
4
|
+
extend AutoCloseable
|
|
5
5
|
extend Synchronizable
|
|
6
6
|
attr_reader :session_factory, :metrics_provider
|
|
7
7
|
# delegate :verify_connectivity, to: :session_factory
|
|
8
8
|
delegate :metrics, :metrics_enabled?, to: :metrics_provider
|
|
9
|
-
|
|
9
|
+
auto_closeable :session
|
|
10
10
|
sync :close, :supports_multi_db?, :verify_connectivity, :session
|
|
11
11
|
|
|
12
12
|
def initialize(security_plan, session_factory, metrics_provider, logger)
|
|
@@ -25,6 +25,15 @@ module Neo4j::Driver
|
|
|
25
25
|
InternalAsyncSession.new(new_session(**session_config))
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def execute_query(query, auth_token = nil, config = {}, **parameters)
|
|
29
|
+
# TODO: auth_token not implemented yet
|
|
30
|
+
session(**config.slice(:bookmarks, :database, :impersonated_user, :default_access_mode, :fetch_size)) do |session|
|
|
31
|
+
session.run(query, parameters, config.slice(:timeout, :metadata)).then do
|
|
32
|
+
EagerResultValue.new(it.keys, it.to_a, it.consume)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
28
37
|
def encrypted?
|
|
29
38
|
assert_open!
|
|
30
39
|
@security_plan.requires_encryption?
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module Neo4j::Driver
|
|
2
2
|
module Internal
|
|
3
3
|
class InternalSession
|
|
4
|
-
extend
|
|
4
|
+
extend AutoCloseable
|
|
5
5
|
extend Synchronizable
|
|
6
6
|
# include Ext::RunOverride
|
|
7
|
-
delegate :open?, :
|
|
8
|
-
|
|
7
|
+
delegate :open?, :last_bookmarks, to: :@session
|
|
8
|
+
auto_closeable :begin_transaction
|
|
9
9
|
sync :close, :begin_transaction, :run, :transaction
|
|
10
10
|
|
|
11
11
|
def initialize(session)
|
|
@@ -38,15 +38,29 @@ module Neo4j::Driver
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def read_transaction(**config, &block)
|
|
41
|
+
Deprecator.log_warning(:read_transaction, :execute_read, '6.0')
|
|
41
42
|
transaction(AccessMode::READ, **config, &block)
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def write_transaction(**config, &block)
|
|
46
|
+
Deprecator.log_warning(:write_transaction, :execute_write, '6.0')
|
|
45
47
|
transaction(AccessMode::WRITE, **config, &block)
|
|
46
48
|
end
|
|
47
49
|
|
|
50
|
+
def execute_read(**config, &block)
|
|
51
|
+
delegating_transaction(AccessMode::READ, **config, &block)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def execute_write(**config, &block)
|
|
55
|
+
delegating_transaction(AccessMode::WRITE, **config, &block)
|
|
56
|
+
end
|
|
57
|
+
|
|
48
58
|
private
|
|
49
59
|
|
|
60
|
+
def delegating_transaction(mode, **config, &block)
|
|
61
|
+
transaction(mode, **config) { |tx| block.call(DelegatingTransaction.new(tx)) }
|
|
62
|
+
end
|
|
63
|
+
|
|
50
64
|
def transaction(mode, **config)
|
|
51
65
|
# use different code path compared to async so that work is executed in the caller thread
|
|
52
66
|
# caller thread will also be the one who sleeps between retries;
|
|
@@ -8,7 +8,7 @@ module Neo4j::Driver
|
|
|
8
8
|
Util::Preconditions.check_argument(message, Request::RouteMessage)
|
|
9
9
|
packer.pack_struct_header(3, message.signature)
|
|
10
10
|
packer.pack(message.routing_context)
|
|
11
|
-
packer.pack(message.bookmark
|
|
11
|
+
packer.pack(message.bookmark || [])
|
|
12
12
|
packer.pack(option(message))
|
|
13
13
|
end
|
|
14
14
|
|
|
@@ -5,10 +5,10 @@ module Neo4j::Driver
|
|
|
5
5
|
class BeginMessage < MessageWithMetadata
|
|
6
6
|
SIGNATURE = 0x11
|
|
7
7
|
|
|
8
|
-
def initialize(
|
|
8
|
+
def initialize(bookmarks, config, database_name, mode, impersonated_user)
|
|
9
9
|
super(Request::TransactionMetadataBuilder.build_metadata(
|
|
10
10
|
timeout: config[:timeout], tx_metadata: config[:metadata], database_name: database_name, mode: mode,
|
|
11
|
-
|
|
11
|
+
bookmarks: bookmarks, impersonated_user: impersonated_user))
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def signature
|
|
@@ -7,10 +7,10 @@ module Neo4j::Driver
|
|
|
7
7
|
attr_reader :query, :parameters
|
|
8
8
|
|
|
9
9
|
class << self
|
|
10
|
-
def auto_commit_tx_run_message(query, config, database_name, mode,
|
|
10
|
+
def auto_commit_tx_run_message(query, config, database_name, mode, bookmarks, impersonated_user)
|
|
11
11
|
metadata = Request::TransactionMetadataBuilder.build_metadata(
|
|
12
12
|
timeout: config[:timeout], tx_metadata: config[:metadata], database_name: database_name, mode: mode,
|
|
13
|
-
|
|
13
|
+
bookmarks: bookmarks, impersonated_user: impersonated_user)
|
|
14
14
|
new(query.text, query.parameters, metadata)
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -6,9 +6,9 @@ module Neo4j::Driver
|
|
|
6
6
|
MODE_READ_VALUE = 'r'
|
|
7
7
|
|
|
8
8
|
class << self
|
|
9
|
-
def build_metadata(timeout:, tx_metadata:, mode:,
|
|
9
|
+
def build_metadata(timeout:, tx_metadata:, mode:, bookmarks:, impersonated_user:,
|
|
10
10
|
database_name: DatabaseNameUtil.default_database)
|
|
11
|
-
{ bookmarks:
|
|
11
|
+
{ bookmarks: bookmarks.presence,
|
|
12
12
|
tx_timeout: timeout&.then(&DurationNormalizer.method(:milliseconds)),
|
|
13
13
|
tx_metadata: tx_metadata.presence,
|
|
14
14
|
mode: (MODE_READ_VALUE if mode == AccessMode::READ),
|
|
@@ -30,9 +30,9 @@ module Neo4j::Driver
|
|
|
30
30
|
message_dispatcher.prepare_to_close_channel
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def begin_transaction(connection,
|
|
33
|
+
def begin_transaction(connection, bookmarks, config)
|
|
34
34
|
verify_database_name_before_transaction(connection.database_name)
|
|
35
|
-
begin_message = Request::BeginMessage.new(
|
|
35
|
+
begin_message = Request::BeginMessage.new(bookmarks, config, connection.database_name, connection.mode, connection.impersonated_user)
|
|
36
36
|
connection.write_and_flush(begin_message, Handlers::BeginTxResponseHandler.new)
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -51,7 +51,7 @@ module Neo4j::Driver
|
|
|
51
51
|
def run_in_auto_commit_transaction(connection, query, bookmark_holder, config, fetch_size)
|
|
52
52
|
verify_database_name_before_transaction(connection.database_name)
|
|
53
53
|
|
|
54
|
-
run_message = Request::RunWithMetadataMessage.auto_commit_tx_run_message(query, config, connection.database_name, connection.mode, bookmark_holder.
|
|
54
|
+
run_message = Request::RunWithMetadataMessage.auto_commit_tx_run_message(query, config, connection.database_name, connection.mode, bookmark_holder.bookmarks, connection.impersonated_user)
|
|
55
55
|
|
|
56
56
|
build_result_cursor_factory(connection, query, bookmark_holder, nil, run_message, fetch_size)
|
|
57
57
|
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
module Neo4j::Driver
|
|
2
2
|
module Internal
|
|
3
3
|
class ReadOnlyBookmarkHolder
|
|
4
|
-
attr_reader :
|
|
4
|
+
attr_reader :bookmarks
|
|
5
5
|
|
|
6
|
-
def initialize(
|
|
7
|
-
@
|
|
6
|
+
def initialize(bookmarks = Set[])
|
|
7
|
+
@bookmarks = bookmarks
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def
|
|
10
|
+
def bookmarks=(_value) end
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -13,7 +13,7 @@ module Neo4j::Driver
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def new_instance(fetch_size: @default_fetch_size, default_access_mode: AccessMode::WRITE, **config)
|
|
16
|
-
bookmark_holder = DefaultBookmarkHolder.new(
|
|
16
|
+
bookmark_holder = DefaultBookmarkHolder.new(Array(config[:bookmarks]).to_set)
|
|
17
17
|
create_session(parse_database_name(config), default_access_mode, bookmark_holder, fetch_size, config[:impersonated_user])
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Neo4j::Driver::Internal::Summary
|
|
4
|
-
class InternalNotification < Struct.new(:code, :title, :description, :
|
|
4
|
+
class InternalNotification < Struct.new(:code, :title, :description, :severity_level, :position)
|
|
5
5
|
|
|
6
6
|
VALUE_TO_NOTIFICATION = lambda do |value|
|
|
7
|
-
severity = value[:severity]
|
|
7
|
+
severity = value[:severity]
|
|
8
8
|
|
|
9
9
|
position = value[:position]&.then do |pos_value|
|
|
10
10
|
InternalInputPosition.new(*pos_value.values_at(:offset, :line, :column).map(&:to_i))
|
|
@@ -35,13 +35,7 @@ module Neo4j::Driver
|
|
|
35
35
|
Summary::InternalDatabaseInfo::DEFAULT_DATABASE_INFO
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def
|
|
39
|
-
bookmark_value = metadata[:bookmark]
|
|
40
|
-
|
|
41
|
-
return InternalBookmark.parse(bookmark_value) if bookmark_value&.is_a? String
|
|
42
|
-
|
|
43
|
-
InternalBookmark.empty
|
|
44
|
-
end
|
|
38
|
+
def extract_bookmark(metadata) = metadata[:bookmark]&.then(&InternalBookmark.method(:new))
|
|
45
39
|
|
|
46
40
|
def extract_neo4j_server_version(metadata)
|
|
47
41
|
server_value = extract_server(metadata)
|
data/ruby/neo4j/driver.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'active_support/core_ext/array/grouping'
|
|
4
4
|
require 'active_support/core_ext/hash/keys'
|
|
5
|
+
require 'active_support/core_ext/object/blank'
|
|
5
6
|
require 'active_support/logger'
|
|
6
7
|
require 'async/io'
|
|
7
8
|
require 'async/io/stream'
|
|
@@ -14,6 +15,7 @@ module Neo4j
|
|
|
14
15
|
module Driver
|
|
15
16
|
Loader.load
|
|
16
17
|
|
|
18
|
+
EagerResult = Neo4j::Driver::Internal::EagerResultValue
|
|
17
19
|
Record = Neo4j::Driver::Internal::InternalRecord
|
|
18
20
|
Result = Neo4j::Driver::Internal::InternalResult
|
|
19
21
|
Transaction = Neo4j::Driver::Internal::InternalTransaction
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: neo4j-ruby-driver
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 6.0.0.alpha.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Heinrich Klobuczek
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|
|
@@ -24,49 +24,55 @@ dependencies:
|
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '7.1'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
27
|
+
name: csv
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
32
|
+
version: '3.0'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version:
|
|
39
|
+
version: '3.0'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: zeitwerk
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
46
|
+
version: 2.1.10
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
|
-
- - "
|
|
51
|
+
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
53
|
+
version: 2.1.10
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: async
|
|
55
|
+
name: async
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
60
|
+
version: '2.13'
|
|
61
|
+
- - "<"
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '2.24'
|
|
61
64
|
type: :runtime
|
|
62
65
|
prerelease: false
|
|
63
66
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
67
|
requirements:
|
|
65
68
|
- - ">="
|
|
66
69
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
70
|
+
version: '2.13'
|
|
71
|
+
- - "<"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '2.24'
|
|
68
74
|
- !ruby/object:Gem::Dependency
|
|
69
|
-
name:
|
|
75
|
+
name: async-io
|
|
70
76
|
requirement: !ruby/object:Gem::Requirement
|
|
71
77
|
requirements:
|
|
72
78
|
- - ">="
|
|
@@ -80,7 +86,7 @@ dependencies:
|
|
|
80
86
|
- !ruby/object:Gem::Version
|
|
81
87
|
version: '0'
|
|
82
88
|
- !ruby/object:Gem::Dependency
|
|
83
|
-
name:
|
|
89
|
+
name: connection_pool
|
|
84
90
|
requirement: !ruby/object:Gem::Requirement
|
|
85
91
|
requirements:
|
|
86
92
|
- - ">="
|
|
@@ -224,7 +230,7 @@ files:
|
|
|
224
230
|
- README.md
|
|
225
231
|
- lib/neo4j-ruby-driver.rb
|
|
226
232
|
- lib/neo4j-ruby-driver_loader.rb
|
|
227
|
-
- lib/neo4j/driver/
|
|
233
|
+
- lib/neo4j/driver/auto_closeable.rb
|
|
228
234
|
- lib/neo4j/driver/exceptions/authentication_exception.rb
|
|
229
235
|
- lib/neo4j/driver/exceptions/authorization_expired_exception.rb
|
|
230
236
|
- lib/neo4j/driver/exceptions/certificate_exception.rb
|
|
@@ -252,6 +258,7 @@ files:
|
|
|
252
258
|
- lib/neo4j/driver/exceptions/value/unsizable.rb
|
|
253
259
|
- lib/neo4j/driver/exceptions/value/value_exception.rb
|
|
254
260
|
- lib/neo4j/driver/internal/bolt_server_address.rb
|
|
261
|
+
- lib/neo4j/driver/internal/deprecator.rb
|
|
255
262
|
- lib/neo4j/driver/internal/duration_normalizer.rb
|
|
256
263
|
- lib/neo4j/driver/internal/validator.rb
|
|
257
264
|
- lib/neo4j/driver/summary/query_type.rb
|
|
@@ -339,8 +346,10 @@ files:
|
|
|
339
346
|
- ruby/neo4j/driver/internal/database_name_util.rb
|
|
340
347
|
- ruby/neo4j/driver/internal/default_bookmark_holder.rb
|
|
341
348
|
- ruby/neo4j/driver/internal/default_domain_name_resolver.rb
|
|
349
|
+
- ruby/neo4j/driver/internal/delegating_transaction.rb
|
|
342
350
|
- ruby/neo4j/driver/internal/direct_connection_provider.rb
|
|
343
351
|
- ruby/neo4j/driver/internal/driver_factory.rb
|
|
352
|
+
- ruby/neo4j/driver/internal/eager_result_value.rb
|
|
344
353
|
- ruby/neo4j/driver/internal/handlers/begin_tx_response_handler.rb
|
|
345
354
|
- ruby/neo4j/driver/internal/handlers/channel_releasing_reset_response_handler.rb
|
|
346
355
|
- ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb
|
|
@@ -524,7 +533,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
524
533
|
- !ruby/object:Gem::Version
|
|
525
534
|
version: '0'
|
|
526
535
|
requirements: []
|
|
527
|
-
rubygems_version: 3.6.
|
|
536
|
+
rubygems_version: 3.6.9
|
|
528
537
|
specification_version: 4
|
|
529
538
|
summary: ''
|
|
530
539
|
test_files: []
|