neo4j-core 4.0.7 → 5.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,7 @@ module Neo4j
6
6
 
7
7
  def initialize(node, match)
8
8
  @node = node
9
+ ::Neo4j::Node.validate_match!(match)
9
10
  @match = match
10
11
  end
11
12
 
@@ -46,7 +47,7 @@ module Neo4j
46
47
  end
47
48
  end
48
49
 
49
- class EmbeddedNode
50
+ class EmbeddedNode < Neo4j::Node
50
51
  class << self
51
52
  # This method is used to extend a Java Neo4j class so that it includes the same mixins as this class.
52
53
  Java::OrgNeo4jKernelImplCore::NodeProxy.class_eval do
@@ -149,41 +150,32 @@ module Neo4j
149
150
  tx_methods :rel
150
151
 
151
152
  def _rel(match = {})
152
- dir = ToJava.dir_to_java(match[:dir] || :both)
153
- rel_type = match[:type]
153
+ dir, rel_type, between_id = _parse_match(match)
154
154
 
155
155
  rel = if rel_type
156
- get_single_relationship(ToJava.type_to_java(rel_type), dir)
156
+ get_single_relationship(rel_type, dir)
157
157
  else
158
- iter = get_relationships(dir).iterator
159
-
160
- if iter.has_next
161
- first = iter.next
162
- fail "Expected to only find one relationship from node #{neo_id} matching #{match.inspect}" if iter.has_next
163
- first
164
- end
158
+ _get_single_relationship_by_dir(dir)
165
159
  end
166
160
 
167
- between_id = match[:between] && match[:between].neo_id
161
+ rel if !(rel && between_id) || rel.other_node(self).neo_id == between_id
162
+ end
168
163
 
169
- if rel && between_id
170
- rel if rel.other_node(self).neo_id == between_id
171
- else
172
- rel
173
- end
164
+ def _get_single_relationship_by_dir(dir)
165
+ iter = get_relationships(dir).iterator
166
+
167
+ return nil if not iter.has_next
168
+
169
+ first = iter.next
170
+ fail "Expected to only find one relationship from node #{neo_id} matching #{match.inspect}" if iter.has_next
171
+ first
174
172
  end
175
173
 
176
174
  def _rels(match = {})
177
- dir = match[:dir] || :both
178
- rel_type = match[:type]
175
+ dir, rel_type, between_id = _parse_match(match)
179
176
 
180
- rels = if rel_type
181
- get_relationships(ToJava.type_to_java(rel_type), ToJava.dir_to_java(dir)).iterator
182
- else
183
- get_relationships(ToJava.dir_to_java(dir)).iterator
184
- end
185
-
186
- between_id = match[:between] && match[:between].neo_id
177
+ args = rel_type ? [rel_type, dir] : [dir]
178
+ rels = get_relationships(*args).iterator
187
179
 
188
180
  if between_id
189
181
  rels.find_all { |r| r.end_node.neo_id == between_id || r.start_node.neo_id == between_id }
@@ -192,8 +184,14 @@ module Neo4j
192
184
  end
193
185
  end
194
186
 
195
- def class
196
- Neo4j::Node
187
+ def _parse_match(match)
188
+ ::Neo4j::Node.validate_match!(match)
189
+
190
+ [
191
+ ToJava.dir_to_java(match[:dir] || :both),
192
+ ToJava.type_to_java(match[:type]),
193
+ match[:between] && match[:between].neo_id
194
+ ]
197
195
  end
198
196
 
199
197
  include Neo4j::Node::Wrapper
@@ -16,7 +16,6 @@ module Neo4j
16
16
  extend Neo4j::Core::TxMethods
17
17
  def_delegator :@graph_db, :begin_tx
18
18
 
19
-
20
19
  def initialize(db_location, config = {})
21
20
  @db_location = db_location
22
21
  @auto_commit = !!config[:auto_commit]
@@ -34,7 +33,7 @@ module Neo4j
34
33
 
35
34
  def version
36
35
  # Wow
37
- version_string = graph_db.to_java(Java::OrgNeo4jKernel::GraphDatabaseAPI).getDependencyResolver.resolveDependency(Java::OrgNeo4jKernel::KernelData.java_class).version.to_s
36
+ version_string = @graph_db.to_java(Java::OrgNeo4jKernel::GraphDatabaseAPI).getDependencyResolver.resolveDependency(Java::OrgNeo4jKernel::KernelData.java_class).version.to_s
38
37
  version_string.split(' ')[-1]
39
38
  end
40
39
 
@@ -69,12 +68,14 @@ module Neo4j
69
68
  end
70
69
 
71
70
  def shutdown
72
- graph_db && graph_db.shutdown
71
+ @graph_db && @graph_db.shutdown
72
+
73
+ Neo4j::Session.clear_listeners
73
74
  @graph_db = nil
74
75
  end
75
76
 
76
77
  def running?
77
- !!graph_db
78
+ !!@graph_db
78
79
  end
79
80
 
80
81
  def create_label(name)
@@ -152,10 +153,10 @@ module Neo4j
152
153
 
153
154
  def create_node(properties = nil, labels = [])
154
155
  if labels.empty?
155
- graph_db.create_node
156
+ @graph_db.create_node
156
157
  else
157
158
  labels = EmbeddedLabel.as_java(labels)
158
- graph_db.create_node(labels)
159
+ @graph_db.create_node(labels)
159
160
  end.tap do |java_node|
160
161
  properties.each_pair { |k, v| java_node[k] = v } if properties
161
162
  end
@@ -22,12 +22,12 @@ module Neo4j
22
22
  "EmbeddedTransaction [nested: #{@pushed_nested} failed?: #{failure?} active: #{Neo4j::Transaction.current == self}]"
23
23
  end
24
24
 
25
- def _delete_tx
25
+ def delete
26
26
  @root_tx.failure
27
27
  @root_tx.close
28
28
  end
29
29
 
30
- def _commit_tx
30
+ def commit
31
31
  @root_tx.success
32
32
  @root_tx.close
33
33
  end
@@ -0,0 +1,65 @@
1
+ module Neo4j
2
+ class Label
3
+ class << self
4
+ def indexes
5
+ schema_query(:get_indexes)
6
+ end
7
+
8
+ def constraints
9
+ schema_query(:get_constraints)
10
+ end
11
+
12
+ def index?(label, property)
13
+ schema_index_operation(label, property, :indexes)
14
+ end
15
+
16
+ def constraint?(label, property)
17
+ schema_index_operation(label, property, :constraints)
18
+ end
19
+
20
+ def drop_all_indexes
21
+ Neo4j::Transaction.run do
22
+ schema.get_indexes.to_a.each do |i|
23
+ begin
24
+ i.drop
25
+ rescue Java::JavaLang::IllegalStateException; end
26
+ end
27
+ end
28
+ end
29
+
30
+ def drop_all_constraints
31
+ Neo4j::Transaction.run do
32
+ schema.get_constraints.to_a.each(&:drop)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def schema_query(query_method)
39
+ [].tap do |index_array|
40
+ Neo4j::Transaction.run do
41
+ schema.send(query_method).to_a.each { |i| index_array << index_hash(i) }
42
+ end
43
+ end
44
+ end
45
+
46
+ def schema_index_operation(label, property, schema_method)
47
+ label = label.to_s
48
+ property = property.to_s
49
+ !send(schema_method).select { |i| matched_index(i, label, property) }.empty?
50
+ end
51
+
52
+ def index_hash(java_index)
53
+ {property_keys: java_index.get_property_keys.to_a, label: java_index.get_label.name}
54
+ end
55
+
56
+ def matched_index(java_index, label, property)
57
+ java_index[:property_keys].first == property && java_index[:label] == label
58
+ end
59
+
60
+ def schema
61
+ Neo4j::Session.current.graph_db.schema
62
+ end
63
+ end
64
+ end
65
+ end
@@ -75,15 +75,13 @@ module Neo4j
75
75
  end
76
76
 
77
77
  def to_java_property(k, v)
78
- validate_property(v)
78
+ validate_property!(v)
79
79
 
80
80
  k = k.to_s
81
81
  case v
82
- when nil
83
- remove_property(k)
82
+ when nil then remove_property(k)
84
83
  when Array
85
- type = java_type_from_value(v[0]) || fail("Not allowed to store array with value #{v[0]} type #{v[0].class}")
86
- set_property(k, v.to_java(type))
84
+ set_property(k, v.to_java(java_type_from_value(v[0])))
87
85
  else
88
86
  set_property(k, v)
89
87
  end
@@ -99,6 +97,8 @@ module Neo4j
99
97
  :boolean
100
98
  when Fixnum
101
99
  :long
100
+ else
101
+ fail "Not allowed to store array with value #{value.inspect} type #{value.class}"
102
102
  end
103
103
  end
104
104
  end
@@ -4,7 +4,7 @@ module Neo4j
4
4
  # @private
5
5
  module ToJava
6
6
  def type_to_java(type)
7
- Java::OrgNeo4jGraphdb::DynamicRelationshipType.withName(type.to_s)
7
+ type && Java::OrgNeo4jGraphdb::DynamicRelationshipType.withName(type.to_s)
8
8
  end
9
9
 
10
10
  module_function :type_to_java
@@ -18,12 +18,9 @@ module Neo4j
18
18
 
19
19
  def dir_from_java(dir)
20
20
  case dir
21
- when Java::OrgNeo4jGraphdb::Direction::OUTGOING then
22
- :outgoing
23
- when Java::OrgNeo4jGraphdb::Direction::BOTH then
24
- :both
25
- when Java::OrgNeo4jGraphdb::Direction::INCOMING then
26
- :incoming
21
+ when Java::OrgNeo4jGraphdb::Direction::OUTGOING then :outgoing
22
+ when Java::OrgNeo4jGraphdb::Direction::BOTH then :both
23
+ when Java::OrgNeo4jGraphdb::Direction::INCOMING then :incoming
27
24
  else
28
25
  fail "unknown direction '#{dir} / #{dir.class}'"
29
26
  end
@@ -33,12 +30,9 @@ module Neo4j
33
30
 
34
31
  def dir_to_java(dir)
35
32
  case dir
36
- when :outgoing then
37
- Java::OrgNeo4jGraphdb::Direction::OUTGOING
38
- when :both then
39
- Java::OrgNeo4jGraphdb::Direction::BOTH
40
- when :incoming then
41
- Java::OrgNeo4jGraphdb::Direction::INCOMING
33
+ when :outgoing then Java::OrgNeo4jGraphdb::Direction::OUTGOING
34
+ when :both then Java::OrgNeo4jGraphdb::Direction::BOTH
35
+ when :incoming then Java::OrgNeo4jGraphdb::Direction::INCOMING
42
36
  else
43
37
  fail "unknown direction '#{dir}', expects argument: outgoing, :incoming or :both"
44
38
  end
@@ -8,6 +8,7 @@ require 'neo4j-embedded/embedded_label'
8
8
  require 'neo4j-embedded/embedded_node'
9
9
  require 'neo4j-embedded/embedded_relationship'
10
10
  require 'neo4j-embedded/embedded_label'
11
+ require 'neo4j-embedded/label'
11
12
  require 'neo4j-embedded/embedded_transaction'
12
13
  require 'neo4j-embedded/cypher_response'
13
14
 
@@ -9,10 +9,9 @@ module Neo4j
9
9
  @session = session
10
10
 
11
11
  @neo_id = if value.is_a?(Hash)
12
- hash = value['data']
13
- @props = Hash[hash.map { |k, v| [k.to_sym, v] }]
14
- @labels = value['metadata']['labels'].map!(&:to_sym) if value['metadata']
15
- value['id'] # value['self'].match(/\d+$/)[0].to_i
12
+ @props = value[:data]
13
+ @labels = value[:metadata][:labels].map!(&:to_sym) if value[:metadata]
14
+ value[:id]
16
15
  else
17
16
  value
18
17
  end
@@ -31,15 +30,12 @@ module Neo4j
31
30
 
32
31
  # (see Neo4j::Node#create_rel)
33
32
  def create_rel(type, other_node, props = nil)
34
- ids_hash = {start_neo_id: neo_id, end_neo_id: other_node.neo_id}
35
- props_with_ids = props.nil? ? ids_hash : cypher_prop_list(props).merge(ids_hash)
36
- id = @session._query_or_fail(rel_string(type, other_node, props), true, props_with_ids)
37
- data_hash = {'type' => type, 'data' => props, 'start' => neo_id, 'end' => other_node.neo_id, 'id' => id}
38
- CypherRelationship.new(@session, data_hash)
39
- end
33
+ q = @session.query.match(:a, :b).where(a: {neo_id: neo_id}, b: {neo_id: other_node.neo_id})
34
+ .create("(a)-[r:`#{type}`]->(b)").break.set(r: props).return(r: :neo_id)
35
+
36
+ id = @session._query_or_fail(q, true)
40
37
 
41
- def rel_string(type, other_node, props)
42
- "MATCH (a), (b) WHERE ID(a) = {start_neo_id} AND ID(b) = {end_neo_id} CREATE (a)-[r:`#{type}` #{prop_identifier(props)}]->(b) RETURN ID(r)"
38
+ CypherRelationship.new(@session, type: type, data: props, start: neo_id, end: other_node.neo_id, id: id)
43
39
  end
44
40
 
45
41
  # (see Neo4j::Node#props)
@@ -47,8 +43,8 @@ module Neo4j
47
43
  if @props
48
44
  @props
49
45
  else
50
- hash = @session._query_entity_data("#{match_start} RETURN n", nil, neo_id: neo_id)
51
- @props = Hash[hash['data'].map { |k, v| [k.to_sym, v] }]
46
+ hash = @session._query_entity_data(match_start_query.return(:n), nil)
47
+ @props = Hash[hash[:data].to_a]
52
48
  end
53
49
  end
54
50
 
@@ -59,29 +55,27 @@ module Neo4j
59
55
  # (see Neo4j::Node#remove_property)
60
56
  def remove_property(key)
61
57
  refresh
62
- @session._query_or_fail("#{match_start} REMOVE n.`#{key}`", false, neo_id: neo_id)
58
+ @session._query_or_fail(match_start_query.remove(n: key), false)
63
59
  end
64
60
 
65
61
  # (see Neo4j::Node#set_property)
66
62
  def set_property(key, value)
67
63
  refresh
68
- @session._query_or_fail("#{match_start} SET n.`#{key}` = { value }", false, value: value, neo_id: neo_id)
69
- value
64
+ @session._query_or_fail(match_start_query.set(n: {key => value}), false)
70
65
  end
71
66
 
72
67
  # (see Neo4j::Node#props=)
73
68
  def props=(properties)
74
69
  refresh
75
- @session._query_or_fail("#{match_start} SET n = { props }", false, props: properties, neo_id: neo_id)
70
+ @session._query_or_fail(match_start_query.set_props(n: properties), false)
76
71
  properties
77
72
  end
78
73
 
79
74
  def remove_properties(properties)
75
+ return if properties.empty?
76
+
80
77
  refresh
81
- q = "#{match_start} REMOVE " + properties.map do |k|
82
- "n.`#{k}`"
83
- end.join(', ')
84
- @session._query_or_fail(q, false, neo_id: neo_id)
78
+ @session._query_or_fail(match_start_query.remove(n: properties), false, neo_id: neo_id)
85
79
  end
86
80
 
87
81
  # (see Neo4j::Node#update_props)
@@ -89,23 +83,19 @@ module Neo4j
89
83
  refresh
90
84
  return if properties.empty?
91
85
 
92
- removed_keys = properties.keys.select { |k| properties[k].nil? }
93
- remove_properties(removed_keys) unless removed_keys.empty?
94
- properties_to_set = properties.keys - removed_keys
95
- return if properties_to_set.empty?
96
- props_list = cypher_prop_list(properties)[:props].merge(neo_id: neo_id)
97
- @session._query_or_fail("#{match_start} SET #{cypher_properties(properties_to_set)}", false, props_list)
86
+ @session._query_or_fail(match_start_query.set(n: properties), false)
87
+
98
88
  properties
99
89
  end
100
90
 
101
91
  # (see Neo4j::Node#get_property)
102
92
  def get_property(key)
103
- @props ? @props[key.to_sym] : @session._query_or_fail("#{match_start} RETURN n.`#{key}`", true, neo_id: neo_id)
93
+ @props ? @props[key.to_sym] : @session._query_or_fail(match_start_query.return(n: key), true)
104
94
  end
105
95
 
106
96
  # (see Neo4j::Node#labels)
107
97
  def labels
108
- @labels ||= @session._query_or_fail("#{match_start} RETURN labels(n) as labels", true, neo_id: neo_id).map!(&:to_sym)
98
+ @labels ||= @session._query_or_fail(match_start_query.return('labels(n) AS labels'), true).map(&:to_sym)
109
99
  end
110
100
 
111
101
  def _cypher_label_list(labels_list)
@@ -113,18 +103,29 @@ module Neo4j
113
103
  end
114
104
 
115
105
  def add_label(*new_labels)
116
- @session._query_or_fail("#{match_start} SET n #{_cypher_label_list(new_labels)}", false, neo_id: neo_id)
106
+ @session._query_or_fail(match_start_query.set(n: new_labels), false)
117
107
  new_labels.each { |label| labels << label }
118
108
  end
119
109
 
120
110
  def remove_label(*target_labels)
121
- @session._query_or_fail("#{match_start} REMOVE n #{_cypher_label_list(target_labels)}", false, neo_id: neo_id)
111
+ @session._query_or_fail(match_start_query.remove(n: target_labels), false)
122
112
  target_labels.each { |label| labels.delete(label) } unless labels.nil?
123
113
  end
124
114
 
125
115
  def set_label(*label_names)
126
- q = "#{match_start} #{remove_labels_if_needed} #{set_labels_if_needed(label_names)}"
127
- @session._query_or_fail(q, false, neo_id: neo_id)
116
+ q = match_start_query
117
+
118
+ labels_to_add = label_names.map(&:to_sym).uniq
119
+ labels_to_remove = labels - label_names
120
+
121
+ common_labels = labels & labels_to_add
122
+ labels_to_add -= common_labels
123
+ labels_to_remove -= common_labels
124
+
125
+ q = q.remove(n: labels_to_remove) unless labels_to_remove.empty?
126
+ q = q.set(n: labels_to_add) unless labels_to_add.empty?
127
+
128
+ @session._query_or_fail(q, false) unless (labels_to_add + labels_to_remove).empty?
128
129
  end
129
130
 
130
131
  # (see Neo4j::Node#del)
@@ -138,7 +139,7 @@ module Neo4j
138
139
 
139
140
  # (see Neo4j::Node#exist?)
140
141
  def exist?
141
- @session._query("#{match_start} RETURN ID(n)", neo_id: neo_id).data.empty? ? false : true
142
+ !@session._query(match_start_query.return(n: :neo_id)).data.empty?
142
143
  end
143
144
 
144
145
  # (see Neo4j::Node#node)
@@ -169,55 +170,44 @@ module Neo4j
169
170
 
170
171
  # @private
171
172
  def match(clazz, returns, match = {})
172
- to_dir = {outgoing: ->(rel) { "-#{rel}->" },
173
- incoming: ->(rel) { "<-#{rel}-" },
174
- both: ->(rel) { "-#{rel}-" }}
175
-
176
- cypher_rel = match[:type] ? "[r:`#{match[:type]}`]" : '[r]'
177
- between_id = match[:between] ? "MATCH (p) WHERE ID(p) = #{match[:between].neo_id}" : ''
178
- dir_func = to_dir[match[:dir] || :both]
179
- cypher = "#{match_start} #{between_id} MATCH (n)#{dir_func.call(cypher_rel)}(p) RETURN #{returns}"
180
- r = @session._query(cypher, neo_id: neo_id)
173
+ ::Neo4j::Node.validate_match!(match)
174
+
175
+ query = self.query
176
+
177
+ query = query.match(:p).where(p: {neo_id: match[:between].neo_id}) if match[:between]
178
+
179
+ r = query.match("(n)#{relationship_arrow(match)}(p)").return(returns).response
180
+
181
181
  r.raise_error if r.error?
182
- _map_result(r)
183
- end
184
182
 
185
- def _map_result(r)
186
183
  r.to_node_enumeration.map(&:result)
187
184
  end
188
185
 
189
- private
190
-
191
- def cypher_properties(properties_to_set)
192
- properties_to_set.map! { |k| "n.`#{k}` = {`#{k}`}" }.join(',')
186
+ def query(identifier = :n)
187
+ @session.query.match(identifier).where(identifier => {neo_id: neo_id})
193
188
  end
194
189
 
195
- def remove_labels_if_needed
196
- if labels.empty?
197
- ''
198
- else
199
- " REMOVE n #{_cypher_label_list(labels)}"
200
- end
201
- end
190
+ private
191
+
192
+ def relationship_arrow(match)
193
+ rel_spec = match[:type] ? "[r:`#{match[:type]}`]" : '[r]'
202
194
 
203
- def set_labels_if_needed(label_names)
204
- if label_names.empty?
205
- ''
195
+ case match[:dir] || :both
196
+ when :outgoing then "-#{rel_spec}->"
197
+ when :incoming then "<-#{rel_spec}-"
198
+ when :both then "-#{rel_spec}-"
206
199
  else
207
- " SET n #{_cypher_label_list(label_names.map(&:to_sym).uniq)}"
200
+ fail "Invalid value for relationship_arrow direction: #{match[:dir].inspect}"
208
201
  end
209
202
  end
210
203
 
211
- def ensure_single_relationship(&block)
204
+ def ensure_single_relationship
205
+ fail 'Expected a block' unless block_given?
212
206
  result = yield
213
207
  fail "Expected to only find one relationship from node #{neo_id} matching #{match.inspect} but found #{result.count}" if result.count > 1
214
208
  result.first
215
209
  end
216
210
 
217
- def match_start(identifier = 'n')
218
- "MATCH (#{identifier}) WHERE ID(#{identifier}) = {neo_id}"
219
- end
220
-
221
211
  def match_start_query(identifier = :n)
222
212
  @session.query.match(identifier).where(identifier => {neo_id: neo_id}).with(identifier)
223
213
  end
@@ -7,7 +7,7 @@ module Neo4j
7
7
  end
8
8
 
9
9
  def [](key)
10
- @data[key.to_s]
10
+ @data[key]
11
11
  end
12
12
  end
13
13
  end
@@ -8,11 +8,11 @@ module Neo4j
8
8
  def initialize(session, value)
9
9
  @session = session
10
10
  @response_hash = value
11
- @rel_type = @response_hash['type']
12
- @props = @response_hash['data']
13
- @start_node_neo_id = @response_hash['start'].is_a?(Integer) ? @response_hash['start'] : @response_hash['start'].match(/\d+$/)[0].to_i
14
- @end_node_neo_id = @response_hash['end'].is_a?(Integer) ? @response_hash['end'] : @response_hash['end'].match(/\d+$/)[0].to_i
15
- @id = @response_hash['id']
11
+ @rel_type = @response_hash[:type]
12
+ @props = @response_hash[:data]
13
+ @start_node_neo_id = neo_id_integer(@response_hash[:start])
14
+ @end_node_neo_id = neo_id_integer(@response_hash[:end])
15
+ @id = @response_hash[:id]
16
16
  end
17
17
 
18
18
  def ==(other)
@@ -80,7 +80,7 @@ module Neo4j
80
80
  @props
81
81
  else
82
82
  hash = @session._query_entity_data("#{match_start} RETURN n", nil, neo_id: neo_id)
83
- @props = Hash[hash['data'].map { |k, v| [k.to_sym, v] }]
83
+ @props = Hash[hash[:data].map { |k, v| [k, v] }]
84
84
  end
85
85
  end
86
86
 
@@ -125,6 +125,10 @@ module Neo4j
125
125
  def resource_data_present?
126
126
  !resource_data.nil? && !resource_data.empty?
127
127
  end
128
+
129
+ def neo_id_integer(id_or_url)
130
+ id_or_url.is_a?(Integer) ? id_or_url : id_or_url.match(/\d+$/)[0].to_i
131
+ end
128
132
  end
129
133
  end
130
134
  end