neo4j-core 3.1.1 → 4.0.0

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 (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