neo4j-core 5.1.14 → 6.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/neo4j-core.rb +1 -1
  3. data/lib/neo4j-core/helpers.rb +6 -0
  4. data/lib/neo4j-core/query.rb +88 -40
  5. data/lib/neo4j-core/query_clauses.rb +67 -67
  6. data/lib/neo4j-core/version.rb +1 -1
  7. data/lib/neo4j-embedded/embedded_label.rb +16 -14
  8. data/lib/neo4j-embedded/embedded_node.rb +115 -113
  9. data/lib/neo4j-embedded/embedded_relationship.rb +53 -51
  10. data/lib/neo4j-embedded/embedded_session.rb +5 -14
  11. data/lib/neo4j-embedded/label.rb +2 -1
  12. data/lib/neo4j-server/cypher_label.rb +8 -7
  13. data/lib/neo4j-server/cypher_relationship.rb +1 -1
  14. data/lib/neo4j-server/cypher_response.rb +3 -2
  15. data/lib/neo4j-server/cypher_transaction.rb +1 -6
  16. data/lib/neo4j-server/resource.rb +1 -1
  17. data/lib/neo4j/core/cypher_session.rb +28 -0
  18. data/lib/neo4j/core/cypher_session/adaptors.rb +108 -0
  19. data/lib/neo4j/core/cypher_session/adaptors/embedded.rb +81 -0
  20. data/lib/neo4j/core/cypher_session/adaptors/http.rb +159 -0
  21. data/lib/neo4j/core/cypher_session/responses.rb +27 -0
  22. data/lib/neo4j/core/cypher_session/responses/embedded.rb +77 -0
  23. data/lib/neo4j/core/cypher_session/responses/http.rb +104 -0
  24. data/lib/neo4j/core/cypher_session/result.rb +39 -0
  25. data/lib/neo4j/core/instrumentable.rb +36 -0
  26. data/lib/neo4j/core/node.rb +28 -0
  27. data/lib/neo4j/core/path.rb +15 -0
  28. data/lib/neo4j/core/relationship.rb +25 -0
  29. data/lib/neo4j/core/wrappable.rb +35 -0
  30. data/lib/neo4j/label.rb +7 -0
  31. data/lib/neo4j/session.rb +3 -3
  32. data/lib/neo4j/transaction.rb +1 -24
  33. data/neo4j-core.gemspec +2 -2
  34. metadata +22 -9
@@ -0,0 +1,27 @@
1
+ require 'neo4j/core/cypher_session/result'
2
+
3
+ module Neo4j
4
+ module Core
5
+ class CypherSession
6
+ module Responses
7
+ MAP = {}
8
+
9
+ class Base
10
+ class CypherError < StandardError; end
11
+
12
+ include Enumerable
13
+
14
+ def each
15
+ results.each do |result|
16
+ yield result
17
+ end
18
+ end
19
+
20
+ def results
21
+ fail '#results not implemented!'
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,77 @@
1
+ require 'neo4j/core/cypher_session/responses'
2
+
3
+ module Neo4j
4
+ module Core
5
+ class CypherSession
6
+ module Responses
7
+ class Embedded < Base
8
+ attr_reader :results, :request_data
9
+
10
+ def initialize(execution_results)
11
+ @results = execution_results.map do |execution_result|
12
+ result_from_execution_result(execution_result)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def result_from_execution_result(execution_result)
19
+ columns = execution_result.columns.to_a
20
+ rows = execution_result.map do |execution_result_row|
21
+ columns.map { |column| wrap_entity(execution_result_row[column]) }
22
+ end
23
+ Result.new(columns, rows)
24
+ end
25
+
26
+ def wrap_entity(entity)
27
+ if entity.is_a?(Array) ||
28
+ entity.is_a?(Java::ScalaCollectionConvert::Wrappers::SeqWrapper)
29
+ entity.to_a.map { |e| wrap_entity(e) }
30
+ elsif entity.is_a?(Java::OrgNeo4jKernelImplCore::NodeProxy)
31
+ wrap_node(entity)
32
+ elsif entity.is_a?(Java::OrgNeo4jKernelImplCore::RelationshipProxy)
33
+ wrap_relationship(entity)
34
+ elsif entity.respond_to?(:path_entities)
35
+ wrap_path(entity)
36
+ else
37
+ wrap_value(entity)
38
+ end
39
+ end
40
+
41
+ def wrap_node(entity)
42
+ ::Neo4j::Core::Node.new(entity.get_id,
43
+ entity.get_labels.map(&:to_s),
44
+ get_entity_properties(entity)).wrap
45
+ end
46
+
47
+ def wrap_relationship(entity)
48
+ ::Neo4j::Core::Relationship.new(entity.get_id,
49
+ entity.get_type.name,
50
+ get_entity_properties(entity)).wrap
51
+ end
52
+
53
+ def wrap_path(entity)
54
+ ::Neo4j::Core::Path.new(entity.nodes.map(&method(:wrap_node)),
55
+ entity.relationships.map(&method(:wrap_relationship)),
56
+ nil).wrap
57
+ end
58
+
59
+ def wrap_value(entity)
60
+ if entity.is_a?(Java::ScalaCollectionConvert::Wrappers::MapWrapper)
61
+ entity.each_with_object({}) { |(k, v), r| r[k.to_sym] = v }
62
+ else
63
+ # Convert from Java?
64
+ entity
65
+ end
66
+ end
67
+
68
+ def get_entity_properties(entity)
69
+ entity.get_property_keys.each_with_object({}) do |key, result|
70
+ result[key.to_sym] = entity.get_property(key)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,104 @@
1
+ require 'neo4j/core/cypher_session/responses'
2
+
3
+ module Neo4j
4
+ module Core
5
+ class CypherSession
6
+ module Responses
7
+ class HTTP < Base
8
+ attr_reader :results, :request_data
9
+
10
+ def initialize(faraday_response, request_data)
11
+ @request_data = request_data
12
+
13
+ validate_faraday_response!(faraday_response)
14
+
15
+ @results = faraday_response.body[:results].map do |result_data|
16
+ result_from_data(result_data[:columns], result_data[:data])
17
+ end
18
+ end
19
+
20
+ def result_from_data(columns, entities_data)
21
+ rows = entities_data.map do |entity_data|
22
+ wrap_entity entity_data[:row], entity_data[:rest]
23
+ end
24
+
25
+ Result.new(columns, rows)
26
+ end
27
+
28
+ def wrap_entity(row_data, rest_data)
29
+ row_data.each_with_index.map do |row_datum, i|
30
+ rest_datum = rest_data[i]
31
+
32
+ if rest_datum.is_a?(Array)
33
+ wrap_entity(row_datum, rest_datum)
34
+ elsif (ident = identify_entity(rest_datum))
35
+ send("wrap_#{ident}", rest_datum, row_datum)
36
+ else
37
+ row_datum
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def identify_entity(rest_datum)
45
+ self_string = rest_datum[:self]
46
+ if self_string
47
+ type = self_string.split('/')[-2]
48
+ if type == 'node'
49
+ :node
50
+ elsif type == 'relationship'
51
+ :relationship
52
+ end
53
+ elsif [:nodes, :relationships, :start, :end, :length].all? { |k| rest_datum.key?(k) }
54
+ :path
55
+ end
56
+ end
57
+
58
+ def wrap_node(rest_datum, _)
59
+ metadata_data = rest_datum[:metadata]
60
+ ::Neo4j::Core::Node.new(id_from_rest_datum(rest_datum),
61
+ metadata_data && metadata_data[:labels],
62
+ rest_datum[:data]).wrap
63
+ end
64
+
65
+ def wrap_relationship(rest_datum, _)
66
+ metadata_data = rest_datum[:metadata]
67
+ ::Neo4j::Core::Relationship.new(id_from_rest_datum(rest_datum),
68
+ metadata_data && metadata_data[:type],
69
+ rest_datum[:data]).wrap
70
+ end
71
+
72
+ def wrap_path(rest_datum, row_datum)
73
+ nodes = rest_datum[:nodes].each_with_index.map do |url, i|
74
+ Node.from_url(url, row_datum[2 * i])
75
+ end
76
+ relationships = rest_datum[:relationships].each_with_index.map do |url, i|
77
+ Relationship.from_url(url, row_datum[(2 * i) + 1])
78
+ end
79
+
80
+ ::Neo4j::Core::Path.new(nodes, relationships, rest_datum[:directions]).wrap
81
+ end
82
+
83
+ def id_from_rest_datum(rest_datum)
84
+ if rest_datum[:metadata]
85
+ rest_datum[:metadata][:id]
86
+ else
87
+ rest_datum[:self].split('/').last.to_i
88
+ end
89
+ end
90
+
91
+ def validate_faraday_response!(faraday_response)
92
+ if error = faraday_response.body[:errors][0]
93
+ fail CypherError, "#{ANSI::CYAN}#{error[:code]}#{ANSI::CLEAR}: #{error[:message]}"
94
+ end
95
+
96
+ return if (200..299).include?(status = faraday_response.status)
97
+
98
+ fail CypherError, "Expected 200-series response for #{faraday_response.env.url} (got #{status})"
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,39 @@
1
+ require 'neo4j/core/node'
2
+ require 'neo4j/core/relationship'
3
+ require 'neo4j/core/path'
4
+
5
+ module Neo4j
6
+ module Core
7
+ class CypherSession
8
+ class Result
9
+ attr_reader :columns, :rows
10
+
11
+ def initialize(columns, rows)
12
+ @columns = columns.map(&:to_sym)
13
+ @rows = rows
14
+ @struct_class = Struct.new(*@columns)
15
+ end
16
+
17
+ include Enumerable
18
+
19
+ def each
20
+ structs.each do |struct|
21
+ yield struct
22
+ end
23
+ end
24
+
25
+ def structs
26
+ @structs ||= rows.map do |row|
27
+ @struct_class.new(*row)
28
+ end
29
+ end
30
+
31
+ def hashes
32
+ @hashes ||= rows.map do |row|
33
+ Hash[@columns.zip(row)]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ require 'active_support/notifications'
2
+
3
+ module Neo4j
4
+ module Core
5
+ module Instrumentable
6
+ def self.included(base)
7
+ base.send :include, InstanceMethods
8
+ base.extend ClassMethods
9
+ end
10
+
11
+ module InstanceMethods
12
+ end
13
+
14
+ module ClassMethods
15
+ def instrument(name, label, arguments, &block)
16
+ # defining class methods
17
+ klass = class << self; self; end
18
+ klass.instance_eval do
19
+ define_method("subscribe_to_#{name}") do |&b|
20
+ ActiveSupport::Notifications.subscribe(label) do |a, start, finish, id, payload|
21
+ b.call block.call(a, start, finish, id, payload)
22
+ end
23
+ end
24
+
25
+ define_method("instrument_#{name}") do |*args, &b|
26
+ hash = arguments.each_with_index.each_with_object({}) do |(argument, i), result|
27
+ result[argument.to_sym] = args[i]
28
+ end
29
+ ActiveSupport::Notifications.instrument(label, hash) { b.call }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ require 'neo4j/core/wrappable'
2
+
3
+ module Neo4j
4
+ module Core
5
+ class Node
6
+ attr_reader :id, :labels, :properties
7
+ alias_method :props, :properties
8
+
9
+ include Wrappable
10
+
11
+ def initialize(id, labels, properties = {})
12
+ @id = id
13
+ @labels = labels.map(&:to_sym) unless labels.nil?
14
+ @properties = properties
15
+ end
16
+
17
+ class << self
18
+ def from_url(url, properties = {})
19
+ id = url.split('/')[-1].to_i
20
+ labels = nil # unknown
21
+ properties = properties
22
+
23
+ new(id, labels, properties)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ module Neo4j
2
+ module Core
3
+ class Path
4
+ attr_reader :nodes, :relationships, :directions
5
+
6
+ include Wrappable
7
+
8
+ def initialize(nodes, relationships, directions)
9
+ @nodes = nodes
10
+ @relationships = relationships
11
+ @directions = directions
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module Neo4j
2
+ module Core
3
+ class Relationship
4
+ attr_reader :id, :type, :properties
5
+
6
+ include Wrappable
7
+
8
+ def initialize(id, type, properties)
9
+ @id = id
10
+ @type = type.to_sym unless type.nil?
11
+ @properties = properties
12
+ end
13
+
14
+ class << self
15
+ def from_url(url, properties = {})
16
+ id = url.split('/')[-1].to_i
17
+ type = nil # unknown
18
+ properties = properties
19
+
20
+ new(id, type, properties)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ module Neo4j
2
+ module Core
3
+ module Wrappable
4
+ def self.included(base)
5
+ base.send :include, InstanceMethods
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ def wrap
11
+ self.class.wrap(self)
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ def wrapper_callback(proc)
17
+ fail 'Callback already specified!' if @wrapper_callback
18
+ @wrapper_callback = proc
19
+ end
20
+
21
+ def clear_wrapper_callback
22
+ @wrapper_callback = nil
23
+ end
24
+
25
+ def wrap(node)
26
+ if @wrapper_callback
27
+ @wrapper_callback.call(node)
28
+ else
29
+ node
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/neo4j/label.rb CHANGED
@@ -59,6 +59,13 @@ module Neo4j
59
59
  session._query_or_fail(cypher)
60
60
  end
61
61
 
62
+ private
63
+
64
+ def validate_index_options!(options)
65
+ return unless options[:type] && options[:type] != :exact
66
+ fail "Type #{options[:type]} is not supported"
67
+ end
68
+
62
69
  class << self
63
70
  INDEX_PATH = '/db/data/schema/index/'
64
71
  CONSTRAINT_PATH = '/db/data/schema/constraint/'
data/lib/neo4j/session.rb CHANGED
@@ -149,8 +149,8 @@ module Neo4j
149
149
 
150
150
  # Registers a callback which will be called immediately if session is already available,
151
151
  # or called when it later becomes available.
152
- def on_session_available
153
- yield Neo4j::Session.current if Neo4j::Session.current
152
+ def on_next_session_available
153
+ return yield(Neo4j::Session.current) if Neo4j::Session.current
154
154
 
155
155
  add_listener do |event, data|
156
156
  yield data if event == :session_available
@@ -185,7 +185,7 @@ module Neo4j
185
185
 
186
186
  # @private
187
187
  def _notify_listeners(event, data)
188
- _listeners.each { |li| li.call(event, data) }
188
+ _listeners.shift.call(event, data) until _listeners.empty?
189
189
  end
190
190
 
191
191
  # @private
@@ -21,18 +21,6 @@ module Neo4j
21
21
  end
22
22
  alias_method :failure?, :failed?
23
23
 
24
- def autoclosed!
25
- @autoclosed = true if transient_failures_autoclose?
26
- end
27
-
28
- def transient_failures_autoclose?
29
- Neo4j::Session.current.version >= '2.2.6'
30
- end
31
-
32
- def autoclosed?
33
- !!@autoclosed
34
- end
35
-
36
24
  def mark_expired
37
25
  @expired = true
38
26
  end
@@ -73,18 +61,7 @@ module Neo4j
73
61
  return if @pushed_nested >= 0
74
62
  fail "Can't commit transaction, already committed" if @pushed_nested < -1
75
63
  Neo4j::Transaction.unregister(self)
76
- post_close!
77
- end
78
-
79
- private
80
-
81
- def post_close!
82
- return if autoclosed?
83
- if failed?
84
- delete
85
- else
86
- commit
87
- end
64
+ failed? ? delete : commit
88
65
  end
89
66
  end
90
67