This repository contains 2 implementation of a Neo4j driver for Ruby:
Network communication is handled using Bolt Protocol.
Table of ContentsAdd this line to your application's Gemfile:
And then execute:
Or install it yourself as:
gem install neo4j-ruby-driver
You need a running Neo4j database in order to use the driver with it. The easiest way to spin up a local instance is through a Docker container.
The command below runs the latest Neo4j version in Docker, setting the admin username and password to neo4j
and password
respectively:
docker run \ -p7474:7474 \ -p7687:7687 \ -d \ -e NEO4J_AUTH=neo4j/password \ neo4j:latest
require 'neo4j/driver' Neo4j::Driver::GraphDatabase.driver( 'bolt://localhost:7687', Neo4j::Driver::AuthTokens.basic('neo4j', 'password') ) do |driver| driver.session(database: 'neo4j') do |session| query_result = session.run('RETURN 2+2 AS value') puts "2+2 equals #{query_result.single['value']}" # consume gives the execution summary create_result = session.run('CREATE (n)').consume puts "Nodes created: #{create_result.counters.nodes_created}" end end
The compatibility with Neo4j Server versions is documented in the Neo4j Knowledge Base.
The API is to highest possible degree consistent with the official Java driver. Please refer to the Neo4j Java Driver Manual, examples in Ruby, and code snippets below to understand how to use it. Neo4j Java Driver API Docs can be helpful as well.
The driver supports the following URI schemes:
URI Scheme Descriptionneo4j://
Connect using routing to a cluster/causal cluster. neo4j+s://
Same as neo4j://
but with full TLS encryption. neo4j+ssc://
Same as neo4j://
but with full TLS encryption, without hostname verification. bolt://
Connect directly to a server using the Bolt protocol. bolt+s://
Same as bolt://
but with full TLS encryption. bolt+ssc://
Same as bolt://
but with full TLS encryption, without hostname verification.
Example:
# Connect to a single instance driver = Neo4j::Driver::GraphDatabase.driver( 'bolt://localhost:7687', Neo4j::Driver::AuthTokens.basic('neo4j', 'password') ) # Connect to a cluster driver = Neo4j::Driver::GraphDatabase.driver( 'neo4j://graph.example.com:7687', Neo4j::Driver::AuthTokens.basic('neo4j', 'password') )
The driver provides multiple authentication methods:
# Basic authentication auth = Neo4j::Driver::AuthTokens.basic('neo4j', 'password') # With realm specification auth = Neo4j::Driver::AuthTokens.basic('neo4j', 'password', 'realm') # Kerberos authentication auth = Neo4j::Driver::AuthTokens.kerberos('ticket') # Bearer authentication auth = Neo4j::Driver::AuthTokens.bearer('token') # Custom authentication auth = Neo4j::Driver::AuthTokens.custom('principal', 'credentials', 'realm', 'scheme') # No authentication auth = Neo4j::Driver::AuthTokens.none
You can configure the driver with additional options:
config = { connection_timeout: 15.seconds, connection_acquisition_timeout: 1.minute, max_transaction_retry_time: 30.seconds, encryption: true, trust_strategy: :trust_all_certificates } driver = Neo4j::Driver::GraphDatabase.driver( 'neo4j://localhost:7687', Neo4j::Driver::AuthTokens.basic('neo4j', 'password'), **config )
if driver.verify_connectivity puts "Driver is connected to the database" else puts "Driver cannot connect to the database" end
The driver provides sessions to interact with the database and to execute queries.
Sessions are lightweight and disposable database connections. Always close your sessions when done:
session = driver.session(database: 'neo4j') begin session.run('MATCH (n) RETURN n LIMIT 10') ensure session.close end
Or use a block that automatically closes the session:
driver.session(database: 'neo4j') do |session| session.run('MATCH (n) RETURN n LIMIT 10') end
Session options:
# Default database session = driver.session # Specific database session = driver.session(database: 'neo4j') # With access mode session = driver.session(database: 'neo4j', default_access_mode: Neo4j::Driver::AccessMode::READ) # With bookmarks for causal consistency session = driver.session( database: 'neo4j', bookmarks: [Neo4j::Driver::Bookmark.from('bookmark-1')] )
For simple, one-off queries, use auto-commit transactions:
session.run('CREATE (n:Person {name: $name})', name: 'Alice')
For multiple queries that need to be executed as a unit, use explicit transactions:
tx = session.begin_transaction begin tx.run('CREATE (n:Person {name: $name})', name: 'Alice') tx.run('CREATE (n:Person {name: $name})', name: 'Bob') tx.commit rescue tx.rollback raise end
Specifically for read operations:
result = session.read_transaction do |tx| tx.run('MATCH (n:Person) RETURN n.name').map { |record| record['n.name'] } end puts result
Specifically for write operations:
session.write_transaction do |tx| tx.run('CREATE (n:Person {name: $name})', name: 'Charlie') end
result = session.run('MATCH (n:Person) RETURN n.name AS name, n.age AS age') # Process results result.each do |record| puts "#{record['name']} is #{record['age']} years old" end # Check if there are more results puts "Has more results: #{result.has_next?}" # Get a single record single = result.single puts single['name'] if single # Get keys available in the result puts "Keys: #{result.keys}" # Access by field index result.each do |record| puts "First field: #{record[0]}" end # Convert to array records = result.to_aAccessing Node and Relationship data
Working with graph entities:
result = session.run('MATCH (p:Person)-[r:KNOWS]->(friend) RETURN p, r, friend') result.each do |record| # Working with nodes person = record['p'] puts "Node ID: #{person.id}" puts "Labels: #{person.labels.join(', ')}" puts "Properties: #{person.properties}" puts "Name property: #{person.properties['name']}" # Working with relationships relationship = record['r'] puts "Relationship ID: #{relationship.id}" puts "Type: #{relationship.type}" puts "Properties: #{relationship.properties}" # Start and end nodes of the relationship puts "Relationship: #{relationship.start_node_id} -> #{relationship.end_node_id}" end
Processing paths returned from Cypher:
result = session.run('MATCH p = (:Person)-[:KNOWS*]->(:Person) RETURN p') result.each do |record| path = record['p'] # Get all nodes in the path nodes = path.nodes puts "Nodes in path: #{nodes.map { |n| n.properties['name'] }.join(' -> ')}" # Get all relationships in the path relationships = path.relationships puts "Relationship types: #{relationships.map(&:type).join(', ')}" # Iterate through the path segments path.each do |segment| puts "#{segment.start_node.properties['name']} -[#{segment.relationship.type}]-> #{segment.end_node.properties['name']}" end endWorking with temporal types
Creating a node with properties of temporal types:
session.run( 'CREATE (e:Event {datetime: $datetime, duration: $duration})', datetime: DateTime.new(2025, 5, 5, 5, 55, 55), duration: 1.hour )
Querying temporal values:
session.run('MATCH (e:Event) LIMIT 1 RETURN e.datetime, e.duration').single.to_h # => {"e.datetime": 2025-05-05 05:55:55 +0000, "e.duration": 3600 seconds}
The Neo4j Ruby Driver maps Cypher types to Ruby types:
Cypher Type Ruby Type null nil List Enumerable Map Hash (symbolized keys) Boolean TrueClass/FalseClass Integer Integer/String1 Float Float String String/Symbol2 (encoding: UTF-8) ByteArray String (encoding: BINARY) Date Date Zoned Time Neo4j::Driver::Types::OffsetTime Local Time Neo4j::Driver::Types::LocalTime Zoned DateTime Time/ActiveSupport::TimeWithZone/DateTime3 Local DateTime Neo4j::Driver::Types::LocalDateTime Duration ActiveSupport::Duration Point Neo4j::Driver::Types::Point Node Neo4j::Driver::Types::Node Relationship Neo4j::Driver::Types::Relationship Path Neo4j::Driver::Types::PathThe driver handles connection pooling automatically. Configure the connection pool:
config = { max_connection_pool_size: 100, max_connection_lifetime: 1.hour } driver = Neo4j::Driver::GraphDatabase.driver('neo4j://localhost:7687', auth, **config)
Configure logging for the driver:
config = { logger: Logger.new(STDOUT).tap { |log| log.level = Logger::DEBUG } } driver = Neo4j::Driver::GraphDatabase.driver('neo4j://localhost:7687', auth, **config)
This gem includes 2 different implementations: a Java driver wrapper and a pure Ruby driver, so you will have to run this command every time you switch the Ruby engine:
There are two sets of tests for the driver. To run the specs placed in this repository, use a normal rspec command:
To run the Testkit that is used to test all Neo4j driver implementations, use the following:
git clone git@github.com:neo4j-drivers/testkit.git cd testkit export TEST_DRIVER_NAME=ruby export TEST_DRIVER_REPO=`realpath ../neo4j-ruby-driver` export TEST_NEO4J_PASS=password python3 main.py --tests UNIT_TESTS --configs 4.3-enterprise
Please refer to the Testkit documentation to learn more about its features.
Suggestions, improvements, bug reports and pull requests are welcome on GitHub at https://github.com/neo4jrb/neo4j-ruby-driver.
The gem is available as open source under the terms of the MIT License.
An Integer smaller than -2 ** 63 or larger than 2 ** 63 will always be implicitly converted to String ↩
A Symbol passed as a parameter will always be implicitly converted to String. All Strings other than BINARY encoded are converted to UTF-8 when stored in Neo4j ↩
A Ruby DateTime passed as a parameter will always be implicitly converted to Time ↩
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4