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.
- 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
|
|