neo4j-core 3.0.0.alpha.1

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +22 -0
  3. data/README.md +332 -0
  4. data/lib/neo4j-core.rb +27 -0
  5. data/lib/neo4j-core/cypher_translator.rb +34 -0
  6. data/lib/neo4j-core/hash_with_indifferent_access.rb +165 -0
  7. data/lib/neo4j-core/helpers.rb +25 -0
  8. data/lib/neo4j-core/label.rb +8 -0
  9. data/lib/neo4j-core/version.rb +5 -0
  10. data/lib/neo4j-embedded.rb +18 -0
  11. data/lib/neo4j-embedded/embedded_database.rb +29 -0
  12. data/lib/neo4j-embedded/embedded_label.rb +80 -0
  13. data/lib/neo4j-embedded/embedded_node.rb +163 -0
  14. data/lib/neo4j-embedded/embedded_relationship.rb +44 -0
  15. data/lib/neo4j-embedded/embedded_session.rb +151 -0
  16. data/lib/neo4j-embedded/property.rb +43 -0
  17. data/lib/neo4j-embedded/to_java.rb +49 -0
  18. data/lib/neo4j-server.rb +10 -0
  19. data/lib/neo4j-server/cypher_label.rb +28 -0
  20. data/lib/neo4j-server/cypher_node.rb +140 -0
  21. data/lib/neo4j-server/cypher_node_uncommited.rb +12 -0
  22. data/lib/neo4j-server/cypher_relationship.rb +82 -0
  23. data/lib/neo4j-server/cypher_response.rb +113 -0
  24. data/lib/neo4j-server/cypher_session.rb +156 -0
  25. data/lib/neo4j-server/cypher_transaction.rb +81 -0
  26. data/lib/neo4j-server/resource.rb +73 -0
  27. data/lib/neo4j/entity_equality.rb +9 -0
  28. data/lib/neo4j/jars/concurrentlinkedhashmap-lru-1.3.1.jar +0 -0
  29. data/lib/neo4j/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
  30. data/lib/neo4j/jars/lucene-core-3.6.2.jar +0 -0
  31. data/lib/neo4j/jars/neo4j-cypher-2.0.0-M06.jar +0 -0
  32. data/lib/neo4j/jars/neo4j-kernel-2.0-SNAPSHOT-tests.jar +0 -0
  33. data/lib/neo4j/jars/neo4j-kernel-2.0.0-M06.jar +0 -0
  34. data/lib/neo4j/jars/neo4j-lucene-index-2.0.0-M06.jar +0 -0
  35. data/lib/neo4j/jars/neo4j-management-2.0.0-M06.jar +0 -0
  36. data/lib/neo4j/jars/org.apache.servicemix.bundles.jline-0.9.94_1.jar +0 -0
  37. data/lib/neo4j/jars/parboiled-core-1.1.6.jar +0 -0
  38. data/lib/neo4j/jars/parboiled-scala_2.10-1.1.6.jar +0 -0
  39. data/lib/neo4j/jars/scala-library-2.10.2.jar +0 -0
  40. data/lib/neo4j/label.rb +88 -0
  41. data/lib/neo4j/node.rb +185 -0
  42. data/lib/neo4j/property_container.rb +22 -0
  43. data/lib/neo4j/property_validator.rb +23 -0
  44. data/lib/neo4j/relationship.rb +84 -0
  45. data/lib/neo4j/session.rb +124 -0
  46. data/lib/neo4j/tasks/neo4j_server.rb +131 -0
  47. data/lib/neo4j/transaction.rb +52 -0
  48. data/neo4j-core.gemspec +35 -0
  49. metadata +144 -0
@@ -0,0 +1,43 @@
1
+
2
+ # TODO code duplication with the Neo4j::PropertyContainer,
3
+ # This module should extend that module by adding transaction around methods
4
+ module Neo4j::Embedded::Property
5
+ include Neo4j::PropertyValidator
6
+ include Neo4j::PropertyContainer
7
+ extend Neo4j::Core::TxMethods
8
+
9
+ # inherit the []= method but add auto transaction around it
10
+ tx_methods :[]=
11
+
12
+ def [](key)
13
+ return nil unless has_property?(key.to_s)
14
+ get_property(key.to_s)
15
+ end
16
+ tx_methods :[]
17
+
18
+ def props
19
+ property_keys.inject({}) do |ret, key|
20
+ ret[key.to_sym] = get_property(key)
21
+ ret
22
+ end
23
+ end
24
+ tx_methods :props
25
+
26
+
27
+ def props=(hash)
28
+ property_keys.each do |key|
29
+ remove_property(key)
30
+ end
31
+
32
+ hash.each_pair do |k,v|
33
+ set_property(k,v)
34
+ end
35
+ hash
36
+ end
37
+ tx_methods :props=
38
+
39
+
40
+ def neo_id
41
+ get_id
42
+ end
43
+ end
@@ -0,0 +1,49 @@
1
+ module Neo4j::Embedded
2
+ # A Utility class for translating Ruby object to Neo4j Java types
3
+ # @private
4
+ module ToJava
5
+ def type_to_java(type)
6
+ Java::OrgNeo4jGraphdb::DynamicRelationshipType.withName(type.to_s)
7
+ end
8
+
9
+ module_function :type_to_java
10
+
11
+ def types_to_java(types)
12
+ types.inject([]) { |result, type| result << type_to_java(type) }.to_java(Java::OrgNeo4jGraphdb::RelationshipType)
13
+ end
14
+
15
+ module_function :types_to_java
16
+
17
+
18
+ def dir_from_java(dir)
19
+ case dir
20
+ when Java::OrgNeo4jGraphdb::Direction::OUTGOING then
21
+ :outgoing
22
+ when Java::OrgNeo4jGraphdb::Direction::BOTH then
23
+ :both
24
+ when Java::OrgNeo4jGraphdb::Direction::INCOMING then
25
+ :incoming
26
+ else
27
+ raise "unknown direction '#{dir} / #{dir.class}'"
28
+ end
29
+ end
30
+
31
+ module_function :dir_from_java
32
+
33
+ def dir_to_java(dir)
34
+ case dir
35
+ when :outgoing then
36
+ Java::OrgNeo4jGraphdb::Direction::OUTGOING
37
+ when :both then
38
+ Java::OrgNeo4jGraphdb::Direction::BOTH
39
+ when :incoming then
40
+ Java::OrgNeo4jGraphdb::Direction::INCOMING
41
+ else
42
+ raise "unknown direction '#{dir}', expects argument: outgoing, :incoming or :both"
43
+ end
44
+ end
45
+
46
+ module_function :dir_to_java
47
+
48
+ end
49
+ end
@@ -0,0 +1,10 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'neo4j-server/resource'
4
+ require 'neo4j-server/cypher_node'
5
+ require 'neo4j-server/cypher_label'
6
+ require 'neo4j-server/cypher_session'
7
+ require 'neo4j-server/cypher_node_uncommited'
8
+ require 'neo4j-server/cypher_relationship'
9
+ require 'neo4j-server/cypher_response'
10
+ require 'neo4j-server/cypher_transaction'
@@ -0,0 +1,28 @@
1
+ module Neo4j::Server
2
+ class CypherLabel
3
+ extend Forwardable
4
+ def_delegator :@session, :query_cypher_for
5
+ attr_reader :name
6
+
7
+ def initialize(session, name)
8
+ @name = name
9
+ @session = session
10
+ end
11
+
12
+ def create_index(*properties)
13
+ response = @session._query("CREATE INDEX ON :`#{@name}`(#{properties.join(',')})")
14
+ response.raise_error if response.error?
15
+ end
16
+
17
+ def drop_index(*properties)
18
+ properties.each do |property|
19
+ response = @session._query("DROP INDEX ON :`#{@name}`(#{property})")
20
+ response.raise_error if response.error? && !response.error_msg.match(/No such INDEX ON/)
21
+ end
22
+ end
23
+
24
+ def indexes
25
+ @session.indexes(@name)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,140 @@
1
+ module Neo4j::Server
2
+ class CypherNode < Neo4j::Node
3
+ include Neo4j::Server::Resource
4
+ include Neo4j::Core::CypherTranslator
5
+
6
+ def initialize(session, id)
7
+ @session = session
8
+ @id = id
9
+ end
10
+
11
+ def neo_id
12
+ @id
13
+ end
14
+
15
+ def inspect
16
+ "CypherNode #{neo_id} (#{object_id})"
17
+ end
18
+
19
+ # TODO, needed by neo4j-cypher
20
+ def _java_node
21
+ self
22
+ end
23
+
24
+ # (see Neo4j::Node#create_rel)
25
+ def create_rel(type, other_node, props = nil)
26
+ q = "START a=node(#{neo_id}), b=node(#{other_node.neo_id}) CREATE (a)-[r:`#{type}` #{cypher_prop_list(props)}]->(b) RETURN ID(r)"
27
+ id = @session._query_or_fail(q, true)
28
+ CypherRelationship.new(@session, id)
29
+ end
30
+
31
+ # (see Neo4j::Node#props)
32
+ def props
33
+ props = @session._query_or_fail("START n=node(#{neo_id}) RETURN n", true)['data']
34
+ props.keys.inject({}){|hash,key| hash[key.to_sym] = props[key]; hash}
35
+ end
36
+
37
+ # (see Neo4j::Node#remove_property)
38
+ def remove_property(key)
39
+ @session._query_or_fail("START n=node(#{neo_id}) REMOVE n.`#{key}`")
40
+ end
41
+
42
+ # (see Neo4j::Node#set_property)
43
+ def set_property(key,value)
44
+ @session._query_or_fail("START n=node(#{neo_id}) SET n.`#{key}` = { value }", false, value: value)
45
+ value
46
+ end
47
+
48
+ # (see Neo4j::Node#props=)
49
+ def props=(properties)
50
+ @session._query_or_fail("START n=node(#{neo_id}) SET n = { props }", false, {props: properties})
51
+ properties
52
+ end
53
+
54
+ # (see Neo4j::Node#get_property)
55
+ def get_property(key)
56
+ @session._query_or_fail("START n=node(#{neo_id}) RETURN n.`#{key}`", true)
57
+ end
58
+
59
+ # (see Neo4j::Node#labels)
60
+ def labels
61
+ r = @session._query_or_fail("START n=node(#{neo_id}) RETURN labels(n) as labels", true)
62
+ r.map(&:to_sym)
63
+ end
64
+
65
+ # (see Neo4j::Node#del)
66
+ def del
67
+ @session._query_or_fail("START n = node(#{neo_id}) MATCH n-[r]-() DELETE r")
68
+ @session._query_or_fail("START n = node(#{neo_id}) DELETE n")
69
+ end
70
+
71
+ # (see Neo4j::Node#exist?)
72
+ def exist?
73
+ response = @session._query("START n=node(#{neo_id}) RETURN ID(n)")
74
+ if (!response.error?)
75
+ return true
76
+ elsif (response.error_status == 'EntityNotFoundException')
77
+ return false
78
+ else
79
+ response.raise_error
80
+ end
81
+ end
82
+
83
+
84
+ # (see Neo4j::Node#node)
85
+ def node(match={})
86
+ result = match(CypherNode, "ID(p)", match)
87
+ raise "Expected to only find one relationship from node #{neo_id} matching #{match.inspect} but found #{result.count}" if result.count > 1
88
+ result.first
89
+ end
90
+
91
+ # (see Neo4j::Node#rel)
92
+ def rel(match={})
93
+ result = match(CypherRelationship, "ID(r)", match)
94
+ raise "Expected to only find one relationship from node #{neo_id} matching #{match.inspect} but found #{result.count}" if result.count > 1
95
+ result.first
96
+ end
97
+
98
+ # (see Neo4j::Node#rel?)
99
+ def rel?(match={})
100
+ result = match(CypherRelationship, "ID(r)", match)
101
+ !!result.first
102
+ end
103
+
104
+ # (see Neo4j::Node#nodes)
105
+ def nodes(match={})
106
+ match(CypherNode, "ID(p)", match)
107
+ end
108
+
109
+
110
+ # (see Neo4j::Node#rels)
111
+ def rels(match = {dir: :both})
112
+ match(CypherRelationship, "ID(r)", match)
113
+ end
114
+
115
+ # @private
116
+ def match(clazz, returns, match={})
117
+ to_dir = {outgoing: ->(rel) {"-#{rel}->"},
118
+ incoming: ->(rel) {"<-#{rel}-"},
119
+ both: ->(rel) {"-#{rel}-"} }
120
+
121
+ cypher_rel = match[:type] ? "[r:`#{match[:type]}`]" : '[r]'
122
+ between_id = match[:between] ? ",p=node(#{match[:between].neo_id}) " : ""
123
+ dir_func = to_dir[match[:dir] || :both]
124
+ cypher = "START n=node(#{neo_id}) #{between_id} MATCH (n)#{dir_func.call(cypher_rel)}(p) RETURN #{returns}"
125
+ r = @session._query(cypher)
126
+ r.raise_error if r.error?
127
+ _map_result(r, clazz)
128
+ end
129
+
130
+ # @private
131
+ def _map_result(r, clazz)
132
+ r.data.map do |rel|
133
+ next if r.uncommited? ? rel['row'].first.nil? : rel.first.nil?
134
+ id = r.uncommited? ? rel['row'].first : rel.first
135
+ clazz.new(@session, id)
136
+ end.compact
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,12 @@
1
+ module Neo4j::Server
2
+ class CypherNodeUncommited
3
+ def initialize(db, data)
4
+ @db = db
5
+ @data = data
6
+ end
7
+
8
+ def [](key)
9
+ @data[key.to_s]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,82 @@
1
+ module Neo4j::Server
2
+
3
+ class CypherRelationship < Neo4j::Relationship
4
+ include Neo4j::Server::Resource
5
+
6
+ def initialize(session, id)
7
+ @session = session
8
+ @id = id
9
+ end
10
+
11
+ def ==(o)
12
+ o.class == self.class && o.neo_id == neo_id
13
+ end
14
+ alias_method :eql?, :==
15
+
16
+ def neo_id
17
+ @id
18
+ end
19
+
20
+ def inspect
21
+ "CypherRelationship #{neo_id}"
22
+ end
23
+
24
+ def load_resource
25
+ id = neo_id
26
+ unless @resource_data
27
+ r = @session._query_internal{ rel(id) }
28
+ @resource_data = r.first_data
29
+ end
30
+ end
31
+
32
+ def start_node
33
+ load_resource
34
+ id = resource_url_id(resource_url(:start))
35
+ Neo4j::Node.load(id)
36
+ end
37
+
38
+ def end_node
39
+ load_resource
40
+ id = resource_url_id(resource_url(:end))
41
+ Neo4j::Node.load(id)
42
+ end
43
+
44
+ def get_property(key)
45
+ id = neo_id
46
+ r = @session._query_internal{rel(id)[key]}
47
+ expect_response_code(r.response, 200)
48
+ r.first_data
49
+ end
50
+
51
+ def set_property(key,value)
52
+ id = neo_id
53
+ r = @session._query_internal{rel(id)[key]=value}
54
+ expect_response_code(r.response, 200)
55
+ end
56
+
57
+ def remove_property(key)
58
+ id = neo_id
59
+ r = @session._query_internal{rel(id)[key]=:NULL}
60
+ expect_response_code(r.response, 200)
61
+ end
62
+
63
+ def del
64
+ id = neo_id
65
+ @session._query_internal{rel(id).del}.raise_unless_response_code(200)
66
+ end
67
+
68
+ def exist?
69
+ id = neo_id
70
+ response = @session._query_internal{rel(id)}
71
+
72
+ if (!response.error?)
73
+ return true
74
+ elsif (response.error_status == 'BadInputException') # TODO see github issue neo4j/1061
75
+ return false
76
+ else
77
+ response.raise_error
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,113 @@
1
+ module Neo4j::Server
2
+ class CypherResponse
3
+ attr_reader :data, :columns, :error_msg, :error_status, :error_code, :response
4
+
5
+ class ResponseError < StandardError
6
+ attr_reader :status, :code
7
+
8
+ def initialize(msg, status, code)
9
+ super(msg)
10
+ @status = status
11
+ @code = code
12
+ end
13
+ end
14
+
15
+
16
+ class HashEnumeration
17
+ include Enumerable
18
+ extend Forwardable
19
+ def_delegator :@response, :error_msg
20
+ def_delegator :@response, :error_status
21
+ def_delegator :@response, :error_code
22
+ def_delegator :@response, :data
23
+ def_delegator :@response, :columns
24
+
25
+ def initialize(response)
26
+ @response = response
27
+ end
28
+
29
+ def each()
30
+ data.each do |row|
31
+ row.each_with_index do |row, i|
32
+ yield columns[i].to_sym => row[i]
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def to_hash_enumeration
39
+ HashEnumeration.new(self)
40
+ end
41
+
42
+ def initialize(response, uncommited = false)
43
+ @response = response
44
+ @uncommited = uncommited
45
+ end
46
+
47
+
48
+ def first_data
49
+ if uncommited?
50
+ @data.first['row'].first
51
+ else
52
+ @data[0][0]
53
+ end
54
+ end
55
+
56
+ def error?
57
+ !!@error
58
+ end
59
+
60
+ def uncommited?
61
+ @uncommited
62
+ end
63
+
64
+ def raise_unless_response_code(code)
65
+ raise "Response code #{response.code}, expected #{code} for #{response.request.path}, #{response.body}" unless response.code == code
66
+ end
67
+
68
+ def set_data(data, columns)
69
+ @data = data
70
+ @columns = columns
71
+ self
72
+ end
73
+
74
+ def set_error(error_msg, error_status, error_core)
75
+ @error = true
76
+ @error_msg = error_msg
77
+ @error_status = error_status
78
+ @error_code = error_core
79
+ self
80
+ end
81
+
82
+ def raise_error
83
+ raise "Tried to raise error without an error" unless @error
84
+ raise ResponseError.new(@error_msg, @error_status, @error_code)
85
+ end
86
+
87
+ def self.create_with_no_tx(response)
88
+ case response.code
89
+ when 200
90
+ CypherResponse.new(response).set_data(response['data'], response['columns'])
91
+ when 400
92
+ CypherResponse.new(response).set_error(response['message'], response['exception'], response['fullname'])
93
+ else
94
+ raise "Unknown response code #{response.code} for #{response.request.path.to_s}"
95
+ end
96
+ end
97
+
98
+ def self.create_with_tx(response)
99
+ raise "Unknown response code #{response.code} for #{response.request.path.to_s}" unless response.code == 200
100
+
101
+ first_result = response['results'][0]
102
+ cr = CypherResponse.new(response, true)
103
+
104
+ if (response['errors'].empty?)
105
+ cr.set_data(first_result['data'], first_result['columns'])
106
+ else
107
+ first_error = response['errors'].first
108
+ cr.set_error(first_error['message'], first_error['status'], first_error['code'])
109
+ end
110
+ cr
111
+ end
112
+ end
113
+ end