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

Sign up to get free protection for your applications and to get access to all the features.
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
+