neo4j-core 6.1.6 → 7.0.0.alpha.1

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