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.
- checksums.yaml +4 -4
- data/lib/neo4j-core.rb +1 -1
- data/lib/neo4j-core/helpers.rb +6 -0
- data/lib/neo4j-core/query.rb +88 -40
- data/lib/neo4j-core/query_clauses.rb +67 -67
- data/lib/neo4j-core/version.rb +1 -1
- data/lib/neo4j-embedded/embedded_label.rb +16 -14
- data/lib/neo4j-embedded/embedded_node.rb +115 -113
- data/lib/neo4j-embedded/embedded_relationship.rb +53 -51
- data/lib/neo4j-embedded/embedded_session.rb +5 -14
- data/lib/neo4j-embedded/label.rb +2 -1
- data/lib/neo4j-server/cypher_label.rb +8 -7
- data/lib/neo4j-server/cypher_relationship.rb +1 -1
- data/lib/neo4j-server/cypher_response.rb +3 -2
- data/lib/neo4j-server/cypher_transaction.rb +1 -6
- data/lib/neo4j-server/resource.rb +1 -1
- data/lib/neo4j/core/cypher_session.rb +28 -0
- data/lib/neo4j/core/cypher_session/adaptors.rb +108 -0
- data/lib/neo4j/core/cypher_session/adaptors/embedded.rb +81 -0
- data/lib/neo4j/core/cypher_session/adaptors/http.rb +159 -0
- data/lib/neo4j/core/cypher_session/responses.rb +27 -0
- data/lib/neo4j/core/cypher_session/responses/embedded.rb +77 -0
- data/lib/neo4j/core/cypher_session/responses/http.rb +104 -0
- data/lib/neo4j/core/cypher_session/result.rb +39 -0
- data/lib/neo4j/core/instrumentable.rb +36 -0
- data/lib/neo4j/core/node.rb +28 -0
- data/lib/neo4j/core/path.rb +15 -0
- data/lib/neo4j/core/relationship.rb +25 -0
- data/lib/neo4j/core/wrappable.rb +35 -0
- data/lib/neo4j/label.rb +7 -0
- data/lib/neo4j/session.rb +3 -3
- data/lib/neo4j/transaction.rb +1 -24
- data/neo4j-core.gemspec +2 -2
- 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
|
153
|
-
yield
|
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.
|
188
|
+
_listeners.shift.call(event, data) until _listeners.empty?
|
189
189
|
end
|
190
190
|
|
191
191
|
# @private
|
data/lib/neo4j/transaction.rb
CHANGED
@@ -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
|
-
|
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
|
|