Neon
About
Neon is fast, minimal ruby binding for the neo4j. It provides a simple api to manipulate a Neo4J database instance hosted on a server or running as an embedded instance.
Usage
Creating Sessions
You start a session with a Neo4J database by creating a session. You can create a session with a REST server or an Embedded instance as follows :-
REST session
session = Neon::Session::Rest.new(url, options)
options = {
directory: '',
cypher: '/cypher',
gremlin: '/ext/GremlinPlugin/graphdb/execute_script',
log: false,
log_path: 'neon.log',
threads: 20,
authentication: nil,
username: nil,
password: nil,
parser: MultiJsonParser
}
You can also quickly initialize using
session = Neon::Session::Rest.new("http://username:password@localhost:7474/mydirectory")
Embedded session
session = Neon::Session::Embedded.new(path_to_db, auto_tx, impermanent)
path_to_db
is the path to the embedded database instance. You can use a symbol :impermanent
to create an impermanent instance.auto_tx
is an optional boolean parameter that is true by default.
The first session created is the default session used by Neon modules. It can be accessed using the current attribute.
Neon::Session.current
Neon::Session.set_current another_session
Neon::Session.current = another_session
another_session.current?
Using Sessions
Irrespective of the underlying session, you can use a common api there onwards i.e. you only make a choice on the session type during initialization and then forget about it. All further entities are linked to this session and provide a common interface for all core graph operations.
started? = session.start
closed? = session.close
running? = session.running?
started? = Neon::Session.start
close? = Neon::Session.close
running? = Neon::Session.running?
session.location
Auto Transaction support
- For REST sessions this has no consequence as auto_tx cannot be disabled. You can though run multiple statements using transactions.
- For Embedded sessions auto_tx means all graph operations will be automatically
wrapped inside a transaction which will be committed.
You can disable auto_tx in which case all operations will need to be wrapped inside a transaction by the developer.
session.auto_tx
session.auto_tx = false
Neon::Session.auto_tx = false
Property Container
Both nodes and relationships are property containers and respond in exactly the same way to the following interface :-
session = container.session
value = container[:key]
value1, value2 = container[:key1, :key2]
values = container[:key1, :key2]
value = container[:invalid_key]
value, nil_value = container[:key, :invalid_key]
value, hello_world = container[:key, :invalid_key] default: 'hello world'
container[:key1, :key2] = value1, value2
container[:key1, :key2] = values
container[:invalid_key] = value
container[nil] = value
container[valid_key] = invalid_value
keys = container.keys
container.key?('a key')
Creating Nodes
Nodes are created in context to a session which determines their underlying model and data access methods. The only time you need to be think about using sessions during creation. Thereafter you can use the same api irrespective of the database.
Create a new node
node = Neon::Node.new(attributes, label1, label2, session)
attributes
is an optional hash consisting of the key-value pairs you would like this node to be initialized with. Defaults to {}labels
- You can provide a comma separated list of labels or an array of labelssession
- the last argument is an optional session. It defines the database where you want to create the node and defaults to the current session
All of these arguments can be used in any combination as long as the order remains the same. Skipping all of them creates an empty node in the current session. Here are all various ways to create a node :-
node = Neon::Node.new
node = Neon::Node.new(name: :name, email: :email)
node = Neon::Node.new({name: :name}, another_session)
node = Neon::Node.new({name: :name, sex: 'male'}, :User, :Man)
node = Neon::Node.new({name: :name, sex: 'male'}, [:User, :Man])
node = Neon::Node.new({name: :name, sex: 'male'}, :User, :Man, another_session)
node = Neon::Node.new({name: :name, sex: 'male'}, [:User, :Man], another_session)
node = Neon::Node.new another_session
node = Neon::Node.new(:User)
node = Neon::Node.new([:User])
node = Neon::Node.new(:User, another_session)
node = Neon::Node.new([:User], another_session)
Loading Nodes
Existing nodes can be loaded by providing an id. Non existing ids return nil.
node = Neon::Node.load(5)
same_node = Neon::Node.load(node.id)
invalid_node = Neon::Node.load(:non_existent_id)
Using Nodes
Node instances have a well defined api to work with which is uniform irrespective of the underlying session.
id = node.id
node.labels
node.add_labels(:runner, :biker)
node.add_labels([:runner, biker])
node.remove_labels(:runner, :biker)
node.remove_labels([:runner, :biker])
node.label?(:Runner)
node.create_rel_to(another_node, :RELATIONSHIP_TYPE)
node.rels
node.rels(dir: :incoming)
node.rels(dir: :outgoing)
node.rels(labels: [:friend, :jusband])
node.rels(type: relationship_type)
node.rels(type: [:relationship_type, :another_relationship_type])
node.rels(between: another_node)
node.rel(:incoming, type: [:RELATIONSHIP_TYPE, :ANOTHER_RELATIONSHIP_TYPE])
node.rel?
node.rel?(dir: :incoming) or node.rel?(:outgoing)
node.rel?(type: :RELATIONSHIP_TYPE)
node.rel?(dir: :incoming, type: :RELATIONSHIP_TYPE)
node.delete
node.delete!
Relationships
Creating Relationships
Relationships are created in context to a node as specified in the node api.
You can load relationships the same way as nodes.
Using relationships
Relationships are property containers and therefore responds to all methods that node does under the property container section
start_node, end_node = rel.nodes
rel.start
rel.end
rel.other(node)
rel.type
rel.type?(a_type)
Transactions
Besides support for auto transactions, one can run transaction explicitly. Beginning a transaction turns off auto_tx until the end of the transaction. At the end auto_tx is restored to it's original status. You can run at most one transaction per thread.
tx = session.begin_tx
begin
tx.failure
rescue Exception => e
else
tx.success
ensure
tx.close
end
Transaction.run(optional_session) do |t|
end
result = Transaction.run { do_something_graphy }
Indexes
Indexes can be set upon nodes as well as relationships using legacy indexing.
Node Indexes
Legacy indexing on nodes can be performed as following :-
Fetch all node indexes
indexes = Neon::Node.indexes
Check if an index exists
index_exists? = Neon::Node.index?("User")
Create an index
index = Neon::Node.create_index("User", :exact, :lucene)
Get a node from an index
hits = user_index.get(key, value)
node_or_nil = hits.single
hits = user_index.query(key, query)
hits = user_index.query(query)
Add a node to an index
user_index.add(node, :email, node[:email])
Remove a node from an index
user_index.remove(node, :email, node[:email])
user_index.remove(node, :email)
user_index.remove(node)
Delete an index
user_index.delete
sore = hits.score
Auto Indexing
auto_index = Neon::Node.index
auto_index.status = true
auto_index.status = false
auto_index.add_property(property)
auto_index.remove_property(property)
hits = auto_index.get(key, value)
hits = auto_index.query(query)
Relationship Indexes
Exactly same api as nodes
Schema Indexes
This is the preferred way of indexing in Neo4J 2.0. Schema indexes are associated with labels on nodes only and not relationships.
Create an index on a label and properties
Neon::Node.create_index_on(:Person, property1, property2)
Find nodes from a schema index
hits = Neon::Node.index_for(:Person, options)
Delete an index on a label with properties
Neon::Node.delete_index_on(:Person, property1, propert2)
Add a unique contraint to a label
Neon::Node.create_contraint_on(:Person, property1, propert2)
Drop a unique contraint on a label
Neon::Node.drop_contraint_on(:Person, property1, propert2)
There is a shorthand to creating a unique node
Create a unique node
unique_ndoe = Neon::Node.uniq(arguments)
Traversal
TODO