neo4j-core 3.0.0.alpha.16 → 3.0.0.alpha.17

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -37
  3. data/lib/mydb/index/lucene-store.db +0 -0
  4. data/lib/mydb/index/lucene.log.active +0 -0
  5. data/lib/mydb/index/{lucene.log.1 → lucene.log.v0} +0 -0
  6. data/lib/mydb/messages.log +359 -0
  7. data/lib/mydb/neostore +0 -0
  8. data/lib/mydb/neostore.id +0 -0
  9. data/lib/mydb/neostore.labeltokenstore.db +0 -0
  10. data/lib/mydb/neostore.labeltokenstore.db.id +0 -0
  11. data/lib/mydb/neostore.labeltokenstore.db.names +0 -0
  12. data/lib/mydb/neostore.labeltokenstore.db.names.id +0 -0
  13. data/lib/mydb/neostore.nodestore.db +0 -0
  14. data/lib/mydb/neostore.nodestore.db.id +0 -0
  15. data/lib/mydb/neostore.nodestore.db.labels +0 -0
  16. data/lib/mydb/neostore.nodestore.db.labels.id +0 -0
  17. data/lib/mydb/neostore.propertystore.db +0 -0
  18. data/lib/mydb/neostore.propertystore.db.arrays +0 -0
  19. data/lib/mydb/neostore.propertystore.db.arrays.id +0 -0
  20. data/lib/mydb/neostore.propertystore.db.id +0 -0
  21. data/lib/mydb/neostore.propertystore.db.index +0 -0
  22. data/lib/mydb/neostore.propertystore.db.index.id +0 -0
  23. data/lib/mydb/neostore.propertystore.db.index.keys +0 -0
  24. data/lib/mydb/neostore.propertystore.db.index.keys.id +0 -0
  25. data/lib/mydb/neostore.propertystore.db.strings +0 -0
  26. data/lib/mydb/neostore.propertystore.db.strings.id +0 -0
  27. data/lib/mydb/neostore.relationshipgroupstore.db +0 -0
  28. data/lib/mydb/neostore.relationshipgroupstore.db.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 +1 -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.active +0 -0
  38. data/lib/mydb/nioneo_logical.log.v0 +0 -0
  39. data/lib/mydb/schema/label/lucene/segments.gen +0 -0
  40. data/lib/mydb/schema/label/lucene/segments_1 +0 -0
  41. data/lib/mydb/tm_tx_log.1 +0 -0
  42. data/lib/neo4j-core.rb +1 -1
  43. data/lib/neo4j-core/helpers.rb +1 -1
  44. data/lib/neo4j-core/query.rb +298 -0
  45. data/lib/neo4j-core/query_clauses.rb +476 -0
  46. data/lib/neo4j-core/version.rb +1 -1
  47. data/lib/neo4j-core/version.rb~ +5 -0
  48. data/lib/neo4j-embedded/cypher_response.rb +9 -45
  49. data/lib/neo4j-embedded/embedded_session.rb +15 -17
  50. data/lib/neo4j-server/cypher_node.rb +16 -5
  51. data/lib/neo4j-server/cypher_relationship.rb +24 -6
  52. data/lib/neo4j-server/cypher_response.rb +35 -35
  53. data/lib/neo4j-server/cypher_session.rb +8 -12
  54. data/lib/neo4j/node.rb +4 -4
  55. data/lib/neo4j/session.rb +11 -63
  56. data/lib/neo4j/tasks/neo4j_server.rb +31 -0
  57. metadata +9 -7
  58. data/lib/mydb/lock +0 -0
  59. data/lib/mydb/nioneo_logical.log.1 +0 -0
  60. data/lib/mydb/schema/label/lucene/write.lock +0 -0
  61. data/lib/neo4j-core/query_builder.rb +0 -200
data/lib/mydb/neostore CHANGED
Binary file
data/lib/mydb/neostore.id CHANGED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ RelationshipTypeStore v0.A.3
Binary file
Binary file
Binary file
Binary file
data/lib/mydb/tm_tx_log.1 CHANGED
Binary file
data/lib/neo4j-core.rb CHANGED
@@ -5,7 +5,7 @@ require 'neo4j/property_validator'
5
5
  require 'neo4j/property_container'
6
6
  require 'neo4j-core/helpers'
7
7
  require 'neo4j-core/cypher_translator'
8
- require 'neo4j-core/query_builder'
8
+ require 'neo4j-core/query'
9
9
 
10
10
  require 'neo4j/entity_equality'
11
11
  require 'neo4j/node'
@@ -3,7 +3,7 @@ module Neo4j::Core
3
3
  module ArgumentHelper
4
4
 
5
5
  def self.session(args)
6
- args.last.kind_of?(Neo4j::Session) ? args.pop : Neo4j::Session.current
6
+ args.last.kind_of?(Neo4j::Session) ? args.pop : Neo4j::Session.current!
7
7
  end
8
8
  end
9
9
 
@@ -0,0 +1,298 @@
1
+ require 'neo4j-core/query_clauses'
2
+
3
+ module Neo4j::Core
4
+ # Allows for generation of cypher queries via ruby method calls (inspired by ActiveRecord / arel syntax)
5
+ #
6
+ # Can be used to express cypher queries in ruby nicely, or to more easily generate queries programatically.
7
+ #
8
+ # Also, queries can be passed around an application to progressively build a query across different concerns
9
+ #
10
+ # See also the following link for full cypher language documentation:
11
+ # http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html
12
+ class Query
13
+ include Neo4j::Core::QueryClauses
14
+
15
+ def initialize(options = {})
16
+ @session = options[:session] || Neo4j::Session.current
17
+
18
+ @options = options
19
+ @clauses = []
20
+ @params = {}
21
+ end
22
+
23
+ # @method start *args
24
+ # START clause
25
+ # @return [Query]
26
+
27
+ # @method match *args
28
+ # MATCH clause
29
+ # @return [Query]
30
+
31
+ # @method optional_match *args
32
+ # OPTIONAL MATCH clause
33
+ # @return [Query]
34
+
35
+ # @method using *args
36
+ # USING clause
37
+ # @return [Query]
38
+
39
+ # @method where *args
40
+ # WHERE clause
41
+ # @return [Query]
42
+
43
+ # @method with *args
44
+ # WITH clause
45
+ # @return [Query]
46
+
47
+ # @method order *args
48
+ # ORDER BY clause
49
+ # @return [Query]
50
+
51
+ # @method limit *args
52
+ # LIMIT clause
53
+ # @return [Query]
54
+
55
+ # @method skip *args
56
+ # SKIP clause
57
+ # @return [Query]
58
+
59
+ # @method set *args
60
+ # SET clause
61
+ # @return [Query]
62
+
63
+ # @method remove *args
64
+ # REMOVE clause
65
+ # @return [Query]
66
+
67
+ # @method unwind *args
68
+ # UNWIND clause
69
+ # @return [Query]
70
+
71
+ # @method return *args
72
+ # RETURN clause
73
+ # @return [Query]
74
+
75
+ # @method create *args
76
+ # CREATE clause
77
+ # @return [Query]
78
+
79
+ # @method create_unique *args
80
+ # CREATE UNIQUE clause
81
+ # @return [Query]
82
+
83
+ # @method merge *args
84
+ # MERGE clause
85
+ # @return [Query]
86
+
87
+ # @method on_create_set *args
88
+ # ON CREATE SET clause
89
+ # @return [Query]
90
+
91
+ # @method on_match_set *args
92
+ # ON MATCH SET clause
93
+ # @return [Query]
94
+
95
+ # @method delete *args
96
+ # DELETE clause
97
+ # @return [Query]
98
+
99
+ METHODS = %w[with start match optional_match using where set create create_unique merge on_create_set on_match_set remove unwind delete return order skip limit]
100
+
101
+ CLAUSES = METHODS.map {|method| const_get(method.split('_').map {|c| c.capitalize }.join + 'Clause') }
102
+
103
+ METHODS.each_with_index do |clause, i|
104
+ clause_class = CLAUSES[i]
105
+
106
+ module_eval(%Q{
107
+ def #{clause}(*args)
108
+ build_deeper_query(#{clause_class}, args)
109
+ end}, __FILE__, __LINE__)
110
+ end
111
+
112
+ alias_method :offset, :skip
113
+ alias_method :order_by, :order
114
+
115
+ # Works the same as the #set method, but when given a nested array it will set properties rather than setting entire objects
116
+ # @example
117
+ # # Creates a query representing the cypher: MATCH (n:Person) SET n.age = 19
118
+ # Query.new.match(n: :Person).set_props(n: {age: 19})
119
+ def set_props(*args)
120
+ build_deeper_query(SetClause, args, set_props: true)
121
+ end
122
+
123
+ # Allows what's been built of the query so far to be frozen and the rest built anew. Can be called multiple times in a string of method calls
124
+ # @example
125
+ # # Creates a query representing the cypher: MATCH (q:Person), r:Car MATCH (p: Person)-->q
126
+ # Query.new.match(q: Person).match('r:Car').break.match('(p: Person)-->q')
127
+ def break
128
+ build_deeper_query(nil)
129
+ end
130
+
131
+ # Allows for the specification of values for params specified in query
132
+ # @example
133
+ # # Creates a query representing the cypher: MATCH (q: Person {id: {id}})
134
+ # # Calls to params don't affect the cypher query generated, but the params will be
135
+ # # Passed down when the query is made
136
+ # Query.new.match('(q: Person {id: {id}})').params(id: 12)
137
+ #
138
+ def params(args)
139
+ @params = @params.merge(args)
140
+
141
+ self
142
+ end
143
+
144
+ def response
145
+ response = @session._query(self.to_cypher, @params)
146
+ if !response.respond_to?(:error?) || !response.error?
147
+ response
148
+ else
149
+ response.raise_cypher_error
150
+ end
151
+ end
152
+
153
+ include Enumerable
154
+
155
+ def each
156
+ response = self.response
157
+ if response.is_a?(Neo4j::Server::CypherResponse)
158
+ self.response.to_node_enumeration
159
+ else
160
+ Neo4j::Embedded::ResultWrapper.new(response, self.to_cypher)
161
+ end.each {|object| yield object }
162
+ end
163
+
164
+ # @method to_a
165
+ # Class is Enumerable. Each yield is a Hash with the key matching the variable returned and the value being the value for that key from the response
166
+ # @return [Array]
167
+ # @raise [Neo4j::Server::CypherResponse::ResponseError] Raises errors from neo4j server
168
+
169
+
170
+ # Executes a query without returning the result
171
+ # @return [Boolean] true if successful
172
+ # @raise [Neo4j::Server::CypherResponse::ResponseError] Raises errors from neo4j server
173
+ def exec
174
+ self.response
175
+
176
+ true
177
+ end
178
+
179
+ # Return the specified columns as an array.
180
+ # If one column is specified, a one-dimensional array is returned with the values of that column
181
+ # If two columns are specified, a n-dimensional array is returned with the values of those columns
182
+ #
183
+ # @example
184
+ # Query.new.match(n: :Person).return(p: :name}.pluck(p: :name) # => Array of names
185
+ # @example
186
+ # Query.new.match(n: :Person).return(p: :name}.pluck('p, p.name') # => Array of [node, name] pairs
187
+ #
188
+ def pluck(*columns)
189
+ query = self.dup
190
+ query.remove_clause_class(ReturnClause)
191
+
192
+ columns = columns.map do |column_definition|
193
+ if column_definition.is_a?(Hash)
194
+ column_definition.map {|k, v| "#{k}.#{v}" }
195
+ else
196
+ column_definition
197
+ end
198
+ end.flatten.map(&:to_sym)
199
+
200
+ query = query.return(columns)
201
+
202
+ case columns.size
203
+ when 0
204
+ raise ArgumentError, 'No columns specified for Query#pluck'
205
+ when 1
206
+ column = columns[0]
207
+ query.map {|row| row[column] }
208
+ else
209
+ query.map do |row|
210
+ columns.map do |column|
211
+ row[column]
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+
218
+ # Returns a CYPHER query string from the object query representation
219
+ # @example
220
+ # Query.new.match(p: :Person).where(p: {age: 30}) # => "MATCH (p:Person) WHERE p.age = 30
221
+ #
222
+ # @return [String] Resulting cypher query string
223
+ def to_cypher
224
+ cypher_string = partitioned_clauses.map do |clauses|
225
+ clauses_by_class = clauses.group_by(&:class)
226
+
227
+ cypher_parts = CLAUSES.map do |clause_class|
228
+ clauses = clauses_by_class[clause_class]
229
+
230
+ clause_class.to_cypher(clauses) if clauses
231
+ end
232
+
233
+ cypher_string = cypher_parts.compact.join(' ')
234
+ cypher_string.strip
235
+ end.join ' '
236
+
237
+ cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser]
238
+ cypher_string.strip
239
+ end
240
+
241
+ # Returns a CYPHER query specifying the union of the callee object's query and the argument's query
242
+ #
243
+ # @example
244
+ # # Generates cypher: MATCH (n:Person) UNION MATCH (o:Person) WHERE o.age = 10
245
+ # q = Neo4j::Core::Query.new.match(o: :Person).where(o: {age: 10})
246
+ # result = Neo4j::Core::Query.new.match(n: :Person).union_cypher(q)
247
+ #
248
+ # @param other_query [Query] Second half of UNION
249
+ # @param options [Hash] Specify {all: true} to use UNION ALL
250
+ # @return [String] Resulting UNION cypher query string
251
+ def union_cypher(other_query, options = {})
252
+ "#{self.to_cypher} UNION#{options[:all] ? ' ALL' : ''} #{other_query.to_cypher}"
253
+ end
254
+
255
+ protected
256
+
257
+ def add_clauses(clauses)
258
+ @clauses += clauses
259
+ end
260
+
261
+ def remove_clause_class(clause_class)
262
+ @clauses = @clauses.reject do |clause|
263
+ clause.is_a?(clause_class)
264
+ end
265
+ end
266
+ private
267
+
268
+ def build_deeper_query(clause_class, args = {}, options = {})
269
+ self.dup.tap do |new_query|
270
+ new_query.add_clauses [nil] if [nil, WithClause].include?(clause_class)
271
+ new_query.add_clauses clause_class.from_args(args, options) if clause_class
272
+ end
273
+ end
274
+
275
+ def break_deeper_query
276
+ self.dup.tap do |new_query|
277
+ new_query.add_clauses [nil]
278
+ end
279
+ end
280
+
281
+ def partitioned_clauses
282
+ partitioning = [[]]
283
+
284
+ @clauses.each do |clause|
285
+ if clause.nil? && partitioning.last != []
286
+ partitioning << []
287
+ else
288
+ partitioning.last << clause
289
+ end
290
+ end
291
+
292
+ partitioning
293
+ end
294
+ end
295
+ end
296
+
297
+
298
+
@@ -0,0 +1,476 @@
1
+ module Neo4j::Core
2
+ module QueryClauses
3
+
4
+ class ArgError < StandardError
5
+ attr_reader :arg_part
6
+ def initialize(arg_part = nil)
7
+ super
8
+ @arg_part = arg_part
9
+ end
10
+ end
11
+
12
+
13
+ class Clause
14
+ include CypherTranslator
15
+
16
+ def initialize(arg, options = {})
17
+ @arg = arg
18
+ @options = options
19
+ end
20
+
21
+ def value
22
+ if @arg.is_a?(String)
23
+ self.from_string @arg
24
+
25
+ elsif @arg.is_a?(Symbol) && self.respond_to?(:from_symbol)
26
+ self.from_symbol @arg
27
+
28
+ elsif @arg.is_a?(Integer) && self.respond_to?(:from_integer)
29
+ self.from_integer @arg
30
+
31
+ elsif @arg.is_a?(Hash)
32
+ if self.respond_to?(:from_hash)
33
+ self.from_hash @arg
34
+ elsif self.respond_to?(:from_key_and_value)
35
+ @arg.map do |key, value|
36
+ self.from_key_and_value key, value
37
+ end
38
+ else
39
+ raise ArgError.new
40
+ end
41
+
42
+ else
43
+ raise ArgError.new
44
+ end
45
+
46
+ rescue ArgError => arg_error
47
+ message = "Invalid argument for #{self.class.keyword}. Full arguments: #{@arg.inspect}"
48
+ message += " | Invalid part: #{arg_error.arg_part.inspect}" if arg_error.arg_part
49
+
50
+ raise ArgumentError, message
51
+ end
52
+
53
+ def from_string(value)
54
+ value
55
+ end
56
+
57
+ def node_from_key_and_value(key, value, options = {})
58
+ prefer = options[:prefer] || :var
59
+
60
+ var, label_string, attributes_string = nil
61
+
62
+ case value
63
+ when String, Symbol
64
+ var = key
65
+ label_string = value
66
+ when Hash
67
+ if !value.values.any? {|v| v.is_a?(Hash) }
68
+ case prefer
69
+ when :var
70
+ var = key
71
+ when :label
72
+ label_string = key
73
+ end
74
+ else
75
+ var = key
76
+ end
77
+
78
+ if value.size == 1 && value.values.first.is_a?(Hash)
79
+ label_string, attributes = value.first
80
+ attributes_string = attributes_string(attributes)
81
+ else
82
+ attributes_string = attributes_string(value)
83
+ end
84
+ when Class
85
+ var = key
86
+ label_string = defined?(value::CYPHER_LABEL) ? value::CYPHER_LABEL : value.name
87
+ else
88
+ raise ArgError.new(value)
89
+ end
90
+
91
+ "(#{var}#{format_label(label_string)}#{attributes_string})"
92
+ end
93
+
94
+ class << self
95
+ attr_reader :keyword
96
+
97
+ def from_args(args, options = {})
98
+ args.flatten.map do |arg|
99
+ if !arg.respond_to?(:empty?) || !arg.empty?
100
+ self.new(arg, options)
101
+ end
102
+ end.compact
103
+ end
104
+
105
+ def to_cypher(clauses)
106
+ "#{@keyword} #{clause_string(clauses)}"
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def format_label(label_string)
113
+ label_string = label_string.to_s.strip
114
+ if !label_string.empty? && label_string[0] != ':'
115
+ label_string = "`#{label_string}`" unless label_string.match(' ')
116
+ label_string = ":#{label_string}"
117
+ end
118
+ label_string
119
+ end
120
+
121
+ def attributes_string(attributes)
122
+ attributes_string = attributes.map do |key, value|
123
+ v = value.to_s.match(/^{.+}$/) ? value : value.inspect
124
+ "#{key}: #{v}"
125
+ end.join(', ')
126
+
127
+ " {#{attributes_string}}"
128
+ end
129
+ end
130
+
131
+ class StartClause < Clause
132
+ @keyword = 'START'
133
+
134
+ def from_symbol(value)
135
+ from_string(value.to_s)
136
+ end
137
+
138
+ def from_key_and_value(key, value)
139
+ case value
140
+ when String, Symbol
141
+ "#{key} = #{value}"
142
+ else
143
+ raise ArgError.new(value)
144
+ end
145
+ end
146
+
147
+ class << self
148
+ def clause_string(clauses)
149
+ clauses.map(&:value).join(', ')
150
+ end
151
+ end
152
+ end
153
+
154
+ class WhereClause < Clause
155
+ @keyword = 'WHERE'
156
+
157
+ def from_key_and_value(key, value)
158
+ case value
159
+ when Hash
160
+ value.map do |k, v|
161
+ if k.to_sym == :neo_id
162
+ "ID(#{key}) = #{v}"
163
+ else
164
+ key.to_s + '.' + from_key_and_value(k, v)
165
+ end
166
+ end.join(' AND ')
167
+ when Array
168
+ "#{key} IN [#{value.join(', ')}]"
169
+ when NilClass
170
+ "#{key} IS NULL"
171
+ when Regexp
172
+ pattern = (value.casefold? ? "(?i)" : "") + value.source
173
+ "#{key} =~ #{escape_value(pattern.gsub(/\\/, '\\\\\\'))}"
174
+ else
175
+ "#{key} = #{value.inspect}"
176
+ end
177
+ end
178
+
179
+ class << self
180
+ def clause_string(clauses)
181
+ clauses.map(&:value).join(' AND ')
182
+ end
183
+ end
184
+ end
185
+
186
+
187
+ class MatchClause < Clause
188
+ @keyword = 'MATCH'
189
+
190
+ def from_symbol(value)
191
+ from_string(value.to_s)
192
+ end
193
+
194
+ def from_key_and_value(key, value)
195
+ self.node_from_key_and_value(key, value)
196
+ end
197
+
198
+ class << self
199
+ def clause_string(clauses)
200
+ clauses.map(&:value).join(', ')
201
+ end
202
+ end
203
+ end
204
+
205
+ class OptionalMatchClause < MatchClause
206
+ @keyword = 'OPTIONAL MATCH'
207
+ end
208
+
209
+ class WithClause < Clause
210
+ @keyword = 'WITH'
211
+
212
+ def from_symbol(value)
213
+ from_string(value.to_s)
214
+ end
215
+
216
+ def from_key_and_value(key, value)
217
+ "#{value} AS #{key}"
218
+ end
219
+
220
+ class << self
221
+ def clause_string(clauses)
222
+ clauses.map(&:value).join(', ')
223
+ end
224
+ end
225
+ end
226
+
227
+ class UsingClause < Clause
228
+ @keyword = 'USING'
229
+
230
+ class << self
231
+ def clause_string(clauses)
232
+ clauses.map(&:value).join(" #{@keyword} ")
233
+ end
234
+ end
235
+ end
236
+
237
+ class CreateClause < Clause
238
+ @keyword = 'CREATE'
239
+
240
+ def from_string(value)
241
+ value
242
+ end
243
+
244
+ def from_symbol(value)
245
+ "(:#{value})"
246
+ end
247
+
248
+ def from_hash(hash)
249
+ if hash.values.any? {|value| value.is_a?(Hash) }
250
+ hash.map do |key, value|
251
+ from_key_and_value(key, value)
252
+ end
253
+ else
254
+ "(#{attributes_string(hash)})"
255
+ end
256
+ end
257
+
258
+ def from_key_and_value(key, value)
259
+ self.node_from_key_and_value(key, value, prefer: :label)
260
+ end
261
+
262
+ class << self
263
+ def clause_string(clauses)
264
+ clauses.map(&:value).join(', ')
265
+ end
266
+ end
267
+ end
268
+
269
+ class CreateUniqueClause < CreateClause
270
+ @keyword = 'CREATE UNIQUE'
271
+ end
272
+
273
+ class MergeClause < CreateClause
274
+ @keyword = 'MERGE'
275
+ end
276
+
277
+ class DeleteClause < Clause
278
+ @keyword = 'DELETE'
279
+
280
+ def from_symbol(value)
281
+ from_string(value.to_s)
282
+ end
283
+
284
+ class << self
285
+ def clause_string(clauses)
286
+ clauses.map(&:value).join(', ')
287
+ end
288
+ end
289
+ end
290
+
291
+ class OrderClause < Clause
292
+ @keyword = 'ORDER BY'
293
+
294
+ def from_symbol(value)
295
+ from_string(value.to_s)
296
+ end
297
+
298
+ def from_key_and_value(key, value)
299
+ case value
300
+ when String, Symbol
301
+ "#{key}.#{value}"
302
+ when Array
303
+ value.map do |v|
304
+ if v.is_a?(Hash)
305
+ from_key_and_value(key, v)
306
+ else
307
+ "#{key}.#{v}"
308
+ end
309
+ end
310
+ when Hash
311
+ value.map do |k, v|
312
+ "#{key}.#{k} #{v.upcase}"
313
+ end
314
+ end
315
+ end
316
+
317
+ class << self
318
+ def clause_string(clauses)
319
+ clauses.map(&:value).join(', ')
320
+ end
321
+ end
322
+ end
323
+
324
+ class LimitClause < Clause
325
+ @keyword = 'LIMIT'
326
+
327
+ def from_string(value)
328
+ value.to_i
329
+ end
330
+
331
+ def from_integer(value)
332
+ value
333
+ end
334
+
335
+ class << self
336
+ def clause_string(clauses)
337
+ clauses.last.value
338
+ end
339
+ end
340
+ end
341
+
342
+ class SkipClause < Clause
343
+ @keyword = 'SKIP'
344
+
345
+ def from_string(value)
346
+ value.to_i
347
+ end
348
+
349
+ def from_integer(value)
350
+ value
351
+ end
352
+
353
+ class << self
354
+ def clause_string(clauses)
355
+ clauses.last.value
356
+ end
357
+ end
358
+ end
359
+
360
+ class SetClause < Clause
361
+ @keyword = 'SET'
362
+
363
+ def from_key_and_value(key, value)
364
+ case value
365
+ when String, Symbol
366
+ "#{key} = #{value}"
367
+ when Hash
368
+ if @options[:set_props]
369
+ value.map do |k, v|
370
+ "#{key}.#{k} = #{v.inspect}"
371
+ end
372
+ else
373
+ attribute_string = value.map {|k, v| "#{k}: #{v.inspect}" }.join(', ')
374
+ "#{key} = {#{attribute_string}}"
375
+ end
376
+ else
377
+ raise ArgError.new(value)
378
+ end
379
+ end
380
+
381
+ class << self
382
+ def clause_string(clauses)
383
+ clauses.map(&:value).join(', ')
384
+ end
385
+ end
386
+ end
387
+
388
+ class OnCreateSetClause < SetClause
389
+ @keyword = 'ON CREATE SET'
390
+
391
+ def initialize(*args)
392
+ super
393
+ @options[:set_props] = true
394
+ end
395
+ end
396
+
397
+ class OnMatchSetClause < OnCreateSetClause
398
+ @keyword = 'ON MATCH SET'
399
+ end
400
+
401
+ class RemoveClause < Clause
402
+ @keyword = 'REMOVE'
403
+
404
+ def from_key_and_value(key, value)
405
+ case value
406
+ when /^:/
407
+ "#{key}:#{value[1..-1]}"
408
+ when String
409
+ "#{key}.#{value}"
410
+ when Symbol
411
+ "#{key}:#{value}"
412
+ else
413
+ raise ArgError.new(value)
414
+ end
415
+
416
+ end
417
+
418
+ class << self
419
+ def clause_string(clauses)
420
+ clauses.map(&:value).join(', ')
421
+ end
422
+ end
423
+ end
424
+
425
+ class UnwindClause < Clause
426
+ @keyword = 'UNWIND'
427
+
428
+ def from_key_and_value(key, value)
429
+ case value
430
+ when String, Symbol
431
+ "#{value} AS #{key}"
432
+ when Array
433
+ "#{value.inspect} AS #{key}"
434
+ else
435
+ raise ArgError.new(value)
436
+ end
437
+ end
438
+
439
+ class << self
440
+ def clause_string(clauses)
441
+ clauses.map(&:value).join(' UNWIND ')
442
+ end
443
+ end
444
+ end
445
+
446
+ class ReturnClause < Clause
447
+ @keyword = 'RETURN'
448
+
449
+ def from_symbol(value)
450
+ from_string(value.to_s)
451
+ end
452
+
453
+ def from_key_and_value(key, value)
454
+ case value
455
+ when Array
456
+ value.map do |v|
457
+ from_key_and_value(key, v)
458
+ end.join(', ')
459
+ when String, Symbol
460
+ "#{key}.#{value}"
461
+ else
462
+ raise ArgError.new(value)
463
+ end
464
+ end
465
+
466
+ class << self
467
+ def clause_string(clauses)
468
+ clauses.map(&:value).join(', ')
469
+ end
470
+ end
471
+ end
472
+
473
+
474
+ end
475
+ end
476
+