neo4j-core 3.1.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -12
  3. data/README.md +7 -7
  4. data/lib/neo4j-core.rb +3 -2
  5. data/lib/neo4j-core/active_entity.rb +8 -10
  6. data/lib/neo4j-core/cypher_translator.rb +61 -59
  7. data/lib/neo4j-core/hash_with_indifferent_access.rb +31 -22
  8. data/lib/neo4j-core/helpers.rb +15 -17
  9. data/lib/neo4j-core/label.rb +7 -6
  10. data/lib/neo4j-core/query.rb +271 -268
  11. data/lib/neo4j-core/query_clauses.rb +371 -355
  12. data/lib/neo4j-core/query_find_in_batches.rb +26 -26
  13. data/lib/neo4j-core/version.rb +1 -1
  14. data/lib/neo4j-embedded.rb +2 -2
  15. data/lib/neo4j-embedded/cypher_response.rb +40 -41
  16. data/lib/neo4j-embedded/embedded_database.rb +21 -22
  17. data/lib/neo4j-embedded/embedded_ha_session.rb +13 -11
  18. data/lib/neo4j-embedded/embedded_impermanent_session.rb +9 -8
  19. data/lib/neo4j-embedded/embedded_label.rb +64 -70
  20. data/lib/neo4j-embedded/embedded_node.rb +68 -73
  21. data/lib/neo4j-embedded/embedded_relationship.rb +6 -13
  22. data/lib/neo4j-embedded/embedded_session.rb +128 -132
  23. data/lib/neo4j-embedded/embedded_transaction.rb +34 -33
  24. data/lib/neo4j-embedded/property.rb +84 -77
  25. data/lib/neo4j-embedded/to_java.rb +24 -23
  26. data/lib/neo4j-server.rb +1 -1
  27. data/lib/neo4j-server/cypher_authentication.rb +105 -103
  28. data/lib/neo4j-server/cypher_label.rb +25 -23
  29. data/lib/neo4j-server/cypher_node.rb +180 -177
  30. data/lib/neo4j-server/cypher_node_uncommited.rb +11 -9
  31. data/lib/neo4j-server/cypher_relationship.rb +101 -102
  32. data/lib/neo4j-server/cypher_response.rb +171 -170
  33. data/lib/neo4j-server/cypher_session.rb +209 -205
  34. data/lib/neo4j-server/cypher_transaction.rb +66 -48
  35. data/lib/neo4j-server/resource.rb +17 -22
  36. data/lib/neo4j/entity_equality.rb +3 -4
  37. data/lib/neo4j/label.rb +13 -16
  38. data/lib/neo4j/node.rb +30 -34
  39. data/lib/neo4j/property_container.rb +3 -3
  40. data/lib/neo4j/property_validator.rb +4 -5
  41. data/lib/neo4j/relationship.rb +17 -22
  42. data/lib/neo4j/session.rb +19 -21
  43. data/lib/neo4j/tasks/config_server.rb +2 -3
  44. data/lib/neo4j/tasks/neo4j_server.rake +82 -74
  45. data/lib/neo4j/transaction.rb +23 -22
  46. data/neo4j-core.gemspec +21 -16
  47. metadata +72 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bb8493440e6f8164e499189ebc58e95998b5bad1
4
- data.tar.gz: 02678788be0642f10aa6781bdcb82fd11ab5fe97
3
+ metadata.gz: 6e441fd4d968dfd3b6caf201fc60ce19c2698d29
4
+ data.tar.gz: d4e88b57b2ecd1a342eaa83c4c05faa1c4d3bdbe
5
5
  SHA512:
6
- metadata.gz: 82882d368b6ff790c061653f0cb68a2af37e328ef2ea948c507671108976d0a7ee60fac78653b6e254ca4a5d00c6cedf1bcda3bce15e038b446665042363fe5d
7
- data.tar.gz: 76be0d527c6d6710eb3753fd370b747341efd3f099654447d9297a18d29bb01c330764183d206ef48fa59aab78ecdfa600cdb55879e0ad106a177e0188970035
6
+ metadata.gz: 65292755970b43e9c31b886c1e3e52455094f7de61e44d483f88c15d9f4ec713a9ceeff2302b0918c02264ee957e08210850aa29ed3651905bcd5149165916dc
7
+ data.tar.gz: b3ef3555c5b6de4ef7527664b2133ecf0ca73de25245b41bf2f92817800e7403010155ef48d7be9f583d8380280fbb431016f7f239184a3782110ffc925845e6
data/Gemfile CHANGED
@@ -4,22 +4,17 @@ gemspec
4
4
 
5
5
  gem 'zip'
6
6
 
7
- #gem 'neo4j-advanced', '>= 1.8.1', '< 2.0', :require => false
8
- #gem 'neo4j-enterprise', '>= 1.8.1', '< 2.0', :require => false
9
-
10
- gem 'coveralls', require: false
11
- gem 'simplecov-html', require: false
7
+ # gem 'neo4j-advanced', '>= 1.8.1', '< 2.0', :require => false
8
+ # gem 'neo4j-enterprise', '>= 1.8.1', '< 2.0', :require => false
12
9
 
13
10
  group 'development' do
14
- gem 'yard'
15
- gem 'simplecov'
16
- gem 'pry'
17
- gem 'pry-rescue'
18
- gem 'pry-stack_explorer', platform: :ruby
11
+ gem 'guard-rspec', require: false
19
12
  end
20
13
 
21
14
  group 'test' do
22
- gem "rake", ">= 0.8.7"
23
- gem "rspec", "~> 3.0"
15
+ gem 'coveralls', require: false
16
+ gem 'simplecov-html', require: false
17
+ gem 'rake', '>= 0.8.7'
18
+ gem 'rspec', '~> 3.0'
24
19
  gem 'rspec-its'
25
20
  end
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
- # Neo4j-core v3.0 [![Code Climate](https://codeclimate.com/github/andreasronge/neo4j-core.png)](https://codeclimate.com/github/andreasronge/neo4j-core) [![Build Status](https://travis-ci.org/neo4jrb/neo4j-core.png)](https://travis-ci.org/andreasronge/neo4j-core) [![Coverage Status](https://coveralls.io/repos/neo4jrb/neo4j-core/badge.png?branch=master)](https://coveralls.io/r/neo4jrb/neo4j-core?branch=master)
1
+ # Neo4j-core v3.0 [![Code Climate](https://codeclimate.com/github/neo4jrb/neo4j-core.png)](https://codeclimate.com/github/neo4jrb/neo4j-core) [![Build Status](https://travis-ci.org/neo4jrb/neo4j-core.png)](https://travis-ci.org/neo4jrb/neo4j-core) [![Coverage Status](https://coveralls.io/repos/neo4jrb/neo4j-core/badge.png?branch=master)](https://coveralls.io/r/neo4jrb/neo4j-core?branch=master) [![PullReview stats](https://www.pullreview.com/github/neo4jrb/neo4j-core/badges/master.svg?)](https://www.pullreview.com/github/neo4jrb/neo4j-core/reviews/master)
2
2
 
3
3
  A simple Ruby wrapper around the Neo4j graph database that works with the server and embedded Neo4j API. This gem can be used both from JRuby and normal MRI.
4
4
  It can be used standalone without the neo4j gem.
5
5
 
6
6
  ## Documentation
7
7
 
8
- * [3.0 Documentation](https://github.com/andreasronge/neo4j-core/wiki)
9
- * [2.x Documentation](https://github.com/andreasronge/neo4j-core/tree/v2.x)
8
+ * [3.0 Documentation](https://github.com/neo4jrb/neo4j-core/wiki)
9
+ * [2.x Documentation](https://github.com/neo4jrb/neo4j-core/tree/v2.x)
10
10
 
11
11
 
12
12
  ## Support
@@ -16,20 +16,20 @@ It can be used standalone without the neo4j gem.
16
16
 
17
17
  ## Developers
18
18
 
19
- * [Andreas Ronge](https://github.com/andreasronge)
19
+ * [Andreas Ronge](https://github.com/neo4jrb)
20
20
  * [Brian Underwood](https://github.com/cheerfulstoic)
21
21
  * [Chris Grigg](https://github.com/subvertallchris)
22
22
 
23
23
 
24
24
  ## Contributing
25
25
 
26
- Pull request with high test coverage and good [code climate](https://codeclimate.com/github/andreasronge/neo4j-core) values will be accepted faster.
26
+ Pull request with high test coverage and good [code climate](https://codeclimate.com/github/neo4jrb/neo4j-core) values will be accepted faster.
27
27
  Notice, only JRuby can run all the tests (embedded and server db). To run tests with coverage: `rake coverage`.
28
28
 
29
29
  ## License
30
- * Neo4j.rb - MIT, see the LICENSE file http://github.com/andreasronge/neo4j-core/tree/master/LICENSE.
30
+ * Neo4j.rb - MIT, see the LICENSE file http://github.com/neo4jrb/neo4j-core/tree/master/LICENSE.
31
31
  * Lucene - Apache, see http://lucene.apache.org/java/docs/features.html
32
- * \Neo4j - Dual free software/commercial license, see http://neo4j.org/
32
+ * Neo4j - Dual free software/commercial license, see http://neo4j.org/
33
33
 
34
34
  Notice there are different license for the neo4j-community, neo4j-advanced and neo4j-enterprise jar gems.
35
35
  Only the neo4j-community gem is by default required.
data/lib/neo4j-core.rb CHANGED
@@ -24,7 +24,8 @@ if RUBY_PLATFORM == 'java'
24
24
  require 'neo4j-embedded'
25
25
  else
26
26
  # just for the tests
27
- module Neo4j::Embedded
27
+ module Neo4j
28
+ module Embedded
29
+ end
28
30
  end
29
31
  end
30
-
@@ -1,13 +1,11 @@
1
- module Neo4j::Core
2
-
3
- # A module to make Neo4j::Node and Neo4j::Relationship work better together with neo4j.rb's Neo4j::ActiveNode and Neo4j::ActiveRel
4
- module ActiveEntity
5
- # @return true
6
- def persisted?
7
- true
1
+ module Neo4j
2
+ module Core
3
+ # A module to make Neo4j::Node and Neo4j::Relationship work better together with neo4j.rb's Neo4j::ActiveNode and Neo4j::ActiveRel
4
+ module ActiveEntity
5
+ # @return true
6
+ def persisted?
7
+ true
8
+ end
8
9
  end
9
-
10
10
  end
11
11
  end
12
-
13
-
@@ -1,74 +1,76 @@
1
- module Neo4j::Core
2
- module CypherTranslator
3
- # Cypher Helper
4
- def escape_value(value)
5
- if value.is_a?(String) || value.is_a?(Symbol)
6
- "'#{escape_quotes(sanitize_escape_sequences(value.to_s))}'"
7
- else
8
- value
1
+ module Neo4j
2
+ module Core
3
+ module CypherTranslator
4
+ # Cypher Helper
5
+ def escape_value(value)
6
+ if value.is_a?(String) || value.is_a?(Symbol)
7
+ "'#{escape_quotes(sanitize_escape_sequences(value.to_s))}'"
8
+ else
9
+ value
10
+ end
9
11
  end
10
- end
11
12
 
12
- # Like escape_value but it does not wrap the value in quotes
13
- def create_escape_value(value)
14
- if value.is_a?(String) || value.is_a?(Symbol)
15
- "#{sanitize_escape_sequences(value.to_s)}"
16
- else
17
- value
13
+ # Like escape_value but it does not wrap the value in quotes
14
+ def create_escape_value(value)
15
+ if value.is_a?(String) || value.is_a?(Symbol)
16
+ "#{sanitize_escape_sequences(value.to_s)}"
17
+ else
18
+ value
19
+ end
18
20
  end
19
- end
20
21
 
21
- # Only following escape sequence characters are allowed in Cypher:
22
- #
23
- # \t Tab
24
- # \b Backspace
25
- # \n Newline
26
- # \r Carriage return
27
- # \f Form feed
28
- # \' Single quote
29
- # \" Double quote
30
- # \\ Backslash
31
- #
32
- # From:
33
- # http://docs.neo4j.org/chunked/stable/cypher-expressions.html#_note_on_string_literals
34
- SANITIZE_ESCAPED_REGEXP = /(?<!\\)\\(\\\\)*(?![futbnr'"\\])/
35
- EMPTY_PROPS = ''
22
+ # Only following escape sequence characters are allowed in Cypher:
23
+ #
24
+ # \t Tab
25
+ # \b Backspace
26
+ # \n Newline
27
+ # \r Carriage return
28
+ # \f Form feed
29
+ # \' Single quote
30
+ # \" Double quote
31
+ # \\ Backslash
32
+ #
33
+ # From:
34
+ # http://docs.neo4j.org/chunked/stable/cypher-expressions.html#_note_on_string_literals
35
+ SANITIZE_ESCAPED_REGEXP = /(?<!\\)\\(\\\\)*(?![futbnr'"\\])/
36
+ EMPTY_PROPS = ''
36
37
 
37
- def sanitize_escape_sequences(s)
38
- s.gsub SANITIZE_ESCAPED_REGEXP, EMPTY_PROPS
39
- end
38
+ def sanitize_escape_sequences(s)
39
+ s.gsub SANITIZE_ESCAPED_REGEXP, EMPTY_PROPS
40
+ end
40
41
 
41
- def escape_quotes(s)
42
- s.gsub("'", %q(\\\'))
43
- end
42
+ def escape_quotes(s)
43
+ s.gsub("'", %q(\\\'))
44
+ end
44
45
 
45
- # Cypher Helper
46
- def cypher_prop_list(props)
47
- return nil unless props
48
- props.reject! {|k,v| v.nil? }
49
- { props: props.each { |k, v| props[k] = create_escape_value(v) } }
50
- end
46
+ # Cypher Helper
47
+ def cypher_prop_list(props)
48
+ return nil unless props
49
+ props.reject! { |_, v| v.nil? }
50
+ {props: props.each { |k, v| props[k] = create_escape_value(v) }}
51
+ end
51
52
 
52
- # Stolen from keymaker
53
- # https://github.com/therubymug/keymaker/blob/master/lib/keymaker/parsers/cypher_response_parser.rb
54
- def self.translate_response(response_body, result)
55
- Hashie::Mash.new(Hash[sanitized_column_names(response_body).zip(result)])
56
- end
53
+ # Stolen from keymaker
54
+ # https://github.com/therubymug/keymaker/blob/master/lib/keymaker/parsers/cypher_response_parser.rb
55
+ def self.translate_response(response_body, result)
56
+ Hashie::Mash.new(Hash[sanitized_column_names(response_body).zip(result)])
57
+ end
57
58
 
58
- def self.sanitized_column_names(response_body)
59
- response_body.columns.map { |column| column[/[^\.]+$/] }
60
- end
59
+ def self.sanitized_column_names(response_body)
60
+ response_body.columns.map { |column| column[/[^\.]+$/] }
61
+ end
61
62
 
62
- def cypher_string(labels, props)
63
- "CREATE (n#{label_string(labels)} #{prop_identifier(props)}) RETURN ID(n)"
64
- end
63
+ def cypher_string(labels, props)
64
+ "CREATE (n#{label_string(labels)} #{prop_identifier(props)}) RETURN ID(n)"
65
+ end
65
66
 
66
- def label_string(labels)
67
- labels.empty? ? '' : ":#{labels.map{|k| "`#{k}`"}.join(':')}"
68
- end
67
+ def label_string(labels)
68
+ labels.empty? ? '' : ":#{labels.map { |k| "`#{k}`" }.join(':')}"
69
+ end
69
70
 
70
- def prop_identifier(props)
71
- '{props}' unless props.nil?
71
+ def prop_identifier(props)
72
+ '{props}' unless props.nil?
73
+ end
72
74
  end
73
75
  end
74
76
  end
@@ -3,7 +3,6 @@ module Neo4j
3
3
  # Stolen from http://as.rubyonrails.org/classes/HashWithIndifferentAccess.html
4
4
  # We don't want to depend on active support
5
5
  class HashWithIndifferentAccess < Hash
6
-
7
6
  # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
8
7
  def extractable_options?
9
8
  true
@@ -103,7 +102,7 @@ module Neo4j
103
102
  # hash.values_at("a", "b") # => ["x", "y"]
104
103
  #
105
104
  def values_at(*indices)
106
- indices.collect {|key| self[convert_key(key)]}
105
+ indices.collect { |key| self[convert_key(key)] }
107
106
  end
108
107
 
109
108
  # Returns an exact copy of the hash.
@@ -116,7 +115,7 @@ module Neo4j
116
115
  # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
117
116
  # Does not overwrite the existing hash.
118
117
  def merge(hash)
119
- self.dup.update(hash)
118
+ dup.update(hash)
120
119
  end
121
120
 
122
121
  # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
@@ -126,7 +125,7 @@ module Neo4j
126
125
  end
127
126
 
128
127
  def reverse_merge!(other_hash)
129
- replace(reverse_merge( other_hash ))
128
+ replace(reverse_merge(other_hash))
130
129
  end
131
130
 
132
131
  # Removes a specified key from the hash.
@@ -134,11 +133,21 @@ module Neo4j
134
133
  super(convert_key(key))
135
134
  end
136
135
 
137
- def stringify_keys!; self end
138
- def stringify_keys; dup end
139
- # undef :symbolize_keys!
140
- def symbolize_keys; to_hash.symbolize_keys end
141
- def to_options!; self end
136
+ def stringify_keys!
137
+ self
138
+ end
139
+
140
+ def stringify_keys
141
+ dup
142
+ end
143
+ # undef :symbolize_keys!
144
+ def symbolize_keys
145
+ to_hash.symbolize_keys
146
+ end
147
+
148
+ def to_options!
149
+ self
150
+ end
142
151
 
143
152
  # Convert to a Hash with String keys.
144
153
  def to_hash
@@ -146,20 +155,20 @@ module Neo4j
146
155
  end
147
156
 
148
157
  protected
149
- def convert_key(key)
150
- key.kind_of?(Symbol) ? key.to_s : key
151
- end
152
158
 
153
- def convert_value(value)
154
- if value.is_a? Hash
155
- value #.nested_under_indifferent_access
156
- elsif value.is_a?(Array)
157
- value.dup.replace(value.map { |e| convert_value(e) })
158
- else
159
- value
160
- end
159
+ def convert_key(key)
160
+ key.is_a?(Symbol) ? key.to_s : key
161
+ end
162
+
163
+ def convert_value(value)
164
+ if value.is_a? Hash
165
+ value # .nested_under_indifferent_access
166
+ elsif value.is_a?(Array)
167
+ value.dup.replace(value.map { |e| convert_value(e) })
168
+ else
169
+ value
161
170
  end
171
+ end
162
172
  end
163
-
164
173
  end
165
- end
174
+ end
@@ -1,24 +1,22 @@
1
- module Neo4j::Core
2
-
3
- module ArgumentHelper
4
-
5
- def self.session(args)
6
- args.last.kind_of?(Neo4j::Session) ? args.pop : Neo4j::Session.current!
1
+ module Neo4j
2
+ module Core
3
+ module ArgumentHelper
4
+ def self.session(args)
5
+ args.last.is_a?(Neo4j::Session) ? args.pop : Neo4j::Session.current!
6
+ end
7
7
  end
8
- end
9
8
 
10
- module TxMethods
11
- def tx_methods(*methods)
12
- methods.each do |method|
13
- tx_method = "#{method}_in_tx"
14
- send(:alias_method, tx_method, method)
15
- send(:define_method, method) do |*args, &block|
16
- session = ArgumentHelper.session(args)
17
- Neo4j::Transaction.run(session.auto_commit?) { send(tx_method, *args, &block) }
9
+ module TxMethods
10
+ def tx_methods(*methods)
11
+ methods.each do |method|
12
+ tx_method = "#{method}_in_tx"
13
+ send(:alias_method, tx_method, method)
14
+ send(:define_method, method) do |*args, &block|
15
+ session = ArgumentHelper.session(args)
16
+ Neo4j::Transaction.run(session.auto_commit?) { send(tx_method, *args, &block) }
17
+ end
18
18
  end
19
19
  end
20
20
  end
21
21
  end
22
-
23
-
24
22
  end
@@ -1,8 +1,9 @@
1
- module Neo4j::Core
2
- class Label
3
- def labels
4
- get_labels.map{|x| Label.new(x.name) }
1
+ module Neo4j
2
+ module Core
3
+ class Label
4
+ def labels
5
+ get_labels.map { |x| Label.new(x.name) }
6
+ end
5
7
  end
6
8
  end
7
-
8
- end
9
+ end
@@ -1,344 +1,347 @@
1
1
  require 'neo4j-core/query_clauses'
2
2
  require 'active_support/notifications'
3
3
 
4
- module Neo4j::Core
5
- # Allows for generation of cypher queries via ruby method calls (inspired by ActiveRecord / arel syntax)
6
- #
7
- # Can be used to express cypher queries in ruby nicely, or to more easily generate queries programatically.
8
- #
9
- # Also, queries can be passed around an application to progressively build a query across different concerns
10
- #
11
- # See also the following link for full cypher language documentation:
12
- # http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html
13
- class Query
14
- include Neo4j::Core::QueryClauses
15
- include Neo4j::Core::QueryFindInBatches
16
-
17
- def initialize(options = {})
18
- @session = options[:session] || Neo4j::Session.current
19
-
20
- @options = options
21
- @clauses = []
22
- @_params = {}
23
- end
24
-
25
- # @method start *args
26
- # START clause
27
- # @return [Query]
28
-
29
- # @method match *args
30
- # MATCH clause
31
- # @return [Query]
4
+ module Neo4j
5
+ module Core
6
+ # Allows for generation of cypher queries via ruby method calls (inspired by ActiveRecord / arel syntax)
7
+ #
8
+ # Can be used to express cypher queries in ruby nicely, or to more easily generate queries programatically.
9
+ #
10
+ # Also, queries can be passed around an application to progressively build a query across different concerns
11
+ #
12
+ # See also the following link for full cypher language documentation:
13
+ # http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html
14
+ class Query
15
+ include Neo4j::Core::QueryClauses
16
+ include Neo4j::Core::QueryFindInBatches
17
+
18
+ def initialize(options = {})
19
+ @session = options[:session] || Neo4j::Session.current
20
+
21
+ @options = options
22
+ @clauses = []
23
+ @_params = {}
24
+ end
32
25
 
33
- # @method optional_match *args
34
- # OPTIONAL MATCH clause
35
- # @return [Query]
26
+ # @method start *args
27
+ # START clause
28
+ # @return [Query]
36
29
 
37
- # @method using *args
38
- # USING clause
39
- # @return [Query]
30
+ # @method match *args
31
+ # MATCH clause
32
+ # @return [Query]
40
33
 
41
- # @method where *args
42
- # WHERE clause
43
- # @return [Query]
34
+ # @method optional_match *args
35
+ # OPTIONAL MATCH clause
36
+ # @return [Query]
44
37
 
45
- # @method with *args
46
- # WITH clause
47
- # @return [Query]
38
+ # @method using *args
39
+ # USING clause
40
+ # @return [Query]
48
41
 
49
- # @method order *args
50
- # ORDER BY clause
51
- # @return [Query]
42
+ # @method where *args
43
+ # WHERE clause
44
+ # @return [Query]
52
45
 
53
- # @method limit *args
54
- # LIMIT clause
55
- # @return [Query]
46
+ # @method with *args
47
+ # WITH clause
48
+ # @return [Query]
56
49
 
57
- # @method skip *args
58
- # SKIP clause
59
- # @return [Query]
50
+ # @method order *args
51
+ # ORDER BY clause
52
+ # @return [Query]
60
53
 
61
- # @method set *args
62
- # SET clause
63
- # @return [Query]
54
+ # @method limit *args
55
+ # LIMIT clause
56
+ # @return [Query]
64
57
 
65
- # @method remove *args
66
- # REMOVE clause
67
- # @return [Query]
58
+ # @method skip *args
59
+ # SKIP clause
60
+ # @return [Query]
68
61
 
69
- # @method unwind *args
70
- # UNWIND clause
71
- # @return [Query]
62
+ # @method set *args
63
+ # SET clause
64
+ # @return [Query]
72
65
 
73
- # @method return *args
74
- # RETURN clause
75
- # @return [Query]
66
+ # @method remove *args
67
+ # REMOVE clause
68
+ # @return [Query]
76
69
 
77
- # @method create *args
78
- # CREATE clause
79
- # @return [Query]
70
+ # @method unwind *args
71
+ # UNWIND clause
72
+ # @return [Query]
80
73
 
81
- # @method create_unique *args
82
- # CREATE UNIQUE clause
83
- # @return [Query]
74
+ # @method return *args
75
+ # RETURN clause
76
+ # @return [Query]
84
77
 
85
- # @method merge *args
86
- # MERGE clause
87
- # @return [Query]
78
+ # @method create *args
79
+ # CREATE clause
80
+ # @return [Query]
88
81
 
89
- # @method on_create_set *args
90
- # ON CREATE SET clause
91
- # @return [Query]
82
+ # @method create_unique *args
83
+ # CREATE UNIQUE clause
84
+ # @return [Query]
92
85
 
93
- # @method on_match_set *args
94
- # ON MATCH SET clause
95
- # @return [Query]
86
+ # @method merge *args
87
+ # MERGE clause
88
+ # @return [Query]
96
89
 
97
- # @method delete *args
98
- # DELETE clause
99
- # @return [Query]
90
+ # @method on_create_set *args
91
+ # ON CREATE SET clause
92
+ # @return [Query]
100
93
 
101
- 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]
94
+ # @method on_match_set *args
95
+ # ON MATCH SET clause
96
+ # @return [Query]
102
97
 
103
- CLAUSES = METHODS.map { |method| const_get(method.split('_').map { |c| c.capitalize }.join + 'Clause') }
98
+ # @method delete *args
99
+ # DELETE clause
100
+ # @return [Query]
104
101
 
105
- METHODS.each_with_index do |clause, i|
106
- clause_class = CLAUSES[i]
102
+ 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)
107
103
 
108
- module_eval(%Q{
109
- def #{clause}(*args)
110
- build_deeper_query(#{clause_class}, args)
111
- end}, __FILE__, __LINE__)
112
- end
104
+ CLAUSES = METHODS.map { |method| const_get(method.split('_').map(&:capitalize).join + 'Clause') }
113
105
 
114
- alias_method :offset, :skip
115
- alias_method :order_by, :order
106
+ METHODS.each_with_index do |clause, i|
107
+ clause_class = CLAUSES[i]
116
108
 
117
- # Clears out previous order clauses and allows only for those specified by args
118
- def reorder(*args)
119
- query = self.copy
109
+ module_eval(%{
110
+ def #{clause}(*args)
111
+ build_deeper_query(#{clause_class}, args)
112
+ end}, __FILE__, __LINE__)
113
+ end
120
114
 
121
- query.remove_clause_class(OrderClause)
122
- query.order(*args)
123
- end
115
+ alias_method :offset, :skip
116
+ alias_method :order_by, :order
124
117
 
125
- # Works the same as the #set method, but when given a nested array it will set properties rather than setting entire objects
126
- # @example
127
- # # Creates a query representing the cypher: MATCH (n:Person) SET n.age = 19
128
- # Query.new.match(n: :Person).set_props(n: {age: 19})
129
- def set_props(*args)
130
- build_deeper_query(SetClause, args, set_props: true)
131
- end
118
+ # Clears out previous order clauses and allows only for those specified by args
119
+ def reorder(*args)
120
+ query = copy
132
121
 
133
- # 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
134
- # @example
135
- # # Creates a query representing the cypher: MATCH (q:Person), r:Car MATCH (p: Person)-->q
136
- # Query.new.match(q: Person).match('r:Car').break.match('(p: Person)-->q')
137
- def break
138
- build_deeper_query(nil)
139
- end
122
+ query.remove_clause_class(OrderClause)
123
+ query.order(*args)
124
+ end
140
125
 
141
- # Allows for the specification of values for params specified in query
142
- # @example
143
- # # Creates a query representing the cypher: MATCH (q: Person {id: {id}})
144
- # # Calls to params don't affect the cypher query generated, but the params will be
145
- # # Passed down when the query is made
146
- # Query.new.match('(q: Person {id: {id}})').params(id: 12)
147
- #
148
- def params(args)
149
- @_params = @_params.merge(args)
126
+ # Works the same as the #set method, but when given a nested array it will set properties rather than setting entire objects
127
+ # @example
128
+ # # Creates a query representing the cypher: MATCH (n:Person) SET n.age = 19
129
+ # Query.new.match(n: :Person).set_props(n: {age: 19})
130
+ def set_props(*args)
131
+ build_deeper_query(SetClause, args, set_props: true)
132
+ end
150
133
 
151
- self
152
- end
134
+ # 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
135
+ # @example
136
+ # # Creates a query representing the cypher: MATCH (q:Person), r:Car MATCH (p: Person)-->q
137
+ # Query.new.match(q: Person).match('r:Car').break.match('(p: Person)-->q')
138
+ def break
139
+ build_deeper_query(nil)
140
+ end
153
141
 
154
- def response
155
- return @response if @response
156
- cypher = self.to_cypher
157
- @response = ActiveSupport::Notifications.instrument('neo4j.cypher_query', context: @options[:context] || 'CYPHER', cypher: cypher, params: merge_params) do
158
- @session._query(cypher, merge_params)
142
+ # Allows for the specification of values for params specified in query
143
+ # @example
144
+ # # Creates a query representing the cypher: MATCH (q: Person {id: {id}})
145
+ # # Calls to params don't affect the cypher query generated, but the params will be
146
+ # # Passed down when the query is made
147
+ # Query.new.match('(q: Person {id: {id}})').params(id: 12)
148
+ #
149
+ def params(args)
150
+ @_params = @_params.merge(args)
151
+
152
+ self
159
153
  end
160
- if !response.respond_to?(:error?) || !response.error?
161
- response
162
- else
163
- response.raise_cypher_error
154
+
155
+ def response
156
+ return @response if @response
157
+ cypher = to_cypher
158
+ @response = ActiveSupport::Notifications.instrument('neo4j.cypher_query', context: @options[:context] || 'CYPHER', cypher: cypher, params: merge_params) do
159
+ @session._query(cypher, merge_params)
160
+ end
161
+ if !response.respond_to?(:error?) || !response.error?
162
+ response
163
+ else
164
+ response.raise_cypher_error
165
+ end
164
166
  end
165
- end
166
167
 
167
- include Enumerable
168
+ include Enumerable
168
169
 
169
- def each
170
- response = self.response
171
- if response.is_a?(Neo4j::Server::CypherResponse)
172
- response.to_node_enumeration
173
- else
174
- Neo4j::Embedded::ResultWrapper.new(response, self.to_cypher)
175
- end.each { |object| yield object }
176
- end
170
+ def each
171
+ response = self.response
172
+ if response.is_a?(Neo4j::Server::CypherResponse)
173
+ response.to_node_enumeration
174
+ else
175
+ Neo4j::Embedded::ResultWrapper.new(response, to_cypher)
176
+ end.each { |object| yield object }
177
+ end
177
178
 
178
- # @method to_a
179
- # 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
180
- # @return [Array]
181
- # @raise [Neo4j::Server::CypherResponse::ResponseError] Raises errors from neo4j server
179
+ # @method to_a
180
+ # 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
181
+ # @return [Array]
182
+ # @raise [Neo4j::Server::CypherResponse::ResponseError] Raises errors from neo4j server
182
183
 
183
184
 
184
- # Executes a query without returning the result
185
- # @return [Boolean] true if successful
186
- # @raise [Neo4j::Server::CypherResponse::ResponseError] Raises errors from neo4j server
187
- def exec
188
- self.response
185
+ # Executes a query without returning the result
186
+ # @return [Boolean] true if successful
187
+ # @raise [Neo4j::Server::CypherResponse::ResponseError] Raises errors from neo4j server
188
+ def exec
189
+ response
189
190
 
190
- true
191
- end
191
+ true
192
+ end
192
193
 
193
- # Return the specified columns as an array.
194
- # If one column is specified, a one-dimensional array is returned with the values of that column
195
- # If two columns are specified, a n-dimensional array is returned with the values of those columns
196
- #
197
- # @example
198
- # Query.new.match(n: :Person).return(p: :name}.pluck(p: :name) # => Array of names
199
- # @example
200
- # Query.new.match(n: :Person).return(p: :name}.pluck('p, DISTINCT p.name') # => Array of [node, name] pairs
201
- #
202
- def pluck(*columns)
203
- query = return_query(columns)
204
- columns = query.response.columns
205
-
206
- case columns.size
207
- when 0
208
- raise ArgumentError, 'No columns specified for Query#pluck'
209
- when 1
210
- column = columns[0]
211
- query.map { |row| row[column] }
212
- else
213
- query.map do |row|
214
- columns.map do |column|
215
- row[column]
194
+ # Return the specified columns as an array.
195
+ # If one column is specified, a one-dimensional array is returned with the values of that column
196
+ # If two columns are specified, a n-dimensional array is returned with the values of those columns
197
+ #
198
+ # @example
199
+ # Query.new.match(n: :Person).return(p: :name}.pluck(p: :name) # => Array of names
200
+ # @example
201
+ # Query.new.match(n: :Person).return(p: :name}.pluck('p, DISTINCT p.name') # => Array of [node, name] pairs
202
+ #
203
+ def pluck(*columns)
204
+ query = return_query(columns)
205
+ columns = query.response.columns
206
+
207
+ case columns.size
208
+ when 0
209
+ fail ArgumentError, 'No columns specified for Query#pluck'
210
+ when 1
211
+ column = columns[0]
212
+ query.map { |row| row[column] }
213
+ else
214
+ query.map do |row|
215
+ columns.map do |column|
216
+ row[column]
217
+ end
216
218
  end
217
219
  end
218
220
  end
219
- end
220
221
 
221
- def return_query(columns)
222
- query = self.copy
223
- query.remove_clause_class(ReturnClause)
222
+ def return_query(columns)
223
+ query = copy
224
+ query.remove_clause_class(ReturnClause)
224
225
 
225
- columns = columns.map do |column_definition|
226
- if column_definition.is_a?(Hash)
227
- column_definition.map { |k, v| "#{k}.#{v}" }
228
- else
229
- column_definition
230
- end
231
- end.flatten.map(&:to_sym)
226
+ columns = columns.map do |column_definition|
227
+ if column_definition.is_a?(Hash)
228
+ column_definition.map { |k, v| "#{k}.#{v}" }
229
+ else
230
+ column_definition
231
+ end
232
+ end.flatten.map(&:to_sym)
232
233
 
233
- query.return(columns)
234
- end
234
+ query.return(columns)
235
+ end
235
236
 
236
- # Returns a CYPHER query string from the object query representation
237
- # @example
238
- # Query.new.match(p: :Person).where(p: {age: 30}) # => "MATCH (p:Person) WHERE p.age = 30
239
- #
240
- # @return [String] Resulting cypher query string
241
- def to_cypher
242
- cypher_string = partitioned_clauses.map do |clauses|
243
- clauses_by_class = clauses.group_by(&:class)
237
+ # Returns a CYPHER query string from the object query representation
238
+ # @example
239
+ # Query.new.match(p: :Person).where(p: {age: 30}) # => "MATCH (p:Person) WHERE p.age = 30
240
+ #
241
+ # @return [String] Resulting cypher query string
242
+ def to_cypher
243
+ cypher_string = partitioned_clauses.map do |clauses|
244
+ clauses_by_class = clauses.group_by(&:class)
244
245
 
245
- cypher_parts = CLAUSES.map do |clause_class|
246
- clauses = clauses_by_class[clause_class]
246
+ cypher_parts = CLAUSES.map do |clause_class|
247
+ clauses = clauses_by_class[clause_class]
247
248
 
248
- clause_class.to_cypher(clauses) if clauses
249
- end
249
+ clause_class.to_cypher(clauses) if clauses
250
+ end
250
251
 
251
- cypher_string = cypher_parts.compact.join(' ')
252
- cypher_string.strip
253
- end.join ' '
252
+ cypher_string = cypher_parts.compact.join(' ')
253
+ cypher_string.strip
254
+ end.join ' '
254
255
 
255
- cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser]
256
- cypher_string.strip
257
- end
256
+ cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser]
257
+ cypher_string.strip
258
+ end
258
259
 
259
- # Returns a CYPHER query specifying the union of the callee object's query and the argument's query
260
- #
261
- # @example
262
- # # Generates cypher: MATCH (n:Person) UNION MATCH (o:Person) WHERE o.age = 10
263
- # q = Neo4j::Core::Query.new.match(o: :Person).where(o: {age: 10})
264
- # result = Neo4j::Core::Query.new.match(n: :Person).union_cypher(q)
265
- #
266
- # @param other_query [Query] Second half of UNION
267
- # @param options [Hash] Specify {all: true} to use UNION ALL
268
- # @return [String] Resulting UNION cypher query string
269
- def union_cypher(other_query, options = {})
270
- "#{self.to_cypher} UNION#{options[:all] ? ' ALL' : ''} #{other_query.to_cypher}"
271
- end
260
+ # Returns a CYPHER query specifying the union of the callee object's query and the argument's query
261
+ #
262
+ # @example
263
+ # # Generates cypher: MATCH (n:Person) UNION MATCH (o:Person) WHERE o.age = 10
264
+ # q = Neo4j::Core::Query.new.match(o: :Person).where(o: {age: 10})
265
+ # result = Neo4j::Core::Query.new.match(n: :Person).union_cypher(q)
266
+ #
267
+ # @param other [Query] Second half of UNION
268
+ # @param options [Hash] Specify {all: true} to use UNION ALL
269
+ # @return [String] Resulting UNION cypher query string
270
+ def union_cypher(other, options = {})
271
+ "#{to_cypher} UNION#{options[:all] ? ' ALL' : ''} #{other.to_cypher}"
272
+ end
272
273
 
273
- def &(other_query)
274
- raise "Sessions don't match!" if @session != other_query.session
274
+ def &(other)
275
+ fail "Sessions don't match!" if @session != other.session
275
276
 
276
- self.class.new(session: @session).tap do |new_query|
277
- new_query.options = self.options.merge(other_query.options)
278
- new_query.clauses = self.clauses + other_query.clauses
279
- end.params(other_query._params)
280
- end
277
+ self.class.new(session: @session).tap do |new_query|
278
+ new_query.options = options.merge(other.options)
279
+ new_query.clauses = clauses + other.clauses
280
+ end.params(other._params)
281
+ end
281
282
 
282
- MEMOIZED_INSTANCE_VARIABLES = [:response, :merge_params]
283
- def copy
284
- self.dup.tap do |query|
285
- MEMOIZED_INSTANCE_VARIABLES.each do |var|
286
- query.instance_variable_set("@#{var}", nil)
283
+ MEMOIZED_INSTANCE_VARIABLES = [:response, :merge_params]
284
+ def copy
285
+ dup.tap do |query|
286
+ MEMOIZED_INSTANCE_VARIABLES.each do |var|
287
+ query.instance_variable_set("@#{var}", nil)
288
+ end
287
289
  end
288
290
  end
289
- end
290
291
 
291
- protected
292
- attr_accessor :session, :options, :clauses, :_params
292
+ protected
293
293
 
294
- def add_clauses(clauses)
295
- @clauses += clauses
296
- end
294
+ attr_accessor :session, :options, :clauses, :_params
297
295
 
298
- def remove_clause_class(clause_class)
299
- @clauses = @clauses.reject do |clause|
300
- clause.is_a?(clause_class)
296
+ def add_clauses(clauses)
297
+ @clauses += clauses
301
298
  end
302
- end
303
- private
304
299
 
305
- def build_deeper_query(clause_class, args = {}, options = {})
306
- self.copy.tap do |new_query|
307
- new_query.add_clauses [nil] if [nil, WithClause].include?(clause_class)
308
- new_query.add_clauses clause_class.from_args(args, options) if clause_class
300
+ def remove_clause_class(clause_class)
301
+ @clauses = @clauses.reject do |clause|
302
+ clause.is_a?(clause_class)
303
+ end
309
304
  end
310
- end
311
305
 
312
- def break_deeper_query
313
- self.copy.tap do |new_query|
314
- new_query.add_clauses [nil]
315
- end
316
- end
306
+ private
317
307
 
318
- def partitioned_clauses
319
- partitioning = [[]]
308
+ def build_deeper_query(clause_class, args = {}, options = {})
309
+ copy.tap do |new_query|
310
+ new_query.add_clauses [nil] if [nil, WithClause].include?(clause_class)
311
+ new_query.add_clauses clause_class.from_args(args, options) if clause_class
312
+ end
313
+ end
320
314
 
321
- @clauses.each do |clause|
322
- if clause.nil? && partitioning.last != []
323
- partitioning << []
324
- else
325
- partitioning.last << clause
315
+ def break_deeper_query
316
+ copy.tap do |new_query|
317
+ new_query.add_clauses [nil]
326
318
  end
327
319
  end
328
320
 
329
- partitioning
330
- end
321
+ def partitioned_clauses
322
+ partitioning = [[]]
331
323
 
332
- def merge_params
333
- @merge_params ||= @clauses.compact.inject(@_params) { |params, clause| params.merge(clause.params) }
334
- end
324
+ @clauses.each do |clause|
325
+ if clause.nil? && partitioning.last != []
326
+ partitioning << []
327
+ else
328
+ partitioning.last << clause
329
+ end
330
+ end
335
331
 
336
- def sanitize_params(params)
337
- passthrough_classes = [String, Numeric, Array, Regexp]
338
- params.each do |key, value|
339
- params[key] = value.to_s if not passthrough_classes.any? {|klass| value.is_a?(klass) }
332
+ partitioning
340
333
  end
341
- end
342
334
 
335
+ def merge_params
336
+ @merge_params ||= @clauses.compact.inject(@_params) { |params, clause| params.merge(clause.params) }
337
+ end
338
+
339
+ def sanitize_params(params)
340
+ passthrough_classes = [String, Numeric, Array, Regexp]
341
+ params.each do |key, value|
342
+ params[key] = value.to_s if not passthrough_classes.any? { |klass| value.is_a?(klass) }
343
+ end
344
+ end
345
+ end
343
346
  end
344
347
  end