neo4j-core 5.1.14 → 6.0.0.alpha.1

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