neo4j_bolt 0.3.0 → 0.4.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 +154 -115
- data/bin/neo4j_bolt +5 -5
- data/lib/neo4j_bolt/version.rb +1 -1
- data/lib/neo4j_bolt.rb +528 -1037
- metadata +29 -29
- data/.gitignore +0 -11
- data/.rspec +0 -3
- data/.travis.yml +0 -6
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -43
- data/Rakefile +0 -6
- data/bin/setup +0 -8
- data/movie_graph.svg +0 -259
- data/neo4j_bolt.gemspec +0 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a0582f714d9d1c8793edb70481db6a2ff5548200b14ad55c1f44b0859270b7d9
|
|
4
|
+
data.tar.gz: 73e300f77c941909738411fdbd9ce6b72ea6ef73a72a10184395e2228ce6a52c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e5debab26d8b655b1fa3fc1e4c877308e8221345aec76844d2b423ef4fe3061bc535241566f9af63ba889274b944c81efbad9daeadf1cc9b0ed166c82a18417
|
|
7
|
+
data.tar.gz: 65ed32647603bdeaf9bbca19335a0ca6fdccf5b17f3aaa11e19ef48868b242731a34b3cba74ff9475dbd5db94e6bf15f44f60110dca5adb8fe83d9709ef45a3d
|
data/README.md
CHANGED
|
@@ -1,196 +1,235 @@
|
|
|
1
1
|
# Neo4jBolt
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
- data types:
|
|
9
|
-
- supported: Null, Boolean, Integer, Float, String, List, Dictionary, Structure
|
|
10
|
-
- not supported: Bytes
|
|
11
|
-
- structures:
|
|
12
|
-
- supported: Node, Relationship
|
|
13
|
-
- not supported: UnboundRelationship, Path, Date, Time, LocalTime, DateTime, DateTimeZoneId, LocalDateTime, Duration, Point2D, Point3D
|
|
14
|
-
- Bolt Protocol:
|
|
15
|
-
- supported: transactions
|
|
16
|
-
- not supported:
|
|
17
|
-
- auto transactions (all transactions are explicit in Neo4jBolt)
|
|
18
|
-
- routing
|
|
19
|
-
- interrupt
|
|
3
|
+
Neo4jBolt 0.4 is a small compatibility and convenience layer for Ruby applications, backed by [neo4j-ruby-driver](https://github.com/neo4jrb/neo4j-ruby-driver). It preserves the straightforward `Neo4jBolt` application API while delegating connections, pooling, Bolt negotiation, protocol state, PackStream, reconnects, and modern Neo4j value support to the upstream driver.
|
|
4
|
+
|
|
5
|
+
Neo4jBolt no longer implements the Bolt wire protocol itself.
|
|
6
|
+
|
|
7
|
+
`0.4.0.pre1` requires Ruby 3.4 or newer and pins `neo4j-ruby-driver` to `6.2.1.beta.4`. Applications on older Rubies can remain on the Neo4jBolt 0.3.x line; this prerelease is intentionally not an automatic upgrade for them.
|
|
20
8
|
|
|
21
9
|
## Installation
|
|
22
10
|
|
|
23
|
-
|
|
11
|
+
For this prerelease, specify the version explicitly:
|
|
24
12
|
|
|
25
13
|
```ruby
|
|
26
|
-
gem
|
|
14
|
+
gem "neo4j_bolt", "0.4.0.pre1"
|
|
27
15
|
```
|
|
28
16
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
$ bundle install
|
|
17
|
+
Then run `bundle install`. A running Neo4j database is required.
|
|
32
18
|
|
|
33
|
-
|
|
19
|
+
## Connecting
|
|
34
20
|
|
|
35
|
-
|
|
21
|
+
The existing host, port, and verbosity settings remain available:
|
|
36
22
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
```ruby
|
|
24
|
+
Neo4jBolt.bolt_host = "localhost"
|
|
25
|
+
Neo4jBolt.bolt_port = 7687
|
|
26
|
+
Neo4jBolt.bolt_verbosity = 0
|
|
41
27
|
```
|
|
42
28
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
## Connecting to a Neo4j database
|
|
46
|
-
|
|
47
|
-
Specify your Bolt host and port (if you omit this it will be localhost:7687 by default):
|
|
29
|
+
Applications can include the module as before:
|
|
48
30
|
|
|
49
31
|
```ruby
|
|
50
|
-
Neo4jBolt
|
|
51
|
-
Neo4jBolt.bolt_port = 7687
|
|
32
|
+
include Neo4jBolt
|
|
52
33
|
```
|
|
53
34
|
|
|
54
|
-
|
|
35
|
+
They can also extend another object or call the convenience methods directly on `Neo4jBolt`.
|
|
36
|
+
|
|
37
|
+
One lazily initialized upstream driver owns a thread-safe connection pool. Each standalone query uses a short-lived session and returns its connection to that pool after its result has been consumed. Queries from several Puma threads are not globally serialized.
|
|
38
|
+
|
|
39
|
+
`cleanup_neo4j` safely retires the driver and its pool after active operations finish. A later query creates a new driver. Do not call cleanup from inside an active query or transaction on the same thread.
|
|
55
40
|
|
|
56
41
|
## Running queries
|
|
57
42
|
|
|
58
|
-
|
|
43
|
+
Materialize all result rows:
|
|
59
44
|
|
|
60
45
|
```ruby
|
|
61
|
-
|
|
46
|
+
rows = neo4j_query("MATCH (person:Person) RETURN person.name AS name")
|
|
47
|
+
puts rows.first["name"]
|
|
62
48
|
```
|
|
63
|
-
|
|
49
|
+
|
|
50
|
+
Or process records incrementally without first materializing the complete result:
|
|
64
51
|
|
|
65
52
|
```ruby
|
|
66
|
-
neo4j_query("MATCH (
|
|
67
|
-
|
|
53
|
+
neo4j_query("MATCH (person:Person) RETURN person") do |row|
|
|
54
|
+
puts row["person"][:name]
|
|
68
55
|
end
|
|
69
56
|
```
|
|
70
57
|
|
|
71
|
-
|
|
58
|
+
The block form returns `nil`. Its upstream session, result, and pooled connection remain alive for the duration of iteration.
|
|
72
59
|
|
|
73
|
-
|
|
74
|
-
- `Neo4jBolt::Relationship` attributes: `id`, `start_node_id`, `end_node_id`, `type`
|
|
60
|
+
Parameters stay separate from Cypher:
|
|
75
61
|
|
|
76
62
|
```ruby
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
puts node.id
|
|
82
|
-
puts node.labels
|
|
83
|
-
puts node.keys
|
|
84
|
-
node.each_pair { |k, v| puts "#{k}: #{v}" }
|
|
85
|
-
puts node.to_json
|
|
86
|
-
puts "a: #{node[:a]}"
|
|
63
|
+
row = neo4j_query_expect_one(
|
|
64
|
+
"MATCH (person:Person {email: $email}) RETURN person",
|
|
65
|
+
email: "ada@example.test"
|
|
66
|
+
)
|
|
87
67
|
```
|
|
88
68
|
|
|
89
|
-
|
|
69
|
+
`neo4j_query_expect_one` raises `Neo4jBolt::ExpectedOneResultError` unless the query produces exactly one row.
|
|
70
|
+
|
|
71
|
+
### Result compatibility
|
|
72
|
+
|
|
73
|
+
Result-row keys are strings:
|
|
90
74
|
|
|
91
|
-
|
|
75
|
+
```ruby
|
|
76
|
+
row["person"]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Nodes and relationships remain Hash subclasses whose property keys are symbols:
|
|
92
80
|
|
|
93
81
|
```ruby
|
|
94
|
-
node =
|
|
82
|
+
node = row["person"]
|
|
83
|
+
node[:name]
|
|
84
|
+
node.id
|
|
85
|
+
node.labels
|
|
86
|
+
node.element_id
|
|
95
87
|
```
|
|
96
88
|
|
|
97
|
-
|
|
89
|
+
`Neo4jBolt::Node` preserves `id` and `labels` and additively exposes `element_id`.
|
|
90
|
+
|
|
91
|
+
`Neo4jBolt::Relationship` preserves `id`, `start_node_id`, `end_node_id`, and `type`, and additively exposes `element_id`, `start_node_element_id`, and `end_node_element_id`.
|
|
92
|
+
|
|
93
|
+
Arrays and maps are adapted recursively. Map/property keys inside values are symbols, matching Neo4jBolt 0.3 behavior. Modern upstream values that 0.3 could not represent—such as temporal, spatial, path, byte, duration, and UUID values—pass through as upstream value objects. This is additive support.
|
|
98
94
|
|
|
99
|
-
|
|
95
|
+
Signed 64-bit integer limits are checked before transport. Out-of-range integers raise `Neo4jBolt::IntegerOutOfRangeError`.
|
|
100
96
|
|
|
101
|
-
|
|
97
|
+
## Transactions
|
|
98
|
+
|
|
99
|
+
The convenient compatibility API is unchanged:
|
|
102
100
|
|
|
103
101
|
```ruby
|
|
104
102
|
transaction do
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
neo4j_query("CREATE (:Person {name: 'Ada'})")
|
|
104
|
+
neo4j_query("CREATE (:Person {name: 'Grace'})")
|
|
107
105
|
end
|
|
108
106
|
```
|
|
109
107
|
|
|
110
|
-
|
|
108
|
+
Nested `transaction` blocks reuse one actual upstream session and transaction. Applications do not receive or pass an upstream transaction object.
|
|
111
109
|
|
|
112
|
-
|
|
110
|
+
Transaction context is isolated by calling thread and by the object using `Neo4jBolt`. Two threads using the same object receive independent upstream transactions and pooled connections.
|
|
113
111
|
|
|
114
|
-
|
|
112
|
+
Transactions are rollback-only after any nested operation raises, even if application code rescues the exception later. This includes database failures and `ExpectedOneResultError`:
|
|
115
113
|
|
|
116
114
|
```ruby
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
transaction do
|
|
116
|
+
neo4j_query("CREATE (:Marker)")
|
|
117
|
+
|
|
118
|
+
begin
|
|
119
|
+
neo4j_query("invalid cypher")
|
|
120
|
+
rescue Neo4jBolt::SyntaxError
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
# Nothing is committed.
|
|
120
124
|
```
|
|
121
125
|
|
|
122
|
-
|
|
123
|
-
- the `email` property of all nodes with label `User` must be unique
|
|
124
|
-
- the `sid` property of all nodes with label `Session` must be unique
|
|
125
|
-
- the `expires` property of all nodes with label `Session` gets indexed for faster lookup
|
|
126
|
+
Calling `rollback` inside a transaction marks that outer transaction rollback-only and returns `nil`. Calling it outside a transaction raises `Neo4jBolt::Error`; the old 0.3 implementation exposed the method but accidentally delegated to a nonexistent private implementation.
|
|
126
127
|
|
|
127
|
-
|
|
128
|
+
Upstream server/driver failures are translated to `Neo4jBolt::Error`. Syntax and uniqueness-constraint failures are translated narrowly to `Neo4jBolt::SyntaxError` and `Neo4jBolt::ConstraintValidationFailedError`. The upstream exception remains available as `error.cause`.
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
## Constraints and indexes
|
|
130
131
|
|
|
131
|
-
|
|
132
|
+
The existing setup format remains supported:
|
|
132
133
|
|
|
133
|
-
|
|
134
|
+
```ruby
|
|
135
|
+
setup_constraints_and_indexes(
|
|
136
|
+
["User/email", "Session/sid"],
|
|
137
|
+
["Session/expires"]
|
|
138
|
+
)
|
|
139
|
+
```
|
|
134
140
|
|
|
135
|
-
|
|
136
|
-
| ------- | ----------- |
|
|
137
|
-
| `neo4j_bolt console` | launch Pry console with Neo4jBolt |
|
|
138
|
-
| `neo4j_bolt clear` | remove all nodes and relationships, needs `--srsly` argument |
|
|
139
|
-
| `neo4j_bolt dump` | dump database contents |
|
|
140
|
-
| `neo4j_bolt load` | load database dump |
|
|
141
|
-
| `neo4j_bolt index ls` | list all database constraints and indexes |
|
|
142
|
-
| `neo4j_bolt index rm` | remove all constraints and indexes, needs `-f` |
|
|
143
|
-
| `neo4j_bolt visualize` | generates a visual representation of the current datbase contents |
|
|
141
|
+
Managed entries retain the `neo4j_bolt_` prefix. Setup removes obsolete entries with that prefix only; it does not remove arbitrary application-defined indexes or constraints.
|
|
144
142
|
|
|
145
|
-
|
|
143
|
+
## Dump and load
|
|
146
144
|
|
|
147
|
-
|
|
145
|
+
The textual format is unchanged:
|
|
148
146
|
|
|
149
|
-
|
|
147
|
+
```text
|
|
148
|
+
n {"id":0,"labels":["Person"],"properties":{"name":"Ada"}}
|
|
149
|
+
n {"id":1,"labels":["Person"],"properties":{"name":"Grace"}}
|
|
150
|
+
r {"from":0,"to":1,"type":"KNOWS","properties":{"since":2024}}
|
|
151
|
+
```
|
|
150
152
|
|
|
151
|
-
|
|
153
|
+
```ruby
|
|
154
|
+
File.open("database.dump", "w") { |io| dump_database(io) }
|
|
155
|
+
File.open("database.dump", "r") { |io| load_database_dump(io) }
|
|
156
|
+
```
|
|
152
157
|
|
|
153
|
-
|
|
158
|
+
The `0, 1, 2, ...` IDs are synthetic dump-local IDs. Database-internal IDs and element IDs are never written to the persistent format. The adapter uses driver-provided entity identity while dumping and a random temporary property (removed in `ensure`) while loading, so relationship reconstruction does not depend on deprecated persistent integer IDs. To keep dump numbering deterministic, ordering uses `elementId()` on modern Neo4j and an internal `id()` fallback only on Neo4j 4.4, where `elementId()` does not exist. Old Neo4jBolt dumps remain loadable.
|
|
154
159
|
|
|
155
|
-
|
|
160
|
+
Loading requires an empty database unless `force_append: true` is passed.
|
|
156
161
|
|
|
157
|
-
|
|
162
|
+
## CLI
|
|
158
163
|
|
|
159
|
-
|
|
164
|
+
The `neo4j_bolt` executable retains these commands:
|
|
160
165
|
|
|
161
|
-
|
|
166
|
+
| Command | Purpose |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| `neo4j_bolt console` | Open an IRB console with Neo4jBolt loaded |
|
|
169
|
+
| `neo4j_bolt clear --srsly` | Delete all nodes and relationships |
|
|
170
|
+
| `neo4j_bolt dump` | Write the textual database dump |
|
|
171
|
+
| `neo4j_bolt load [--force] PATH` | Load a textual dump |
|
|
172
|
+
| `neo4j_bolt index ls` | List constraints and indexes |
|
|
173
|
+
| `neo4j_bolt index rm --force` | Remove all constraints and indexes |
|
|
174
|
+
| `neo4j_bolt visualize` | Generate a GraphViz document |
|
|
162
175
|
|
|
163
|
-
|
|
176
|
+
Use `--host HOST:PORT` to select a server. `gli` remains a runtime dependency for the CLI, and `pry` remains for the separate `bin/console` executable.
|
|
164
177
|
|
|
165
|
-
|
|
178
|
+
## Tested Neo4j versions
|
|
166
179
|
|
|
167
|
-
|
|
168
|
-
./bin/neo4j_bolt visualize | dot -Tsvg > graph.svg
|
|
169
|
-
```
|
|
180
|
+
The same complete integration suite is run against these exact Community images:
|
|
170
181
|
|
|
171
|
-
|
|
182
|
+
- `neo4j:4.4.48-community`
|
|
183
|
+
- `neo4j:5.26.28-community`
|
|
184
|
+
- `neo4j:2026.06.0-community`
|
|
172
185
|
|
|
173
|
-
|
|
174
|
-
./bin/neo4j_bolt visualize | docker run --rm -i nshine/dot dot -Tsvg > graph.svg
|
|
175
|
-
```
|
|
186
|
+
No compatibility beyond this matrix is claimed for `0.4.0.pre1`.
|
|
176
187
|
|
|
177
|
-
|
|
188
|
+
Run one modern LTS target:
|
|
178
189
|
|
|
179
|
-
|
|
190
|
+
```bash
|
|
191
|
+
bundle exec rake spec
|
|
192
|
+
```
|
|
180
193
|
|
|
181
|
-
|
|
194
|
+
Run the complete sequential matrix:
|
|
182
195
|
|
|
183
|
-
|
|
196
|
+
```bash
|
|
197
|
+
bundle exec rake spec:matrix
|
|
198
|
+
```
|
|
184
199
|
|
|
185
|
-
|
|
200
|
+
The harness creates uniquely named disposable containers with `NEO4J_AUTH=none`, dynamically publishes Bolt ports, waits by establishing a real driver/query connection, and cleans every container through shell traps. A developer does not need to start Neo4j manually.
|
|
201
|
+
|
|
202
|
+
To point RSpec itself at an already disposable database, set `NEO4J_BOLT_TEST_HOST` and `NEO4J_BOLT_TEST_PORT`. The explicit port requirement protects real databases from the destructive integration suite.
|
|
203
|
+
|
|
204
|
+
## Compatibility inventory for 0.4
|
|
205
|
+
|
|
206
|
+
| API | Status |
|
|
207
|
+
| --- | --- |
|
|
208
|
+
| `bolt_host`, `bolt_port`, `bolt_verbosity` | Preserved |
|
|
209
|
+
| Included/extended/module-style use | Preserved |
|
|
210
|
+
| `neo4j_query`, including incremental block form | Preserved via adapter |
|
|
211
|
+
| `neo4j_query_expect_one` | Preserved via adapter |
|
|
212
|
+
| `transaction`, nesting, rollback-only behavior | Preserved via thread-local adapter |
|
|
213
|
+
| `rollback` | Preserved and repaired as rollback-only |
|
|
214
|
+
| `cleanup_neo4j`, `wait_for_neo4j` | Preserved via pooled driver lifecycle |
|
|
215
|
+
| `setup_constraints_and_indexes` | Preserved with current Cypher |
|
|
216
|
+
| `dump_database`, `load_database_dump` | Preserved; relationship reconstruction modernized |
|
|
217
|
+
| Named error classes | Preserved with narrow upstream translation |
|
|
218
|
+
| `Node` and `Relationship` Hash behavior | Preserved via recursive value adapter |
|
|
219
|
+
| Element identity and modern values | Additive behavior |
|
|
220
|
+
| CLI commands | Preserved |
|
|
221
|
+
| `BoltSocket`, `BoltBuffer`, protocol markers/state/parser/packer | Intentionally removed private implementation details |
|
|
222
|
+
|
|
223
|
+
## 0.4.0.pre1 migration notes
|
|
224
|
+
|
|
225
|
+
- Ruby 3.4 or newer is required; Ruby 2.x/3.0–3.3 applications should stay on 0.3.x until upgraded.
|
|
226
|
+
- The exact prerelease upstream dependency is pinned while no stable `neo4j-ruby-driver` 6.2.x exists.
|
|
227
|
+
- Connections are pooled and safe for concurrent use instead of one mutable socket per including object.
|
|
228
|
+
- TLS, routing, authentication, database selection, and other upstream-driver configuration are not newly exposed through the legacy three-setting API in this compatibility prerelease.
|
|
229
|
+
- `BoltSocket`, `BoltBuffer`, `ServerState`, `BoltMarker`, `UnexpectedServerResponse`, `State`, and `CypherError` were undocumented wire internals and are removed.
|
|
186
230
|
|
|
187
231
|
## Development
|
|
188
232
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
192
|
-
|
|
193
|
-
## Contributing
|
|
194
|
-
|
|
195
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/specht/neo4j_bolt.
|
|
233
|
+
Run `bin/setup`, then `bundle exec rake spec` or `bundle exec rake spec:matrix`. Build without publishing with `bundle exec rake build`.
|
|
196
234
|
|
|
235
|
+
Bug reports and pull requests are welcome at <https://github.com/specht/neo4j_bolt>.
|
data/bin/neo4j_bolt
CHANGED
|
@@ -92,10 +92,10 @@ class App
|
|
|
92
92
|
command :index do |c|
|
|
93
93
|
c.command :ls do |c2|
|
|
94
94
|
c2.action do |global_options, options, args|
|
|
95
|
-
neo4j_query("SHOW
|
|
95
|
+
neo4j_query("SHOW CONSTRAINTS") do |row|
|
|
96
96
|
puts "#{row['type']} #{row['name']} #{(row['labelsOrTypes'] || []).join('/')}/#{(row['properties'] || []).join('/')}"
|
|
97
97
|
end
|
|
98
|
-
neo4j_query("SHOW
|
|
98
|
+
neo4j_query("SHOW INDEXES") do |row|
|
|
99
99
|
puts "#{row['uniqueness']} #{row['entityType']} #{row['state']} #{row['populationPercent']}% #{row['name']} #{(row['labelsOrTypes'] || []).join('/')}/#{(row['properties'] || []).join('/')}"
|
|
100
100
|
end
|
|
101
101
|
end
|
|
@@ -106,10 +106,10 @@ class App
|
|
|
106
106
|
if options[:force]
|
|
107
107
|
all_constraints = []
|
|
108
108
|
all_indexes = []
|
|
109
|
-
neo4j_query("SHOW
|
|
109
|
+
neo4j_query("SHOW CONSTRAINTS") do |row|
|
|
110
110
|
all_constraints << row['name']
|
|
111
111
|
end
|
|
112
|
-
neo4j_query("SHOW
|
|
112
|
+
neo4j_query("SHOW INDEXES") do |row|
|
|
113
113
|
all_indexes << row['name']
|
|
114
114
|
end
|
|
115
115
|
transaction do
|
|
@@ -242,7 +242,7 @@ class App
|
|
|
242
242
|
'BTREE' => 'indexed',
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
['SHOW
|
|
245
|
+
['SHOW CONSTRAINTS', 'SHOW INDEXES'].each do |query|
|
|
246
246
|
neo4j_query(query) do |row|
|
|
247
247
|
# STDERR.puts row.to_json
|
|
248
248
|
labels_or_types = row['labelsOrTypes'] || []
|
data/lib/neo4j_bolt/version.rb
CHANGED