neo4j-core 6.1.6 → 7.0.0.alpha.1

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -9
  3. data/README.md +48 -0
  4. data/lib/neo4j-core.rb +23 -0
  5. data/lib/neo4j-core/helpers.rb +8 -0
  6. data/lib/neo4j-core/query.rb +23 -20
  7. data/lib/neo4j-core/query_clauses.rb +18 -32
  8. data/lib/neo4j-core/query_find_in_batches.rb +3 -1
  9. data/lib/neo4j-core/version.rb +1 -1
  10. data/lib/neo4j-embedded/cypher_response.rb +4 -0
  11. data/lib/neo4j-embedded/embedded_database.rb +3 -5
  12. data/lib/neo4j-embedded/embedded_node.rb +4 -4
  13. data/lib/neo4j-embedded/embedded_session.rb +21 -10
  14. data/lib/neo4j-embedded/embedded_transaction.rb +4 -10
  15. data/lib/neo4j-server/cypher_node.rb +5 -4
  16. data/lib/neo4j-server/cypher_relationship.rb +3 -3
  17. data/lib/neo4j-server/cypher_response.rb +4 -0
  18. data/lib/neo4j-server/cypher_session.rb +31 -22
  19. data/lib/neo4j-server/cypher_transaction.rb +23 -15
  20. data/lib/neo4j-server/resource.rb +3 -4
  21. data/lib/neo4j/core/cypher_session.rb +17 -9
  22. data/lib/neo4j/core/cypher_session/adaptors.rb +116 -33
  23. data/lib/neo4j/core/cypher_session/adaptors/bolt.rb +331 -0
  24. data/lib/neo4j/core/cypher_session/adaptors/bolt/chunk_writer_io.rb +76 -0
  25. data/lib/neo4j/core/cypher_session/adaptors/bolt/pack_stream.rb +288 -0
  26. data/lib/neo4j/core/cypher_session/adaptors/embedded.rb +60 -29
  27. data/lib/neo4j/core/cypher_session/adaptors/has_uri.rb +63 -0
  28. data/lib/neo4j/core/cypher_session/adaptors/http.rb +123 -119
  29. data/lib/neo4j/core/cypher_session/responses.rb +17 -2
  30. data/lib/neo4j/core/cypher_session/responses/bolt.rb +135 -0
  31. data/lib/neo4j/core/cypher_session/responses/embedded.rb +46 -11
  32. data/lib/neo4j/core/cypher_session/responses/http.rb +49 -40
  33. data/lib/neo4j/core/cypher_session/transactions.rb +33 -0
  34. data/lib/neo4j/core/cypher_session/transactions/bolt.rb +36 -0
  35. data/lib/neo4j/core/cypher_session/transactions/embedded.rb +32 -0
  36. data/lib/neo4j/core/cypher_session/transactions/http.rb +52 -0
  37. data/lib/neo4j/core/instrumentable.rb +2 -2
  38. data/lib/neo4j/core/label.rb +182 -0
  39. data/lib/neo4j/core/node.rb +8 -3
  40. data/lib/neo4j/core/relationship.rb +12 -4
  41. data/lib/neo4j/entity_equality.rb +1 -1
  42. data/lib/neo4j/session.rb +4 -5
  43. data/lib/neo4j/transaction.rb +108 -72
  44. data/neo4j-core.gemspec +6 -6
  45. metadata +34 -40
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 131388b56fcbd32c29bb5408fb8474cdc8b78776
4
- data.tar.gz: 8cd2bf7b5850b2bcac88575196c3174a80a1a47e
3
+ metadata.gz: d35477edf97f10ae13b42128f88351fd5823160f
4
+ data.tar.gz: ef4e066f6673584c3863f0a68a7c0f78034755eb
5
5
  SHA512:
6
- metadata.gz: c9fb22452fdfe95e4c533445eeeccc7180657eba6332f03d0139372575b346db43e744caefa4d16004b426d85b030fe1253d5eb2e3e7a751b5385f7e6782ab2e
7
- data.tar.gz: e58f76b625bd8db33c0e6814ff95c9cd0ef3b2c6b3ab90b7ece0eaa331af3bea722a20e8b7bf75f876e7c2ce469524d9472ab8b4e11a8f4daa98e8ce89630f3d
6
+ metadata.gz: 6855bc2443d440660c73c1f8d0270abf0ca18dca819cbae37f57a2a277e0ef9c49d166209f8377c87397d6ed1314eda0cba700e3b729c764f2b3077c38770476
7
+ data.tar.gz: 0887b3a29ca9c0ce06c1352df53824678f2c571a15fd0b89da9383374af3faab6add251f927079689695d323e412c1d00721e77eb23917331b78972014f72b80
data/Gemfile CHANGED
@@ -5,23 +5,18 @@ gemspec
5
5
  # gem 'neo4j-advanced', '>= 1.8.1', '< 2.0', :require => false
6
6
  # gem 'neo4j-enterprise', '>= 1.8.1', '< 2.0', :require => false
7
7
 
8
- gem 'activesupport', '~> 4.2' if RUBY_VERSION.to_f < 2.2
8
+ gem 'tins', '< 1.7' if RUBY_VERSION.to_f < 2.0
9
9
 
10
10
  group 'development' do
11
- gem 'listen', '< 3.1'
12
- gem 'guard-rspec', require: false
13
- if RUBY_VERSION.to_f < 2.0
14
- gem 'overcommit', '< 0.35.0'
15
- else
16
- gem 'overcommit'
17
- end
11
+ gem 'guard-rspec', require: false if RUBY_PLATFORM != 'java'
12
+ gem 'overcommit'
18
13
  end
19
14
 
20
15
  group 'test' do
21
16
  gem 'coveralls', require: false
22
17
  gem 'simplecov-html', require: false
23
- gem 'tins', '< 1.7' if RUBY_VERSION.to_f < 2.0
24
18
  gem 'rspec', '~> 3.0'
25
19
  gem 'rspec-its'
26
20
  gem 'dotenv'
21
+ gem 'activesupport', '~> 4.0'
27
22
  end
data/README.md CHANGED
@@ -3,6 +3,54 @@
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
+ ## Basic usage
7
+
8
+ ### Executing Cypher queries
9
+
10
+ To make a basic connection to Neo4j to execute Cypher queries, first choose an adaptor. Adaptors for HTTP and Embedded mode (jRuby only) are available (support for Neo4j 3.0's [Bolt protocol](http://alpha.neohq.net/docs/server-manual/bolt.html) is planned). You can create an adaptor like:
11
+
12
+ http_adaptor = Neo4j::Core::CypherSession::Adaptors::HTTP.new('http://neo4j:pass@localhost:7474')
13
+
14
+ # or
15
+
16
+ neo4j_adaptor = Neo4j::Core::CypherSession::Adaptors::Embedded.new('/file/path/to/graph.db')
17
+
18
+ Once you have an adaptor you can create a session like so:
19
+
20
+ neo4j_session = Neo4j::Core::CypherSession.new(http_adaptor)
21
+
22
+ From there you can make queries with a Cypher string:
23
+
24
+ # Basic query
25
+ neo4j_session.query('MATCH (n) RETURN n LIMIT 10')
26
+
27
+ # Query with parameters
28
+ neo4j_session.query('MATCH (n) RETURN n LIMIT {limit}', limit: 10)
29
+
30
+ Or via the `Neo4j::Core::Query` class
31
+
32
+ query_obj = Neo4j::Core::Query.new.match(:n).return(:n).limit(10)
33
+
34
+ neo4j_session.query(query_obj)
35
+
36
+ Making multiple queries with one request is support with the HTTP Adaptor:
37
+
38
+ results = neo4j_session.queries do
39
+ append 'MATCH (n:Foo) RETURN n LIMIT 10'
40
+ append 'MATCH (n:Bar) RETURN n LIMIT 5'
41
+ end
42
+
43
+ results[0] # results of first query
44
+ results[1] # results of second query
45
+
46
+ When doing batched queries, there is also a shortcut for getting a new `Neo4j::Core::Query`:
47
+
48
+ results = neo4j_session.queries do
49
+ append query.match(:n).return(:n).limit(10)
50
+ end
51
+
52
+ results[0] # result
53
+
6
54
  ## Documentation
7
55
 
8
56
  ### 3.0+ Documentation:
data/lib/neo4j-core.rb CHANGED
@@ -24,3 +24,26 @@ require 'neo4j/transaction'
24
24
 
25
25
  require 'rake'
26
26
  require 'neo4j/rake_tasks'
27
+
28
+ require 'logger'
29
+
30
+ module Neo4j
31
+ module Core
32
+ ORIGINAL_FORMATTER = ::Logger::Formatter.new
33
+
34
+ def self.logger(stream = STDOUT)
35
+ @logger ||= Logger.new(stream).tap do |logger|
36
+ logger.formatter = method(:formatter)
37
+ end
38
+ end
39
+
40
+ def self.formatter(severity, datetime, progname, msg)
41
+ output = ''
42
+ if Thread.current != Thread.main
43
+ output += "#{ANSI::YELLOW}Thread: #{Thread.current.object_id}: #{ANSI::CLEAR}"
44
+ end
45
+ output += msg
46
+ ORIGINAL_FORMATTER.call(severity, datetime, progname, output)
47
+ end
48
+ end
49
+ end
@@ -18,6 +18,14 @@ module Neo4j
18
18
  def self.using_new_session?
19
19
  ENV.key?('NEW_NEO4J_SESSIONS')
20
20
  end
21
+
22
+ def self.wrapping_level(level = nil)
23
+ if level.nil?
24
+ @wrapping_level || :core_entity
25
+ else
26
+ @wrapping_level = level
27
+ end
28
+ end
21
29
  end
22
30
  end
23
31
  end
@@ -158,10 +158,11 @@ module Neo4j
158
158
  # DETACH DELETE clause
159
159
  # @return [Query]
160
160
 
161
- METHODS = %w(start match optional_match call using where create create_unique merge set on_create_set on_match_set remove unwind delete detach_delete with return order skip limit) # rubocop:disable Metrics/LineLength
162
- BREAK_METHODS = %(with call)
161
+ METHODS = %w(start match optional_match using where create create_unique merge set on_create_set on_match_set remove unwind delete detach_delete with return order skip limit)
162
+ BREAK_METHODS = %(with)
163
163
 
164
164
  CLAUSIFY_CLAUSE = proc { |method| const_get(method.to_s.split('_').map(&:capitalize).join + 'Clause') }
165
+
165
166
  CLAUSES = METHODS.map(&CLAUSIFY_CLAUSE)
166
167
 
167
168
  METHODS.each_with_index do |clause, i|
@@ -175,8 +176,8 @@ module Neo4j
175
176
  end
176
177
  end
177
178
 
178
- alias_method :offset, :skip
179
- alias_method :order_by, :order
179
+ alias offset skip
180
+ alias order_by order
180
181
 
181
182
  # Clears out previous order clauses and allows only for those specified by args
182
183
  def reorder(*args)
@@ -235,16 +236,16 @@ module Neo4j
235
236
  def response
236
237
  return @response if @response
237
238
 
238
- cypher = to_cypher
239
- pretty_cypher = to_cypher(pretty: true) if self.class.pretty_cypher
240
-
241
239
  @response = if session_is_new_api?
242
- @session.query(self)
240
+ @session.query(self, transaction: Transaction.current_for(@session), wrap_level: (:core_entity if unwrapped?))
243
241
  else
244
- @session._query(cypher, merge_params, context: @options[:context], pretty_cypher: pretty_cypher)
242
+ @session._query(to_cypher, merge_params,
243
+ context: @options[:context], pretty_cypher: (pretty_cypher if self.class.pretty_cypher)).tap(&method(:raise_if_cypher_error!))
245
244
  end
245
+ end
246
246
 
247
- (!@response.respond_to?(:error?) || !response.error?) ? @response : @response.raise_cypher_error
247
+ def raise_if_cypher_error!(response)
248
+ response.raise_cypher_error if response.respond_to?(:error?) && response.error?
248
249
  end
249
250
 
250
251
  def match_nodes(hash, optional_match = false)
@@ -269,9 +270,11 @@ module Neo4j
269
270
 
270
271
  def each
271
272
  response = self.response
272
- if response.is_a?(Neo4j::Server::CypherResponse)
273
+ if defined?(Neo4j::Server::CypherResponse) && response.is_a?(Neo4j::Server::CypherResponse)
273
274
  response.unwrapped! if unwrapped?
274
275
  response.to_node_enumeration
276
+ elsif defined?(Neo4j::Core::CypherSession::Result) && response.is_a?(Neo4j::Core::CypherSession::Result)
277
+ response.to_a
275
278
  else
276
279
  Neo4j::Embedded::ResultWrapper.new(response, to_cypher, unwrapped?)
277
280
  end.each { |object| yield object }
@@ -307,8 +310,7 @@ module Neo4j
307
310
  query = return_query(columns)
308
311
  columns = query.response.columns
309
312
 
310
- case columns.size
311
- when 1
313
+ if columns.size == 1
312
314
  column = columns[0]
313
315
  query.map { |row| row[column] }
314
316
  else
@@ -346,7 +348,7 @@ module Neo4j
346
348
  cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser]
347
349
  cypher_string.tap(&:strip!)
348
350
  end
349
- alias_method :cypher, :to_cypher
351
+ alias cypher to_cypher
350
352
 
351
353
  def pretty_cypher
352
354
  to_cypher(pretty: true)
@@ -481,17 +483,18 @@ module Neo4j
481
483
  @partitioning[-2] && @partitioning[-2].any? { |c| self.class.clause_is_order_or_limit?(c) }
482
484
  end
483
485
 
484
- def self.clause_is_order_or_limit?(clause)
485
- clause.is_a?(::Neo4j::Core::QueryClauses::OrderClause) ||
486
- clause.is_a?(::Neo4j::Core::QueryClauses::LimitClause)
486
+ class << self
487
+ def clause_is_order_or_limit?(clause)
488
+ clause.is_a?(::Neo4j::Core::QueryClauses::OrderClause) ||
489
+ clause.is_a?(::Neo4j::Core::QueryClauses::LimitClause)
490
+ end
487
491
  end
488
492
  end
489
493
 
490
494
  # SHOULD BE DEPRECATED
491
495
  def merge_params
492
- @clauses.compact!
493
- @merge_params ||= @clauses.inject(@params.to_hash) { |params, clause| params.merge!(clause.params) }
494
- @params.to_hash
496
+ @merge_params_base ||= @clauses.compact.inject({}) { |params, clause| params.merge!(clause.params) }
497
+ @params.to_hash.merge(@merge_params_base)
495
498
  end
496
499
  end
497
500
  end
@@ -90,8 +90,8 @@ module Neo4j
90
90
  when Hash
91
91
  if value.values.map(&:class) == [Hash]
92
92
  value.first.first
93
- else
94
- key if !_use_key_for_var?(value, prefer)
93
+ elsif !_use_key_for_var?(value, prefer)
94
+ key
95
95
  end
96
96
  else
97
97
  fail ArgError, value
@@ -144,7 +144,7 @@ module Neo4j
144
144
  keyword
145
145
  end
146
146
 
147
- "#{final_keyword} #{string}" if string.size > 0
147
+ "#{final_keyword} #{string}" if !string.empty?
148
148
  end
149
149
 
150
150
  def clause_string(clauses, pretty)
@@ -162,6 +162,14 @@ module Neo4j
162
162
  def clause_color
163
163
  ANSI::CYAN
164
164
  end
165
+
166
+ def from_key_and_single_value(key, value)
167
+ if value.to_sym == :neo_id
168
+ "ID(#{key})"
169
+ else
170
+ "#{key}.#{value}"
171
+ end
172
+ end
165
173
  end
166
174
 
167
175
  def self.paramaterize_key!(key)
@@ -211,7 +219,7 @@ module Neo4j
211
219
  label_arg = label_arg.to_s
212
220
  label_arg.strip!
213
221
  if !label_arg.empty? && label_arg[0] != ':'
214
- label_arg = "`#{label_arg}`" unless label_arg.match(' ')
222
+ label_arg = "`#{label_arg}`" unless label_arg[' ']
215
223
  label_arg = ":#{label_arg}"
216
224
  end
217
225
  label_arg
@@ -221,10 +229,10 @@ module Neo4j
221
229
  return '' if not attributes
222
230
 
223
231
  attributes_string = attributes.map do |key, value|
224
- if value.to_s.match(/^{.+}$/)
232
+ if value.to_s =~ /^{.+}$/
225
233
  "#{key}: #{value}"
226
234
  else
227
- param_key = "#{prefix}#{key}".gsub('::', '_')
235
+ param_key = "#{prefix}#{key}".gsub(/:+/, '_')
228
236
  param_key = add_param(param_key, value)
229
237
  "#{key}: {#{param_key}}"
230
238
  end
@@ -337,24 +345,6 @@ module Neo4j
337
345
  end
338
346
  end
339
347
 
340
- class CallClause < Clause
341
- KEYWORD = 'CALL'
342
-
343
- def from_string(value)
344
- value
345
- end
346
-
347
- class << self
348
- def clause_strings(clauses)
349
- clauses.map!(&:value)
350
- end
351
-
352
- def clause_join
353
- " #{KEYWORD} "
354
- end
355
- end
356
- end
357
-
358
348
  class MatchClause < Clause
359
349
  KEYWORD = 'MATCH'
360
350
 
@@ -511,13 +501,13 @@ module Neo4j
511
501
  def from_key_and_value(key, value)
512
502
  case value
513
503
  when String, Symbol
514
- "#{key}.#{value}"
504
+ self.class.from_key_and_single_value(key, value)
515
505
  when Array
516
506
  value.map do |v|
517
- v.is_a?(Hash) ? from_key_and_value(key, v) : "#{key}.#{v}"
507
+ v.is_a?(Hash) ? from_key_and_value(key, v) : self.class.from_key_and_single_value(key, v)
518
508
  end
519
509
  when Hash
520
- value.map { |k, v| "#{key}.#{k} #{v.upcase}" }
510
+ value.map { |k, v| "#{self.class.from_key_and_single_value(key, k)} #{v.upcase}" }
521
511
  end
522
512
  end
523
513
 
@@ -708,11 +698,7 @@ module Neo4j
708
698
  from_key_and_value(key, v)
709
699
  end.join(Clause::COMMA_SPACE)
710
700
  when String, Symbol
711
- if value.to_sym == :neo_id
712
- "ID(#{key})"
713
- else
714
- "#{key}.#{value}"
715
- end
701
+ self.class.from_key_and_single_value(key, value)
716
702
  else
717
703
  fail ArgError, value
718
704
  end
@@ -18,7 +18,9 @@ module Neo4j
18
18
 
19
19
  break if records_size < batch_size
20
20
 
21
- records = query.where("#{node_var}.#{prop_var} > {primary_key_offset}").params(primary_key_offset: primary_key_offset).to_a
21
+ primary_key_var = Neo4j::Core::QueryClauses::Clause.from_key_and_single_value(node_var, prop_var)
22
+ records = query.where("#{primary_key_var} > {primary_key_offset}")
23
+ .params(primary_key_offset: primary_key_offset).to_a
22
24
  end
23
25
  end
24
26
 
@@ -1,5 +1,5 @@
1
1
  module Neo4j
2
2
  module Core
3
- VERSION = '6.1.6'
3
+ VERSION = '7.0.0.alpha.1'
4
4
  end
5
5
  end
@@ -39,6 +39,10 @@ module Neo4j
39
39
  @source.columns.map!(&:to_sym)
40
40
  end
41
41
 
42
+ def error?
43
+ false
44
+ end
45
+
42
46
  def each
43
47
  fail ResultsAlreadyConsumedException unless @unread
44
48
 
@@ -6,11 +6,9 @@ module Neo4j
6
6
 
7
7
  class << self
8
8
  def connect(db_location, config = {})
9
- if Neo4j::Session.current.respond_to?(:db_location) && Neo4j::Session.current.db_location == db_location
10
- return Neo4j::Session.current
11
- else
12
- EmbeddedSession.new(db_location, config)
13
- end
9
+ return Neo4j::Session.current if Neo4j::Session.current.respond_to?(:db_location) && Neo4j::Session.current.db_location == db_location
10
+
11
+ EmbeddedSession.new(db_location, config)
14
12
  end
15
13
 
16
14
  def create_db(db_location)
@@ -16,8 +16,8 @@ module Neo4j
16
16
  'Enumerable<Neo4j::Relationship>'
17
17
  end
18
18
 
19
- def each(&block)
20
- @node._rels(@match).each { |r| block.call(r.wrapper) }
19
+ def each
20
+ @node._rels(@match).each { |r| yield(r.wrapper) }
21
21
  end
22
22
  tx_methods :each
23
23
 
@@ -39,8 +39,8 @@ module Neo4j
39
39
  'Enumerable<Neo4j::Node>'
40
40
  end
41
41
 
42
- def each(&block)
43
- @node._rels(@match).each { |r| block.call(r.other_node(@node)) }
42
+ def each
43
+ @node._rels(@match).each { |r| yield(r.other_node(@node)) }
44
44
  end
45
45
  tx_methods :each
46
46
 
@@ -68,21 +68,32 @@ module Neo4j
68
68
  Java::OrgNeo4jTest::ImpermanentGraphDatabase
69
69
  end
70
70
 
71
- def begin_tx
72
- if Neo4j::Transaction.current
73
- # Handle nested transaction "placebo transaction"
74
- Neo4j::Transaction.current.push_nested!
75
- else
76
- Neo4j::Embedded::EmbeddedTransaction.new(@graph_db.begin_tx)
77
- end
78
- Neo4j::Transaction.current
79
- end
80
-
81
71
  def close
82
72
  super
83
73
  shutdown
84
74
  end
85
75
 
76
+ def self.transaction_class
77
+ Neo4j::Embedded::EmbeddedTransaction
78
+ end
79
+
80
+ # Duplicate of CypherSession::Adaptor::Base#transaction
81
+ def transaction
82
+ return self.class.transaction_class.new(self) if !block_given?
83
+
84
+ begin
85
+ tx = transaction
86
+
87
+ yield tx
88
+ rescue Exception => e # rubocop:disable Lint/RescueException
89
+ tx.mark_failed
90
+
91
+ raise e
92
+ ensure
93
+ tx.close
94
+ end
95
+ end
96
+
86
97
  def shutdown
87
98
  @graph_db && @graph_db.shutdown
88
99