neo4j-core 3.0.0.alpha.13 → 3.0.0.alpha.14

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -49
  3. data/lib/mydb/active_tx_log +1 -0
  4. data/lib/mydb/index/lucene-store.db +0 -0
  5. data/lib/mydb/index/lucene.log.1 +0 -0
  6. data/lib/mydb/index/lucene.log.active +0 -0
  7. data/lib/mydb/lock +0 -0
  8. data/lib/mydb/messages.log +313 -0
  9. data/lib/mydb/neostore +0 -0
  10. data/lib/mydb/neostore.id +0 -0
  11. data/lib/mydb/neostore.labeltokenstore.db +0 -0
  12. data/lib/mydb/neostore.labeltokenstore.db.id +0 -0
  13. data/lib/mydb/neostore.labeltokenstore.db.names +0 -0
  14. data/lib/mydb/neostore.labeltokenstore.db.names.id +0 -0
  15. data/lib/mydb/neostore.nodestore.db +0 -0
  16. data/lib/mydb/neostore.nodestore.db.id +0 -0
  17. data/lib/mydb/neostore.nodestore.db.labels +0 -0
  18. data/lib/mydb/neostore.nodestore.db.labels.id +0 -0
  19. data/lib/mydb/neostore.propertystore.db +0 -0
  20. data/lib/mydb/neostore.propertystore.db.arrays +0 -0
  21. data/lib/mydb/neostore.propertystore.db.arrays.id +0 -0
  22. data/lib/mydb/neostore.propertystore.db.id +0 -0
  23. data/lib/mydb/neostore.propertystore.db.index +0 -0
  24. data/lib/mydb/neostore.propertystore.db.index.id +0 -0
  25. data/lib/mydb/neostore.propertystore.db.index.keys +0 -0
  26. data/lib/mydb/neostore.propertystore.db.index.keys.id +0 -0
  27. data/lib/mydb/neostore.propertystore.db.strings +0 -0
  28. data/lib/mydb/neostore.propertystore.db.strings.id +0 -0
  29. data/lib/mydb/neostore.relationshipstore.db +0 -0
  30. data/lib/mydb/neostore.relationshipstore.db.id +0 -0
  31. data/lib/mydb/neostore.relationshiptypestore.db +0 -0
  32. data/lib/mydb/neostore.relationshiptypestore.db.id +0 -0
  33. data/lib/mydb/neostore.relationshiptypestore.db.names +0 -0
  34. data/lib/mydb/neostore.relationshiptypestore.db.names.id +0 -0
  35. data/lib/mydb/neostore.schemastore.db +0 -0
  36. data/lib/mydb/neostore.schemastore.db.id +0 -0
  37. data/lib/mydb/nioneo_logical.log.1 +0 -0
  38. data/lib/mydb/nioneo_logical.log.active +0 -0
  39. data/lib/mydb/schema/label/lucene/write.lock +0 -0
  40. data/lib/mydb/store_lock +0 -0
  41. data/lib/mydb/tm_tx_log.1 +0 -0
  42. data/lib/neo4j-core.rb +1 -2
  43. data/lib/neo4j-core/query_builder.rb +200 -0
  44. data/lib/neo4j-core/version.rb +1 -1
  45. data/lib/neo4j-embedded.rb +1 -0
  46. data/lib/neo4j-embedded/cypher_response.rb +89 -0
  47. data/lib/neo4j-embedded/cypher_response.rb~ +85 -0
  48. data/lib/neo4j-embedded/embedded_node.rb +12 -0
  49. data/lib/neo4j-embedded/embedded_node.rb~ +201 -0
  50. data/lib/neo4j-embedded/embedded_relationship.rb +8 -0
  51. data/lib/neo4j-embedded/embedded_relationship.rb~ +62 -0
  52. data/lib/neo4j-embedded/embedded_session.rb +20 -12
  53. data/lib/neo4j-embedded/embedded_session.rb~ +145 -0
  54. data/lib/neo4j-server/cypher_relationship.rb +6 -11
  55. data/lib/neo4j-server/cypher_response.rb +43 -5
  56. data/lib/neo4j-server/cypher_response.rb~ +155 -0
  57. data/lib/neo4j-server/cypher_session.rb +54 -19
  58. data/lib/neo4j/label.rb +7 -51
  59. data/lib/neo4j/node.rb +21 -19
  60. data/lib/neo4j/relationship.rb +7 -7
  61. data/lib/neo4j/session.rb +70 -28
  62. data/lib/neo4j/session.rb~ +202 -0
  63. data/neo4j-core.gemspec +0 -1
  64. metadata +49 -16
@@ -25,8 +25,7 @@ module Neo4j::Server
25
25
  def load_resource
26
26
  id = neo_id
27
27
  unless @resource_data
28
- r = @session._query_internal{ rel(id) }
29
- @resource_data = r.first_data
28
+ @resource_data = @session._query_or_fail("START n=relationship(#{id}) RETURN n", true) # r.first_data
30
29
  end
31
30
  end
32
31
 
@@ -44,21 +43,17 @@ module Neo4j::Server
44
43
 
45
44
  def get_property(key)
46
45
  id = neo_id
47
- r = @session._query_internal{rel(id)[key]}
48
- expect_response_code(r.response, 200)
49
- r.first_data
46
+ @session._query_or_fail("START n=relationship(#{id}) RETURN n.`#{key}`", true)
50
47
  end
51
48
 
52
49
  def set_property(key,value)
53
50
  id = neo_id
54
- r = @session._query_internal{rel(id)[key]=value}
55
- expect_response_code(r.response, 200)
51
+ @session._query_or_fail("START n=relationship(#{id}) SET n.`#{key}` = {value}", false, {value: value})
56
52
  end
57
53
 
58
54
  def remove_property(key)
59
55
  id = neo_id
60
- r = @session._query_internal{rel(id)[key]=:NULL}
61
- expect_response_code(r.response, 200)
56
+ @session._query_or_fail("START n=relationship(#{id}) REMOVE n.`#{key}`")
62
57
  end
63
58
 
64
59
  # (see Neo4j::Relationship#props)
@@ -86,12 +81,12 @@ module Neo4j::Server
86
81
 
87
82
  def del
88
83
  id = neo_id
89
- @session._query_internal{rel(id).del}.raise_unless_response_code(200)
84
+ @session._query("START n=relationship(#{id}) DELETE n").raise_unless_response_code(200)
90
85
  end
91
86
 
92
87
  def exist?
93
88
  id = neo_id
94
- response = @session._query_internal{rel(id)}
89
+ response = @session._query("START n=relationship(#{id}) RETURN n")
95
90
 
96
91
  if (!response.error?)
97
92
  return true
@@ -22,23 +22,61 @@ module Neo4j::Server
22
22
  def_delegator :@response, :data
23
23
  def_delegator :@response, :columns
24
24
 
25
- def initialize(response)
25
+ def initialize(response, map_return_procs, query)
26
26
  @response = response
27
+ @map_return_procs = map_return_procs
28
+ @query = query
27
29
  end
28
30
 
29
- def each()
31
+ def to_s
32
+ @query
33
+ end
34
+
35
+ def inspect
36
+ "Enumerable query: '#{@query}'"
37
+ end
38
+
39
+ def each_no_mapping
30
40
  data.each do |row|
31
41
  hash = {}
32
42
  row.each_with_index do |row, i|
33
- hash[columns[i].to_sym] = row
43
+ key = columns[i].to_sym
44
+ hash[key] = row
34
45
  end
35
46
  yield hash
36
47
  end
37
48
  end
49
+
50
+ def each_multi_column_mapping
51
+ data.each do |row|
52
+ hash = {}
53
+ row.each_with_index do |row, i|
54
+ key = columns[i].to_sym
55
+ proc = @map_return_procs[key]
56
+ hash[key] = proc ? proc.call(row) : row
57
+ end
58
+ yield hash
59
+ end
60
+ end
61
+
62
+ def each_single_column_mapping
63
+ data.each do |row|
64
+ result = @map_return_procs.call(row.first)
65
+ yield result
66
+ end
67
+ end
68
+
69
+ def each(&block)
70
+ case @map_return_procs
71
+ when NilClass then each_no_mapping &block
72
+ when Hash then each_multi_column_mapping &block
73
+ else each_single_column_mapping &block
74
+ end
75
+ end
38
76
  end
39
77
 
40
- def to_hash_enumeration
41
- HashEnumeration.new(self)
78
+ def to_hash_enumeration(map_return_procs={}, cypher='')
79
+ HashEnumeration.new(self, map_return_procs, cypher)
42
80
  end
43
81
 
44
82
  def initialize(response, uncommited = false)
@@ -0,0 +1,155 @@
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, map_return_procs, query)
26
+ @response = response
27
+ @map_return_procs = map_return_procs
28
+ @query = query
29
+ end
30
+
31
+ def to_s
32
+ @query
33
+ end
34
+
35
+ puts "RELOADED"
36
+ def inspect
37
+ #"Enumerable query: '#{@query}' map_return: [#{@map_return_procs.keys.join(', ')}]"
38
+ "Enumerable query: '#{@query}' map_return: [#{@map_return_procs.keys.inspect}]"
39
+ end
40
+
41
+ def each_no_mapping
42
+ data.each do |row|
43
+ hash = {}
44
+ row.each_with_index do |row, i|
45
+ key = columns[i].to_sym
46
+ hash[key] = row
47
+ end
48
+ yield hash
49
+ end
50
+ end
51
+
52
+ def each_multi_column_mapping
53
+ data.each do |row|
54
+ hash = {}
55
+ row.each_with_index do |row, i|
56
+ key = columns[i].to_sym
57
+ proc = @map_return_procs[key]
58
+ hash[key] = proc ? proc.call(row) : row
59
+ end
60
+ yield hash
61
+ end
62
+ end
63
+
64
+ def each_single_column_mapping
65
+ data.each do |row|
66
+ result = @map_return_procs.call(row.first)
67
+ yield result
68
+ end
69
+ end
70
+
71
+ def each(&block)
72
+ case @map_return_procs
73
+ when NilClass then each_no_mapping &block
74
+ when Hash then each_multi_column_mapping &block
75
+ else each_single_column_mapping &block
76
+ end
77
+ end
78
+ end
79
+
80
+ def to_hash_enumeration(map_return_procs={}, cypher='')
81
+ HashEnumeration.new(self, map_return_procs, cypher)
82
+ end
83
+
84
+ def initialize(response, uncommited = false)
85
+ @response = response
86
+ @uncommited = uncommited
87
+ end
88
+
89
+
90
+ def first_data
91
+ if uncommited?
92
+ @data.first['row'].first
93
+ else
94
+ @data[0][0]
95
+ end
96
+ end
97
+
98
+ def error?
99
+ !!@error
100
+ end
101
+
102
+ def uncommited?
103
+ @uncommited
104
+ end
105
+
106
+ def raise_unless_response_code(code)
107
+ raise "Response code #{response.code}, expected #{code} for #{response.request.path}, #{response.body}" unless response.code == code
108
+ end
109
+
110
+ def set_data(data, columns)
111
+ @data = data
112
+ @columns = columns
113
+ self
114
+ end
115
+
116
+ def set_error(error_msg, error_status, error_core)
117
+ @error = true
118
+ @error_msg = error_msg
119
+ @error_status = error_status
120
+ @error_code = error_core
121
+ self
122
+ end
123
+
124
+ def raise_error
125
+ raise "Tried to raise error without an error" unless @error
126
+ raise ResponseError.new(@error_msg, @error_status, @error_code)
127
+ end
128
+
129
+ def self.create_with_no_tx(response)
130
+ case response.code
131
+ when 200
132
+ CypherResponse.new(response).set_data(response['data'], response['columns'])
133
+ when 400
134
+ CypherResponse.new(response).set_error(response['message'], response['exception'], response['fullname'])
135
+ else
136
+ raise "Unknown response code #{response.code} for #{response.request.path.to_s}"
137
+ end
138
+ end
139
+
140
+ def self.create_with_tx(response)
141
+ raise "Unknown response code #{response.code} for #{response.request.path.to_s}" unless response.code == 200
142
+
143
+ first_result = response['results'][0]
144
+ cr = CypherResponse.new(response, true)
145
+
146
+ if (response['errors'].empty?)
147
+ cr.set_data(first_result['data'], first_result['columns'])
148
+ else
149
+ first_error = response['errors'].first
150
+ cr.set_error(first_error['message'], first_error['status'], first_error['code'])
151
+ end
152
+ cr
153
+ end
154
+ end
155
+ end
@@ -15,9 +15,8 @@ module Neo4j::Server
15
15
  # Opens a session to the database
16
16
  # @see Neo4j::Session#open
17
17
  #
18
- # @param url - defaults to 'http://localhost:7474' if not given
19
- # @params - see https://github.com/jnunemaker/httparty/blob/master/lib/httparty.rb for supported
20
- # HTTParty options
18
+ # @param [String] endpoint_url - the url to the neo4j server, defaults to 'http://localhost:7474'
19
+ # @param [Hash] params - see https://github.com/jnunemaker/httparty/blob/master/lib/httparty.rb for supported HTTParty options
21
20
  def self.open(endpoint_url=nil, params = {})
22
21
  endpoint = Neo4jServerEndpoint.new(params)
23
22
  url = endpoint_url || 'http://localhost:7474'
@@ -36,10 +35,19 @@ module Neo4j::Server
36
35
  Neo4j::Session.register(self)
37
36
  initialize_resource(data_url)
38
37
  Neo4j::Session._notify_listeners(:session_available, self)
38
+ @query_builder = Neo4j::Core::QueryBuilder.new
39
39
  end
40
40
 
41
41
  def to_s
42
- "CypherSession #{@resource_url}"
42
+ "#{self.class} url: '#{@resource_url}'"
43
+ end
44
+
45
+ def inspect
46
+ "#{to_s} version: '#{version}'"
47
+ end
48
+
49
+ def version
50
+ resource_data ? resource_data['neo4j_version'] : ''
43
51
  end
44
52
 
45
53
  def initialize_resource(data_url)
@@ -109,7 +117,7 @@ module Neo4j::Server
109
117
 
110
118
  def find_all_nodes(label_name)
111
119
  response = _query_or_fail("MATCH (n:`#{label_name}`) RETURN ID(n)")
112
- search_result_to_enumerable(response)
120
+ search_result_to_enumerable_first_column(response)
113
121
  end
114
122
 
115
123
  def find_nodes(label_name, key, value)
@@ -120,20 +128,20 @@ module Neo4j::Server
120
128
  WHERE n.#{key} = #{value}
121
129
  RETURN ID(n)
122
130
  CYPHER
123
- search_result_to_enumerable(response)
131
+ search_result_to_enumerable_first_column(response)
124
132
  end
125
133
 
126
- def query(*params, &query_dsl)
127
- result = super
134
+ def query(*params)
135
+ query_hash = @query_builder.to_query_hash(params, :id_to_node)
136
+ cypher = @query_builder.to_cypher(query_hash)
137
+
138
+ result = _query(cypher, query_hash[:params])
128
139
  if result.error?
129
140
  raise Neo4j::Session::CypherError.new(result.error_msg, result.error_code, result.error_status)
130
141
  end
131
- result.to_hash_enumeration
132
- end
133
142
 
134
- # TODO remove this function and do not use cypher DSL internally
135
- def _query_internal(*params, &query_dsl)
136
- super_query(*params, &query_dsl)
143
+ map_return_procs = @query_builder.to_map_return_procs(query_hash)
144
+ result.to_hash_enumeration(map_return_procs, cypher)
137
145
  end
138
146
 
139
147
  def _query_or_fail(q, single_row = false, params=nil)
@@ -142,10 +150,6 @@ module Neo4j::Server
142
150
  single_row ? response.first_data : response
143
151
  end
144
152
 
145
- def query_default_return
146
- " RETURN ID(n)"
147
- end
148
-
149
153
  def _query(q, params=nil)
150
154
  curr_tx = Neo4j::Transaction.current
151
155
  if (curr_tx)
@@ -158,9 +162,8 @@ module Neo4j::Server
158
162
  end
159
163
  end
160
164
 
161
- def search_result_to_enumerable(response)
165
+ def search_result_to_enumerable_first_column(response)
162
166
  return [] unless response.data
163
-
164
167
  Enumerator.new do |yielder|
165
168
  response.data.each do |data|
166
169
  yielder << CypherNode.new(self, data[0]).wrapper
@@ -169,7 +172,39 @@ module Neo4j::Server
169
172
  end
170
173
 
171
174
 
175
+ def map_column(key, map, data)
176
+ case map[key]
177
+ when :node
178
+ CypherNode.new(self, data).wrapper
179
+ when :rel, :relationship
180
+ CypherRelationship.new(self, data)
181
+ else
182
+ data
183
+ end
184
+ end
185
+
172
186
 
187
+ def search_result_to_enumerable(response, ret, map)
188
+ return [] unless response.data
189
+
190
+ if (ret.size == 1)
191
+ Enumerator.new do |yielder|
192
+ response.data.each do |data|
193
+ yielder << map_column(key, map, data[0])
194
+ end
195
+ end
173
196
 
197
+ else
198
+ Enumerator.new do |yielder|
199
+ response.data.each do |data|
200
+ hash = {}
201
+ ret.each_with_index do |key, i|
202
+ hash[key] = map_column(key, map, data[i])
203
+ end
204
+ yielder << hash
205
+ end
206
+ end
207
+ end
208
+ end
174
209
  end
175
210
  end
data/lib/neo4j/label.rb CHANGED
@@ -3,6 +3,7 @@ module Neo4j
3
3
  # See Neo4j::Node how to create and delete nodes
4
4
  # @see http://docs.neo4j.org/chunked/milestone/graphdb-neo4j-labels.html
5
5
  class Label
6
+ class InvalidQueryError < StandardError; end
6
7
 
7
8
  # @abstract
8
9
  def name
@@ -61,70 +62,25 @@ module Neo4j
61
62
  class << self
62
63
  include Neo4j::Core::CypherTranslator
63
64
 
65
+ # Returns a label of given name that can be used to specifying constraints
66
+ # @param [Symbol,String] name the name of the label
64
67
  def create(name, session = Neo4j::Session.current)
65
68
  session.create_label(name)
66
69
  end
67
70
 
68
- def query(label_name, query, session = Neo4j::Session.current)
69
- cypher = "MATCH (n:`#{label_name}`)"
70
- cypher += condition_to_cypher(query) if query[:conditions] && !query[:conditions].empty?
71
- cypher += session.query_default_return
72
- cypher += order_to_cypher(query) if query[:order]
73
-
74
- response = session._query_or_fail(cypher)
75
- session.search_result_to_enumerable(response) # TODO make it work in Embedded and refactor
76
- end
77
-
78
71
 
72
+ # @return [Enumerable<Neo4j::Node>] all nodes having given label. Nodes can be wrapped in your own model ruby classes.
79
73
  def find_all_nodes(label_name, session = Neo4j::Session.current)
80
74
  session.find_all_nodes(label_name)
81
75
  end
82
76
 
77
+ # @return [Enumerable<Neo4j::Node>] all nodes having given label and properties. Nodes can be wrapped in your own model ruby classes.
83
78
  def find_nodes(label_name, key, value, session = Neo4j::Session.current)
84
79
  session.find_nodes(label_name, key, value)
85
80
  end
86
81
 
87
- private
88
-
89
- def condition_to_cypher(query)
90
- conditions = query[:conditions]
91
- " WHERE " + conditions.keys.map do |k|
92
- value = conditions[k]
93
- if value.is_a? Regexp
94
- pattern = (value.casefold? ? "(?i)" : "") + value.source
95
- "n.#{k}=~#{escape_value(pattern.gsub(/\\/, '\\\\\\'))}"
96
- else
97
- "n.#{k}=#{escape_value(conditions[k])}"
98
- end
99
- end.join(" AND ")
100
- end
101
-
102
- def order_to_cypher(query)
103
- cypher = " ORDER BY "
104
- order = query[:order]
105
-
106
- handleHash = Proc.new do |hash|
107
- if (hash.is_a?(Hash))
108
- k, v = hash.first
109
- raise "only :asc or :desc allowed in order, got #{query.inspect}" unless [:asc, :desc].include?(v)
110
- v.to_sym == :asc ? "n.`#{k}`" : "n.`#{k}` DESC"
111
- else
112
- "n.`#{hash}`" unless hash.is_a?(Hash)
113
- end
114
- end
115
-
116
- case order
117
- when Array
118
- cypher += order.map(&handleHash).join(', ')
119
- when Hash
120
- cypher += handleHash.call(order)
121
- else
122
- cypher += "n.`#{order}`"
123
- end
124
-
125
- cypher
126
- end
127
82
  end
83
+
128
84
  end
129
85
 
130
- end
86
+ end