neo4j_bolt 0.4.4 → 0.4.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6322e3f763f052647f81029aaa96e45d6cb6f41a8e8d625c198d025b1408a81
4
- data.tar.gz: ddf55ab34e93f5b65b36273a2c148037f4455e18252aba692296e52598668902
3
+ metadata.gz: cc5ccd396277de9c75f73bd338f89a2af476475940d53e0399cfbbeaa9589dc6
4
+ data.tar.gz: 90a393bb3f4819245613ca34206d24932487de5af4959b27e26232137766e15f
5
5
  SHA512:
6
- metadata.gz: bff514f49ee709624ac1b178e2216edd001ad168845c397aff2f9d28a3d3752a4c3fcbf0d7f9ea4bc6c0a1f68ffd6feef8fae560127774c0aa0277a4d6cf6ffd
7
- data.tar.gz: d5ef2dbebc19f6f1538ee461993ec20a9184b734ba4b490ca6a941f4de7b2cb526f22ad59daa9f8578afee00f0be25728c16eed29f6ddfab1a581901b8eabb42
6
+ metadata.gz: e4f8c0aa87b72f4081d19476dbb684ced5fed6851b729f7db4bb7d8b06f7e41f1607581498b1195043edc63433fe91b8ca7aadf0c58c3d8c7f14bf79dcae1395
7
+ data.tar.gz: '028fc3fc677e0a8d83f6f5554d851ff29b1ab7bf6b0c059ea646f18062f646e4c43554f612bb6c69055658af56e4f08bda30c5756b57899475b98178b91d6e87'
data/README.md CHANGED
@@ -4,14 +4,14 @@ Neo4jBolt 0.4 is a small compatibility and convenience layer for Ruby applicatio
4
4
 
5
5
  Neo4jBolt no longer implements the Bolt wire protocol itself.
6
6
 
7
- `0.4.4` 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.
7
+ `0.4.5` 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.
8
8
 
9
9
  ## Installation
10
10
 
11
11
  For this prerelease, specify the version explicitly:
12
12
 
13
13
  ```ruby
14
- gem "neo4j_bolt", "0.4.4"
14
+ gem "neo4j_bolt", "0.4.5"
15
15
  ```
16
16
 
17
17
  Then run `bundle install`. A running Neo4j database is required.
@@ -26,17 +26,20 @@ Neo4jBolt.bolt_port = 7687
26
26
  Neo4jBolt.bolt_verbosity = 0
27
27
  ```
28
28
 
29
- Authentication and database selection can use the standard Neo4j client environment variables:
29
+ The connection endpoint, authentication, and database selection can use the standard Neo4j client environment variables:
30
30
 
31
31
  ```bash
32
+ export NEO4J_URI=neo4j://localhost:7687
32
33
  export NEO4J_USERNAME=neo4j
33
34
  export NEO4J_PASSWORD=secret
34
35
  export NEO4J_DATABASE=neo4j
35
36
  ```
36
37
 
37
- They are read when `neo4j_bolt` is loaded. Explicit Ruby configuration overrides those defaults:
38
+ They are read when `neo4j_bolt` is loaded. `NEO4J_URI` accepts `neo4j://HOST[:PORT]` and `bolt://HOST[:PORT]` endpoint notation; Neo4jBolt uses the host and port while retaining its existing direct Bolt connection behavior. The default port is 7687. Explicit Ruby configuration overrides those defaults:
38
39
 
39
40
  ```ruby
41
+ Neo4jBolt.bolt_host = "localhost"
42
+ Neo4jBolt.bolt_port = 7687
40
43
  Neo4jBolt.bolt_username = "neo4j"
41
44
  Neo4jBolt.bolt_password = "secret"
42
45
  Neo4jBolt.bolt_database = "movies"
@@ -186,7 +189,7 @@ neo4j_bolt load --color bright-blue database.dump
186
189
 
187
190
  The `0, 1, 2, ...` IDs are synthetic dump-local IDs. Database-internal IDs and element IDs are never written to the persistent format. Dumping keeps the count, node, and relationship reads in one transaction, so the driver-provided entity identity used to connect the two streamed result sets stays within Neo4j's transaction-scoped identity guarantee. 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.
188
191
 
189
- For relational loads, the adapter assigns a random temporary label and dump-ID property to imported nodes. After the nodes are committed it builds a temporary `neo4j_bolt_` index on that property, uses indexed lookups while creating relationships, drops the index, and removes the temporary metadata in batches. No database-internal identity is carried from one load transaction to another. Loading a relational dump therefore requires permission to create and drop an index.
192
+ For relational loads, the adapter assigns a random temporary label and the fixed reserved property `__neo4j_bolt_load_id` to imported nodes. After the nodes are committed it builds a temporary `neo4j_bolt_` index on that property, uses indexed lookups while creating relationships, drops the index, and removes the temporary metadata in batches. The fixed property key is reused by every load instead of registering a new random property-key token each time. Neo4j can keep the property key registered after all values have been removed, so it may remain visible in Browser's property-key list; no imported node retains the property after successful cleanup. Relational loads refuse to run if the dump itself, or existing data during `force_append`, currently uses the reserved property. No database-internal identity is carried from one load transaction to another. Loading a relational dump therefore requires permission to create and drop an index.
190
193
 
191
194
  Loads start with batches of 5,000 records by default. This is an initial ceiling rather than a claimed optimum: if Neo4j returns a transaction-memory/resource error whose server semantics guarantee rollback, the loader halves the failed batch and retries it, then keeps the smaller size for the rest of that phase. Node and relationship phases adapt independently. Other errors are not retried, because a generic connection failure cannot safely prove that a `CREATE` transaction did not commit. Callers can change the initial ceiling with `initial_batch_size:`; the CLI exposes the same setting as `--batch-size`.
192
195
 
@@ -206,7 +209,7 @@ The `neo4j_bolt` executable retains these commands:
206
209
  | `neo4j_bolt index rm --force` | Remove all constraints and indexes |
207
210
  | `neo4j_bolt visualize` | Generate a GraphViz document |
208
211
 
209
- Use `--host HOST:PORT`, `--username USER`, `--password PASSWORD`, and `--database DATABASE` to override connection settings. Username, password, and database otherwise inherit `NEO4J_USERNAME`, `NEO4J_PASSWORD`, and `NEO4J_DATABASE`. Prefer the password environment variable for routine use because command-line arguments may be visible in shell history or process listings. `gli` remains a runtime dependency for the CLI, and `pry` remains for the separate `bin/console` executable.
212
+ Use `--host HOST:PORT`, `--username USER`, `--password PASSWORD`, and `--database DATABASE` to override connection settings. Host and port otherwise inherit `NEO4J_URI` when present, falling back to `localhost:7687`; username, password, and database inherit `NEO4J_USERNAME`, `NEO4J_PASSWORD`, and `NEO4J_DATABASE`. Prefer the password environment variable for routine use because command-line arguments may be visible in shell history or process listings. `gli` remains a runtime dependency for the CLI, and `pry` remains for the separate `bin/console` executable.
210
213
 
211
214
  ## Tested Neo4j versions
212
215
 
@@ -216,7 +219,7 @@ The same complete integration suite is run against these exact Community images:
216
219
  - `neo4j:5.26.28-community`
217
220
  - `neo4j:2026.06.0-community`
218
221
 
219
- No compatibility beyond this matrix is claimed for `0.4.4`.
222
+ No compatibility beyond this matrix is claimed for `0.4.5`.
220
223
 
221
224
  Run one modern LTS target:
222
225
 
@@ -254,13 +257,15 @@ To point RSpec itself at an already disposable database, set `NEO4J_BOLT_TEST_HO
254
257
  | CLI commands | Preserved |
255
258
  | `BoltSocket`, `BoltBuffer`, protocol markers/state/parser/packer | Intentionally removed private implementation details |
256
259
 
257
- ## 0.4.4 migration notes
260
+ ## 0.4.5 migration notes
258
261
 
259
262
  - Ruby 3.4 or newer is required; Ruby 2.x/3.0–3.3 applications should stay on 0.3.x until upgraded.
260
263
  - The exact prerelease upstream dependency is pinned while no stable `neo4j-ruby-driver` 6.2.x exists.
261
264
  - Connections are pooled and safe for concurrent use instead of one mutable socket per including object.
262
- - Basic authentication and database selection are now exposed through `bolt_username`, `bolt_password`, and `bolt_database`, with `NEO4J_USERNAME`, `NEO4J_PASSWORD`, and `NEO4J_DATABASE` as environment defaults.
263
- - TLS, routing, URI schemes, and other upstream-driver configuration are still not exposed through the compatibility API.
265
+ - `NEO4J_URI` now supplies the default host and port for `bolt://` and `neo4j://` endpoint notation; an explicit CLI `--host` or Ruby `bolt_host`/`bolt_port` setting overrides it.
266
+ - Basic authentication and database selection are exposed through `bolt_username`, `bolt_password`, and `bolt_database`, with `NEO4J_USERNAME`, `NEO4J_PASSWORD`, and `NEO4J_DATABASE` as environment defaults.
267
+ - TLS, routing behavior, URI query parameters, and other upstream-driver configuration are still not exposed through the compatibility API.
268
+ - Relational loads reuse one reserved `__neo4j_bolt_load_id` property key while retaining the existing indexed lookup algorithm, preventing a new property-key token from being registered on every load.
264
269
  - `BoltSocket`, `BoltBuffer`, `ServerState`, `BoltMarker`, `UnexpectedServerResponse`, `State`, and `CypherError` were undocumented wire internals and are removed.
265
270
 
266
271
  ## Development
data/bin/neo4j_bolt CHANGED
@@ -21,15 +21,18 @@ class App
21
21
  version Neo4jBolt::VERSION
22
22
 
23
23
  flag [:v, :verbosity], :default_value => 0
24
- flag [:h, :host], :default_value => 'localhost:7687'
24
+ flag [:h, :host], :default_value => nil,
25
+ :desc => 'Neo4j host:port (default: NEO4J_URI or localhost:7687)'
25
26
  flag [:u, :username], :default_value => nil, :desc => 'Neo4j username (default: NEO4J_USERNAME)'
26
27
  flag [:p, :password], :default_value => nil, :desc => 'Neo4j password (default: NEO4J_PASSWORD)'
27
28
  flag [:d, :database], :default_value => nil, :desc => 'Neo4j database (default: NEO4J_DATABASE)'
28
29
 
29
30
  pre do |global_options, command, options, args|
30
31
  host = global_options[:host]
31
- Neo4jBolt.bolt_host = host.split(':').first
32
- Neo4jBolt.bolt_port = host.split(':').last.to_i
32
+ unless host.nil?
33
+ Neo4jBolt.bolt_host = host.split(':').first
34
+ Neo4jBolt.bolt_port = host.split(':').last.to_i
35
+ end
33
36
  Neo4jBolt.bolt_verbosity = global_options[:verbosity].to_i
34
37
  Neo4jBolt.bolt_username = global_options[:username] unless global_options[:username].nil?
35
38
  Neo4jBolt.bolt_password = global_options[:password] unless global_options[:password].nil?
@@ -1,3 +1,3 @@
1
1
  module Neo4jBolt
2
- VERSION = "0.4.4"
2
+ VERSION = "0.4.5"
3
3
  end
data/lib/neo4j_bolt.rb CHANGED
@@ -4,6 +4,7 @@ require "json"
4
4
  require "securerandom"
5
5
  require "set"
6
6
  require "thread"
7
+ require "uri"
7
8
  require "neo4j/driver"
8
9
  require "neo4j_bolt/version"
9
10
 
@@ -18,8 +19,9 @@ module Neo4jBolt
18
19
  LOAD_MEMORY_GQL_STATUSES = %w[51N72 51N73].freeze
19
20
  LOAD_PROGRESS_STEP = 5_000
20
21
  DUMP_PROGRESS_STEP = 1_000
22
+ LOAD_ID_PROPERTY = "__neo4j_bolt_load_id"
21
23
  private_constant :LOAD_MEMORY_ERROR_CODES, :LOAD_MEMORY_GQL_STATUSES,
22
- :LOAD_PROGRESS_STEP, :DUMP_PROGRESS_STEP
24
+ :LOAD_PROGRESS_STEP, :DUMP_PROGRESS_STEP, :LOAD_ID_PROPERTY
23
25
 
24
26
  class ProgressReporter
25
27
  UPDATE_INTERVAL = 0.5
@@ -396,8 +398,29 @@ module Neo4jBolt
396
398
  end
397
399
  end
398
400
 
399
- self.bolt_host = "localhost"
400
- self.bolt_port = 7687
401
+ neo4j_uri = ENV["NEO4J_URI"]
402
+ if neo4j_uri.nil? || neo4j_uri.empty?
403
+ self.bolt_host = "localhost"
404
+ self.bolt_port = 7687
405
+ else
406
+ begin
407
+ parsed_neo4j_uri = URI.parse(neo4j_uri)
408
+ rescue URI::InvalidURIError => error
409
+ raise Error, "Invalid NEO4J_URI: #{error.message}"
410
+ end
411
+
412
+ unless %w[bolt neo4j].include?(parsed_neo4j_uri.scheme) && parsed_neo4j_uri.host
413
+ raise Error, "NEO4J_URI must be a bolt:// or neo4j:// URI with a host"
414
+ end
415
+ unless parsed_neo4j_uri.userinfo.nil? &&
416
+ ["", "/"].include?(parsed_neo4j_uri.path.to_s) &&
417
+ parsed_neo4j_uri.query.nil? && parsed_neo4j_uri.fragment.nil?
418
+ raise Error, "NEO4J_URI may only contain scheme, host, and port"
419
+ end
420
+
421
+ self.bolt_host = parsed_neo4j_uri.host
422
+ self.bolt_port = parsed_neo4j_uri.port || 7687
423
+ end
401
424
  self.bolt_verbosity = 0
402
425
  self.bolt_username = ENV["NEO4J_USERNAME"]
403
426
  self.bolt_password = ENV["NEO4J_PASSWORD"]
@@ -650,12 +673,32 @@ module Neo4jBolt
650
673
 
651
674
  temporary_token = SecureRandom.hex(12)
652
675
  temporary_label = "__neo4j_bolt_load_#{temporary_token}"
653
- temporary_id_property = "__neo4j_bolt_load_id_#{temporary_token}"
676
+ temporary_id_property = LOAD_ID_PROPERTY
654
677
  temporary_index = "neo4j_bolt_load_#{temporary_token}"
655
678
  quoted_temporary_label = quote_identifier(temporary_label)
656
679
  quoted_temporary_id = quote_identifier(temporary_id_property)
657
680
  quoted_temporary_index = quote_identifier(temporary_index)
658
681
  needs_relationship_lookup = total_relationships.positive?
682
+
683
+ if needs_relationship_lookup
684
+ dump_uses_reserved_property = node_batches.each_value.any? do |nodes|
685
+ nodes.any? { |node| node.fetch("properties", {}).key?(LOAD_ID_PROPERTY) }
686
+ end
687
+ if dump_uses_reserved_property
688
+ raise Error, "Dump contains reserved Neo4jBolt load property #{LOAD_ID_PROPERTY.inspect}."
689
+ end
690
+
691
+ if force_append
692
+ reserved_property_in_use = neo4j_query(
693
+ "MATCH (n) WHERE n.#{quoted_temporary_id} IS NOT NULL RETURN 1 AS present LIMIT 1"
694
+ ).any?
695
+ if reserved_property_in_use
696
+ raise Error,
697
+ "Database contains reserved Neo4jBolt load property #{LOAD_ID_PROPERTY.inspect}."
698
+ end
699
+ end
700
+ end
701
+
659
702
  loaded_nodes = 0
660
703
  loaded_relationships = 0
661
704
  index_created = false
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j_bolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Specht