neon 0.1.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 +7 -0
- data/Gemfile +6 -0
- data/README.md +432 -0
- data/lib/helpers/argument_helpers.rb +19 -0
- data/lib/helpers/transaction_helpers.rb +56 -0
- data/lib/neon.rb +23 -0
- data/lib/neon/Gemfile +8 -0
- data/lib/neon/node.rb +48 -0
- data/lib/neon/node/embedded.rb +31 -0
- data/lib/neon/node/rest.rb +109 -0
- data/lib/neon/property_container.rb +277 -0
- data/lib/neon/relationship.rb +38 -0
- data/lib/neon/relationship/embedded.rb +59 -0
- data/lib/neon/relationship/rest.rb +73 -0
- data/lib/neon/session.rb +68 -0
- data/lib/neon/session/embedded.rb +92 -0
- data/lib/neon/session/invalid_session.rb +6 -0
- data/lib/neon/session/rest.rb +53 -0
- data/lib/neon/transaction.rb +28 -0
- data/lib/neon/transaction/placebo.rb +38 -0
- data/lib/neon/transaction/rest.rb +47 -0
- data/lib/neon/version.rb +7 -0
- data/lib/tasks.rb +139 -0
- data/neon.gemspec +33 -0
- metadata +180 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Neon
|
|
2
|
+
module Relationship
|
|
3
|
+
class << self
|
|
4
|
+
# Creates a new Relationship and immediately persists it to the database. All subsequent changes are immediately persisted.
|
|
5
|
+
#
|
|
6
|
+
# @param start_node [Node] The node from which the relationship starts i.e. is outgoing.
|
|
7
|
+
# @param end_node [Node] The node at which the relationship ends i.e. is incoming.
|
|
8
|
+
# @param attributes [Hash] An optional hash of properties to initialize the relationship with.
|
|
9
|
+
#
|
|
10
|
+
# @return [Relationship] A new relationship.
|
|
11
|
+
#
|
|
12
|
+
def new(start_node, name, end_node, attributes = {})
|
|
13
|
+
start_node.create_rel_to(end_node, name, attributes)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Loads an existing relationship with the given id
|
|
17
|
+
#
|
|
18
|
+
# @param id [Integer] The id of the relationship to be loaded and returned.
|
|
19
|
+
# @param session [Session] An optional session from where to load the node.
|
|
20
|
+
#
|
|
21
|
+
# @return [Relationship] An existing relationship with the given id and specified session.
|
|
22
|
+
# It returns nil if the node is not found.
|
|
23
|
+
#
|
|
24
|
+
def load(id, session = Session.current)
|
|
25
|
+
begin
|
|
26
|
+
session.load_rel(id)
|
|
27
|
+
rescue NoMethodError
|
|
28
|
+
_raise_invalid_session_error(session)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
def _raise_invalid_session_error(session)
|
|
34
|
+
raise Session::InvalidSessionTypeError.new(session.class)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Extend the Java RelationshipProxy
|
|
2
|
+
Java::OrgNeo4jKernelImplCore::RelationshipProxy.class_eval do
|
|
3
|
+
include Neon::PropertyContainer::Embedded
|
|
4
|
+
include Neon::TransactionHelpers
|
|
5
|
+
|
|
6
|
+
def type
|
|
7
|
+
run_in_transaction { _type }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def start
|
|
11
|
+
run_in_transaction { _start }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def end
|
|
15
|
+
run_in_transaction { _end }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
"Embedded Relationship[#{getId}]"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def other_node(node)
|
|
23
|
+
run_in_transaction { _other_node node }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def nodes
|
|
27
|
+
run_in_transaction { get_nodes }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
def _destroy
|
|
32
|
+
nodes = get_nodes
|
|
33
|
+
delete
|
|
34
|
+
nodes.each { |node| node.delete }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def _type
|
|
38
|
+
get_type.name
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def _start
|
|
42
|
+
get_start_node
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def _end
|
|
46
|
+
get_end_node
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def _other_node(node)
|
|
50
|
+
case node
|
|
51
|
+
when start
|
|
52
|
+
self.end
|
|
53
|
+
when self.end
|
|
54
|
+
start
|
|
55
|
+
else
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require "neon/property_container"
|
|
2
|
+
|
|
3
|
+
module Neon
|
|
4
|
+
module Relationship
|
|
5
|
+
class Rest
|
|
6
|
+
include PropertyContainer::Rest
|
|
7
|
+
attr_reader :session, :id, :start, :end, :nodes, :type
|
|
8
|
+
|
|
9
|
+
def initialize(relationship, session)
|
|
10
|
+
@relationship = relationship
|
|
11
|
+
@session = session
|
|
12
|
+
@id = @relationship["self"].split('/').last.to_i # Set the id
|
|
13
|
+
@start = @session.load(@relationship["start"])
|
|
14
|
+
@end = @session.load(@relationship["end"])
|
|
15
|
+
@nodes = [@start, @end]
|
|
16
|
+
@type = @relationship["type"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def other_node(node)
|
|
20
|
+
case node
|
|
21
|
+
when @start
|
|
22
|
+
@end
|
|
23
|
+
when @end
|
|
24
|
+
@start
|
|
25
|
+
else
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
rescue NoMethodError => e
|
|
29
|
+
_raise_doesnt_exist_anymore_error(e)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def to_s
|
|
34
|
+
"REST Relationship[#{@id}]"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def type
|
|
38
|
+
@relationship["type"]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def _get_properties(*keys)
|
|
43
|
+
@session.neo.get_relationship_properties(@relationship, *keys)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def _set_properties(keys)
|
|
47
|
+
@session.neo.set_relationship_properties(@relationship, keys)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def _reset_properties(attributes)
|
|
51
|
+
@session.neo.reset_relationship_properties(@relationship, attributes)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def _remove_properties(keys_to_delete)
|
|
55
|
+
@session.neo.remove_relationship_properties(@relationship, keys_to_delete)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def _set_private_vars_to_nil
|
|
59
|
+
@relationship = @session = @start = @end = @nodes = nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def _delete
|
|
63
|
+
@session.neo.delete_relationship(@relationship)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def _destroy
|
|
67
|
+
_delete
|
|
68
|
+
@start.del
|
|
69
|
+
@end.del
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/neon/session.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require "neon/session/rest"
|
|
2
|
+
require "neon/session/embedded"
|
|
3
|
+
require "neon/session/invalid_session"
|
|
4
|
+
|
|
5
|
+
module Neon
|
|
6
|
+
# A session established with a Neo4J database.
|
|
7
|
+
module Session
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor :current # The current default session running right now.
|
|
10
|
+
|
|
11
|
+
# Create a new session with the database.
|
|
12
|
+
#
|
|
13
|
+
# @param type [:rest, :embedded] the type of session to create. Any other type will raise a InvalidSesionTypeError.
|
|
14
|
+
# @param args [Array] other args to pass to the session - usually stuff like the address of the database.
|
|
15
|
+
#
|
|
16
|
+
# @return [Session] a new session of type *type* and to the database initiated with *args*.
|
|
17
|
+
def new(type, *args)
|
|
18
|
+
session = case type
|
|
19
|
+
when :rest
|
|
20
|
+
Rest.new(*args)
|
|
21
|
+
when :embedded
|
|
22
|
+
Embedded.new(*args)
|
|
23
|
+
else
|
|
24
|
+
raise InvalidSessionTypeError.new(type)
|
|
25
|
+
end
|
|
26
|
+
# Set the current session unless one already exists
|
|
27
|
+
@current = session unless @current
|
|
28
|
+
session
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Class] the class of the current session.
|
|
32
|
+
def class
|
|
33
|
+
@current.class
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Boolean] wether the current session is running or not.
|
|
37
|
+
def running?
|
|
38
|
+
if @current
|
|
39
|
+
@current.running?
|
|
40
|
+
else
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Starts the current session.
|
|
46
|
+
# @return [Boolean] wether the session started successfully or not.
|
|
47
|
+
def start
|
|
48
|
+
if @current
|
|
49
|
+
@current.start
|
|
50
|
+
else
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Stops the current session
|
|
56
|
+
# @return [Boolean] wether the session stopped successfully or not.
|
|
57
|
+
def stop
|
|
58
|
+
if @current
|
|
59
|
+
result = @current.stop
|
|
60
|
+
@current = nil if result
|
|
61
|
+
result
|
|
62
|
+
else
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module Neon
|
|
2
|
+
module Session
|
|
3
|
+
# A session to an embedded instance of Neo4J
|
|
4
|
+
class Embedded
|
|
5
|
+
include TransactionHelpers
|
|
6
|
+
# @!attribute
|
|
7
|
+
# @return [Boolean] Auto Transaction flag. Enabled by default.
|
|
8
|
+
attr_accessor :auto_tx
|
|
9
|
+
|
|
10
|
+
# Create a new session to an embedded database.
|
|
11
|
+
#
|
|
12
|
+
# @param path [String] a path to the location of the embedded database.
|
|
13
|
+
# @param auto_tx [Boolean] an optional flag to set auto transaction (defaults to true).
|
|
14
|
+
#
|
|
15
|
+
# @return [Embedded] a new embedded session.
|
|
16
|
+
def initialize(path = "neo4j", auto_tx = true)
|
|
17
|
+
raise "Cannot start a embedded session without JRuby" if RUBY_PLATFORM != 'java'
|
|
18
|
+
@db_location = path
|
|
19
|
+
@running = false
|
|
20
|
+
@auto_tx = auto_tx
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [Boolean] wether the session is running or not.
|
|
24
|
+
def running?
|
|
25
|
+
@running
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Java::OrgNeo4jKernel::EmbeddedGraphDatabase] the Java graph database backing this session.
|
|
29
|
+
def database
|
|
30
|
+
@db
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Boolean] wether the session started successfully.
|
|
34
|
+
def start
|
|
35
|
+
return false if @started
|
|
36
|
+
@started = true
|
|
37
|
+
@db = Java::OrgNeo4jGraphdbFactory::GraphDatabaseFactory.new.new_embedded_database(@db_location)
|
|
38
|
+
@running = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Boolean] wether the session stopped successfully.
|
|
42
|
+
def stop
|
|
43
|
+
return false if @stopped
|
|
44
|
+
@db.shutdown
|
|
45
|
+
@running = false
|
|
46
|
+
@stopped = true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def begin_tx
|
|
50
|
+
@db.begin_tx
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def run_tx(&block)
|
|
54
|
+
Transaction::Placebo.run(begin_tx, &block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Nodes
|
|
58
|
+
# Create a new node. If auto_tx is true then we begin a new transaction and commit it after the creation
|
|
59
|
+
def create_node(attributes, labels)
|
|
60
|
+
run_in_transaction { _create_node attributes, labels }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def load(id)
|
|
64
|
+
run_in_transaction { _load id }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def load_rel(id)
|
|
68
|
+
run_in_transaction { _load_rel id }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def to_s
|
|
72
|
+
@db_location
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
def _create_node(attributes, labels)
|
|
77
|
+
labels.map! { |label| Java::OrgNeo4jGraphdb::DynamicLabel.label(label) }
|
|
78
|
+
node = @db.create_node(*labels)
|
|
79
|
+
node.props = attributes
|
|
80
|
+
node
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def _load(id)
|
|
84
|
+
@db.get_node_by_id(id)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def _load_rel(id)
|
|
88
|
+
@db.get_relationship_by_id(id)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require "neography"
|
|
2
|
+
|
|
3
|
+
module Neon
|
|
4
|
+
module Session
|
|
5
|
+
class Rest
|
|
6
|
+
attr_reader :neo, :url
|
|
7
|
+
attr_accessor :auto_tx
|
|
8
|
+
|
|
9
|
+
def initialize(url = "http://localhost:7474", auto_tx = false)
|
|
10
|
+
@neo = Neography::Rest.new url
|
|
11
|
+
@url = url
|
|
12
|
+
@auto_tx = auto_tx
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# These methods make no sense for a rest server so we just return true to make our specs happy
|
|
16
|
+
def start
|
|
17
|
+
true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
alias :stop :start
|
|
21
|
+
alias :running? :start
|
|
22
|
+
|
|
23
|
+
def create_node(attributes, labels)
|
|
24
|
+
node = @neo.create_node(attributes)
|
|
25
|
+
return nil if node.nil?
|
|
26
|
+
@neo.add_label(node, labels)
|
|
27
|
+
Neon::Node::Rest.new(node, self)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def load(id)
|
|
31
|
+
node = @neo.get_node(id)
|
|
32
|
+
Neon::Node::Rest.new(node, self)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def load_rel(id)
|
|
36
|
+
rel = @neo.get_relationship(id)
|
|
37
|
+
Relationship::Rest.new(rel, self)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def begin_tx
|
|
41
|
+
Transaction::Rest.begin_tx(self)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def run_tx(&block)
|
|
45
|
+
Transaction::Placebo.run(begin_tx, &block)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_s
|
|
49
|
+
@url
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Neon
|
|
2
|
+
module Transaction
|
|
3
|
+
class << self
|
|
4
|
+
# Begins a transaction
|
|
5
|
+
#
|
|
6
|
+
# @param session [Session::Rest, Session::Embedded] the current running session
|
|
7
|
+
#
|
|
8
|
+
# @return [Transaction::Rest, Java::OrgNeo4jKernel::PlaceboTransaction] a new transaction if one is not currently running.
|
|
9
|
+
# Otherwise it returns the currently running transaction.
|
|
10
|
+
def begin(session = Session.current)
|
|
11
|
+
session.begin_tx
|
|
12
|
+
rescue NoMethodError => e
|
|
13
|
+
_raise_invalid_session_error(session, e)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def run(session = Session.current, &block)
|
|
17
|
+
session.run_tx(&block)
|
|
18
|
+
rescue NoMethodError => e
|
|
19
|
+
_raise_invalid_session_error(session, e)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def _raise_invalid_session_error(session, e)
|
|
24
|
+
raise Neon::Session::InvalidSessionTypeError.new(session.class), e.to_s
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Neon
|
|
2
|
+
module Transaction
|
|
3
|
+
class Placebo
|
|
4
|
+
def success
|
|
5
|
+
@success = true
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def failure
|
|
9
|
+
@success = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def close
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def success?
|
|
16
|
+
@success
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.run(tx)
|
|
20
|
+
placebo = new
|
|
21
|
+
placebo.success # Mark for success by default
|
|
22
|
+
result = yield(placebo) if block_given?
|
|
23
|
+
if placebo.success?
|
|
24
|
+
tx.success
|
|
25
|
+
else
|
|
26
|
+
tx.failure
|
|
27
|
+
end
|
|
28
|
+
tx.close
|
|
29
|
+
return result, placebo.success?
|
|
30
|
+
rescue Exception => e
|
|
31
|
+
# Roll back the transaction
|
|
32
|
+
tx.failure
|
|
33
|
+
tx.close
|
|
34
|
+
raise e # Let the exception bubble up
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|