neo4j-core 8.1.4 → 9.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 (59) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +71 -8
  3. data/lib/neo4j-core.rb +3 -49
  4. data/lib/neo4j/core.rb +4 -0
  5. data/lib/neo4j/core/config.rb +13 -0
  6. data/lib/neo4j/core/cypher_session/adaptors.rb +15 -15
  7. data/lib/neo4j/core/cypher_session/adaptors/bolt.rb +39 -48
  8. data/lib/neo4j/core/cypher_session/adaptors/bolt/chunk_writer_io.rb +0 -4
  9. data/lib/neo4j/core/cypher_session/adaptors/bolt/pack_stream.rb +7 -3
  10. data/lib/neo4j/core/cypher_session/adaptors/embedded.rb +1 -2
  11. data/lib/neo4j/core/cypher_session/adaptors/has_uri.rb +4 -0
  12. data/lib/neo4j/core/cypher_session/adaptors/http.rb +1 -3
  13. data/lib/neo4j/core/cypher_session/responses.rb +1 -1
  14. data/lib/neo4j/core/cypher_session/responses/bolt.rb +0 -17
  15. data/lib/neo4j/core/cypher_session/responses/embedded.rb +9 -7
  16. data/lib/neo4j/core/cypher_session/responses/http.rb +3 -4
  17. data/lib/neo4j/core/cypher_session/transactions.rb +2 -0
  18. data/lib/{neo4j-core → neo4j/core}/helpers.rb +1 -14
  19. data/lib/neo4j/core/logging.rb +44 -0
  20. data/lib/{neo4j-core → neo4j/core}/query.rb +7 -6
  21. data/lib/{neo4j-core → neo4j/core}/query_clauses.rb +9 -16
  22. data/lib/{neo4j-core → neo4j/core}/query_find_in_batches.rb +3 -5
  23. data/lib/{neo4j-core → neo4j/core}/version.rb +1 -1
  24. data/lib/neo4j/transaction.rb +6 -8
  25. data/neo4j-core.gemspec +13 -11
  26. metadata +46 -50
  27. data/lib/ext/kernel.rb +0 -9
  28. data/lib/neo4j-core/active_entity.rb +0 -11
  29. data/lib/neo4j-core/label.rb +0 -9
  30. data/lib/neo4j-embedded.rb +0 -16
  31. data/lib/neo4j-embedded/cypher_response.rb +0 -71
  32. data/lib/neo4j-embedded/embedded_database.rb +0 -26
  33. data/lib/neo4j-embedded/embedded_ha_session.rb +0 -30
  34. data/lib/neo4j-embedded/embedded_impermanent_session.rb +0 -17
  35. data/lib/neo4j-embedded/embedded_label.rb +0 -88
  36. data/lib/neo4j-embedded/embedded_node.rb +0 -206
  37. data/lib/neo4j-embedded/embedded_relationship.rb +0 -77
  38. data/lib/neo4j-embedded/embedded_session.rb +0 -203
  39. data/lib/neo4j-embedded/embedded_transaction.rb +0 -30
  40. data/lib/neo4j-embedded/label.rb +0 -66
  41. data/lib/neo4j-embedded/property.rb +0 -106
  42. data/lib/neo4j-embedded/to_java.rb +0 -44
  43. data/lib/neo4j-server.rb +0 -12
  44. data/lib/neo4j-server/cypher_label.rb +0 -35
  45. data/lib/neo4j-server/cypher_node.rb +0 -221
  46. data/lib/neo4j-server/cypher_relationship.rb +0 -142
  47. data/lib/neo4j-server/cypher_response.rb +0 -248
  48. data/lib/neo4j-server/cypher_session.rb +0 -263
  49. data/lib/neo4j-server/cypher_transaction.rb +0 -100
  50. data/lib/neo4j-server/label.rb +0 -40
  51. data/lib/neo4j-server/resource.rb +0 -57
  52. data/lib/neo4j/entity_equality.rb +0 -8
  53. data/lib/neo4j/entity_marshal.rb +0 -20
  54. data/lib/neo4j/label.rb +0 -90
  55. data/lib/neo4j/node.rb +0 -216
  56. data/lib/neo4j/property_container.rb +0 -17
  57. data/lib/neo4j/property_validator.rb +0 -22
  58. data/lib/neo4j/relationship.rb +0 -161
  59. data/lib/neo4j/session.rb +0 -222
@@ -1,100 +0,0 @@
1
- module Neo4j
2
- module Server
3
- # The CypherTransaction object lifecycle is as follows:
4
- # * It is initialized with the transactional endpoint URL and the connection object to use for communication. It does not communicate with the server to create this.
5
- # * The first query within the transaction sets the commit and execution addresses, :commit_url and :query_url.
6
- # * At any time, `failure` can be called to mark a transaction failed and trigger a rollback upon closure.
7
- # * `close` is called to end the transaction. It calls `commit` or `delete`.
8
- #
9
- # If a transaction is created and then closed without performing any queries, an OpenStruct is returned that behaves like a successfully closed query.
10
- class CypherTransaction < Neo4j::Transaction::Base
11
- include Resource
12
-
13
- attr_reader :commit_url, :query_url
14
-
15
- def connection
16
- @session.connection
17
- end
18
-
19
- def base_url
20
- require 'uri'
21
- URI(@session.instance_variable_get('@resource_url')).tap do |uri|
22
- uri.path = ''
23
- end.to_s
24
- end
25
-
26
- ROW_REST = %w[row REST]
27
- def _query(cypher_query, params = nil)
28
- fail 'Transaction expired, unable to perform query' if expired?
29
- statement = {statement: cypher_query, parameters: params, resultDataContents: ROW_REST}
30
- body = {statements: [statement]}
31
-
32
- response = @query_url ? query(body) : start(body)
33
-
34
- create_cypher_response(response)
35
- end
36
-
37
- def start(body)
38
- request(:post, start_url, 201, body).tap do |response|
39
- @commit_url = response.body[:commit]
40
- @query_url = response.headers[:Location]
41
-
42
- fail "NO ENDPOINT URL #{connection} : HEAD: #{response.headers.inspect}" if !@query_url || @query_url.empty?
43
-
44
- init_resource_data(response.body, base_url)
45
- end
46
- end
47
-
48
- def start_url
49
- @session.resource_data.fetch(:transaction) || base_url
50
- end
51
-
52
- def query(body)
53
- request(:post, @query_url, 200, body)
54
- end
55
-
56
- EMPTY_RESPONSE = OpenStruct.new(status: 200, body: '')
57
-
58
- def delete
59
- return EMPTY_RESPONSE if !@commit_url || expired?
60
-
61
- request(:delete, @query_url, 200, nil, resource_headers)
62
- end
63
-
64
- def commit
65
- return EMPTY_RESPONSE if !@commit_url || expired?
66
-
67
- request(:post, @commit_url, 200, nil, resource_headers)
68
- end
69
-
70
- private
71
-
72
- def request(action, endpoint_url, expected_code = 200, body = nil, headers = {})
73
- connection.send(action, endpoint_url, body, headers).tap do |response|
74
- expect_response_code!(response, expected_code)
75
- end
76
- end
77
-
78
- def create_cypher_response(response)
79
- CypherResponse.new(response, true).tap do |cypher_response|
80
- if response.body[:errors].empty?
81
- cypher_response.set_data(response.body[:results][0])
82
- else
83
- first_error = response.body[:errors].first
84
- tx_cleanup!(first_error)
85
- cypher_response.set_error(first_error)
86
- end
87
- end
88
- end
89
-
90
- def tx_cleanup!(first_error)
91
- autoclosed!
92
- mark_expired if first_error[:message] =~ /Unrecognized transaction id/
93
- end
94
-
95
- def empty_response
96
- OpenStruct.new(status: 200, body: '')
97
- end
98
- end
99
- end
100
- end
@@ -1,40 +0,0 @@
1
- module Neo4j
2
- class Label
3
- class << self
4
- def constraints(session = Neo4j::Session.current)
5
- session.connection.get(CONSTRAINT_PATH).body
6
- end
7
-
8
- def constraint?(label_name, property, session = Neo4j::Session.current)
9
- label_constraints = session.connection.get("#{CONSTRAINT_PATH}/#{label_name}").body
10
- !label_constraints.select { |c| c[:label] == label_name.to_s && c[:property_keys].first == property.to_s }.empty?
11
- end
12
-
13
- def indexes(session = Neo4j::Session.current)
14
- session.connection.get(INDEX_PATH).body
15
- end
16
-
17
- def index?(label_name, property, session = Neo4j::Session.current)
18
- label_indexes = session.connection.get("#{INDEX_PATH}/#{label_name}").body
19
- !label_indexes.select { |i| i[:label] == label_name.to_s && i[:property_keys].first == property.to_s }.empty?
20
- end
21
-
22
- def drop_all_indexes(session = Neo4j::Session.current)
23
- indexes.each do |i|
24
- begin
25
- session._query_or_fail("DROP INDEX ON :`#{i[:label]}`(#{i[:property_keys].first})")
26
- rescue Neo4j::Server::CypherResponse::ResponseError
27
- # This will error on each constraint. Ignore and continue.
28
- next
29
- end
30
- end
31
- end
32
-
33
- def drop_all_constraints(session = Neo4j::Session.current)
34
- constraints.each do |c|
35
- session._query_or_fail("DROP CONSTRAINT ON (n:`#{c[:label]}`) ASSERT n.`#{c[:property_keys].first}` IS UNIQUE")
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,57 +0,0 @@
1
- module Neo4j
2
- module Server
3
- module Resource
4
- class ServerException < Exception
5
- end
6
-
7
- attr_reader :resource_data, :resource_url
8
-
9
- def init_resource_data(resource_data, resource_url)
10
- fail "Exception #{resource_data[:exception]}" if resource_data[:exception]
11
- fail "Expected @resource_data to be Hash got #{resource_data.inspect}" unless resource_data.respond_to?(:[])
12
-
13
- @resource_data = resource_data
14
- @resource_url = resource_url
15
-
16
- self
17
- end
18
-
19
- def wrap_resource(connection)
20
- CypherTransaction.new(resource_url(:transaction), connection)
21
- end
22
-
23
- def resource_url(key = nil)
24
- return @resource_url if key.nil?
25
- resource_data.fetch key
26
- rescue KeyError
27
- raise "No resource key '#{key}', available #{@resource_data.keys.inspect}"
28
- end
29
-
30
- def expect_response_code!(response, expected_code, msg = 'Error for request')
31
- handle_response_error!(response, "Expected response code #{expected_code} #{msg}") unless response.status == expected_code
32
- response
33
- end
34
-
35
- def handle_response_error!(response, msg = 'Error for request')
36
- fail ServerException, "#{msg} #{response.env && response.env[:url].to_s}, #{response.status}"
37
- end
38
-
39
- def response_exception(response)
40
- return nil if response.body.nil? || response.body.empty?
41
- JSON.parse(response.body)[:exception]
42
- end
43
-
44
- def resource_headers
45
- {'Content-Type' => 'application/json', 'Accept' => 'application/json; charset=UTF-8', 'User-Agent' => ::Neo4j::Session.user_agent_string}
46
- end
47
-
48
- def resource_url_id(url = resource_url)
49
- url.match(%r{/(\d+)$})[1].to_i
50
- end
51
-
52
- def convert_from_json_value(value)
53
- JSON.parse(value, quirks_mode: true)
54
- end
55
- end
56
- end
57
- end
@@ -1,8 +0,0 @@
1
- module Neo4j
2
- module EntityEquality
3
- def ==(other)
4
- other.class == self.class && other.neo_id == neo_id
5
- end
6
- alias eql? ==
7
- end
8
- end
@@ -1,20 +0,0 @@
1
- module Neo4j
2
- # To support Ruby marshaling
3
- module EntityMarshal
4
- def marshal_dump
5
- marshal_instance_variables.map(&method(:instance_variable_get))
6
- end
7
-
8
- def marshal_load(array)
9
- marshal_instance_variables.zip(array).each do |var, value|
10
- instance_variable_set(var, value)
11
- end
12
- end
13
-
14
- private
15
-
16
- def marshal_instance_variables
17
- self.class::MARSHAL_INSTANCE_VARIABLES
18
- end
19
- end
20
- end
@@ -1,90 +0,0 @@
1
- module Neo4j
2
- # A label is a named graph construct that is used to group nodes.
3
- # See Neo4j::Node how to create and delete nodes
4
- # @see http://docs.neo4j.org/chunked/milestone/graphdb-neo4j-labels.html
5
- class Label
6
- class InvalidQueryError < StandardError; end
7
-
8
- # @abstract
9
- def name
10
- fail 'not implemented'
11
- end
12
-
13
- # @abstract
14
- def create_index(*properties)
15
- fail 'not implemented'
16
- end
17
-
18
- # @abstract
19
- def drop_index(*properties)
20
- fail 'not implemented'
21
- end
22
-
23
- # List indices for a label
24
- # @abstract
25
- def indexes
26
- fail 'not implemented'
27
- end
28
-
29
- # Creates a neo4j constraint on a property
30
- # See http://docs.neo4j.org/chunked/stable/query-constraints.html
31
- # @example
32
- # label = Neo4j::Label.create(:person, session)
33
- # label.create_constraint(:name, {type: :unique}, session)
34
- #
35
- def create_constraint(property, constraints, session = Neo4j::Session.current)
36
- cypher = case constraints[:type]
37
- when :unique
38
- "CREATE CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
39
- else
40
- fail "Not supported constrain #{constraints.inspect} for property #{property} (expected :type => :unique)"
41
- end
42
- session._query_or_fail(cypher)
43
- end
44
-
45
- # Drops a neo4j constraint on a property
46
- # See http://docs.neo4j.org/chunked/stable/query-constraints.html
47
- # @example
48
- # label = Neo4j::Label.create(:person, session)
49
- # label.create_constraint(:name, {type: :unique}, session)
50
- # label.drop_constraint(:name, {type: :unique}, session)
51
- #
52
- def drop_constraint(property, constraint, session = Neo4j::Session.current)
53
- cypher = case constraint[:type]
54
- when :unique
55
- "DROP CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
56
- else
57
- fail "Not supported constrain #{constraint.inspect}"
58
- end
59
- session._query_or_fail(cypher)
60
- end
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
-
69
- class << self
70
- INDEX_PATH = '/db/data/schema/index/'
71
- CONSTRAINT_PATH = '/db/data/schema/constraint/'
72
-
73
- # Returns a label of given name that can be used to specifying constraints
74
- # @param [Symbol,String] name the name of the label
75
- def create(name, session = Neo4j::Session.current)
76
- session.create_label(name)
77
- end
78
-
79
- # @return [Enumerable<Neo4j::Node>] all nodes having given label. Nodes can be wrapped in your own model ruby classes.
80
- def find_all_nodes(label_name, session = Neo4j::Session.current)
81
- session.find_all_nodes(label_name)
82
- end
83
-
84
- # @return [Enumerable<Neo4j::Node>] all nodes having given label and properties. Nodes can be wrapped in your own model ruby classes.
85
- def find_nodes(label_name, key, value, session = Neo4j::Session.current)
86
- session.find_nodes(label_name, key, value)
87
- end
88
- end
89
- end
90
- end
@@ -1,216 +0,0 @@
1
- module Neo4j
2
- # The base class for both the Embedded and Server Neo4j Node
3
- # Notice this class is abstract and can't be instantiated
4
- class Node
5
- # A module that allows plugins to register wrappers around Neo4j::Node objects
6
- module Wrapper
7
- # Used by Neo4j::NodeMixin to wrap nodes
8
- def wrapper
9
- self
10
- end
11
-
12
- def neo4j_obj
13
- self
14
- end
15
- end
16
-
17
- include EntityEquality
18
- include Wrapper
19
- include PropertyContainer
20
- include EntityMarshal
21
-
22
- # @return [Hash<Symbol, Object>] all properties of the node
23
- def props
24
- fail 'not implemented'
25
- end
26
-
27
- # replace all properties with new properties
28
- # @param [Hash<Symbol, Object>] properties a hash of properties the node should have
29
- def props=(properties)
30
- fail 'not implemented'
31
- end
32
-
33
- # Refresh the properties by reading it from the database again next time an property value is requested.
34
- def refresh
35
- fail 'not implemented'
36
- end
37
-
38
- # Updates the properties, keeps old properties
39
- # @param [Hash<Symbol, Object>] properties hash of properties that should be updated on the node
40
- def update_props(properties)
41
- fail 'not implemented'
42
- end
43
-
44
- # Directly remove the property on the node (low level method, may need transaction)
45
- def remove_property(key)
46
- fail 'not implemented'
47
- end
48
-
49
- # Directly set the property on the node (low level method, may need transaction)
50
- # @param [Symbol, String] key
51
- # @param value see Neo4j::PropertyValidator::VALID_PROPERTY_VALUE_CLASSES for valid values
52
- def set_property(key, value)
53
- fail 'not implemented'
54
- end
55
-
56
- # Directly get the property on the node (low level method, may need transaction)
57
- # @param [Symbol, String] key
58
- # @return the value of the key
59
- def get_property(key, value)
60
- fail 'not implemented'
61
- end
62
-
63
- # Creates a relationship of given type to other_node with optionally properties
64
- # @param [Symbol] type the type of the relation between the two nodes
65
- # @param [Neo4j::Node] other_node the other node
66
- # @param [Hash<Symbol, Object>] props optionally properties for the created relationship
67
- def create_rel(type, other_node, props = nil)
68
- fail 'not implemented'
69
- end
70
-
71
-
72
- # Returns an enumeration of relationships.
73
- # It always returns relationships of depth one.
74
- #
75
- # @param [Hash] match the options to create a message with.
76
- # @option match [Symbol] :dir dir the direction of the relationship, allowed values: :both, :incoming, :outgoing.
77
- # @option match [Symbol] :type the type of relationship to navigate
78
- # @option match [Symbol] :between return all the relationships between this and given node
79
- # @return [Enumerable<Neo4j::Relationship>] of Neo4j::Relationship objects
80
- #
81
- # @example Return both incoming and outgoing relationships of any type
82
- # node_a.rels
83
- #
84
- # @example All outgoing or incoming relationship of type friends
85
- # node_a.rels(type: :friends)
86
- #
87
- # @example All outgoing relationships between me and another node of type friends
88
- # node_a.rels(type: :friends, dir: :outgoing, between: node_b)
89
- #
90
- def rels(match = {dir: :both})
91
- fail 'not implemented'
92
- end
93
-
94
- # Adds one or more Neo4j labels on the node
95
- # @param [Array<Symbol>] labels one or more labels to add
96
- def add_label(*labels)
97
- fail 'not implemented'
98
- end
99
-
100
- # Sets label on the node. Any old labels will be removed
101
- # @param [Array<Symbol>] labels one or more labels to set
102
- def set_label(*labels)
103
- fail 'not implemented'
104
- end
105
-
106
- # Removes given labels
107
- def remove_label(*labels)
108
- fail 'not implemented'
109
- end
110
-
111
- #
112
- # @return [Array<Symbol>]all labels on the node
113
- def labels
114
- fail 'not implemented'
115
- end
116
-
117
- # Deletes this node from the database
118
- def del
119
- fail 'not implemented'
120
- end
121
-
122
- # @return [Boolean] true if the node exists in the database
123
- def exist?
124
- fail 'not implemented'
125
- end
126
-
127
- # Returns the only node of a given type and direction that is attached to this node, or nil.
128
- # This is a convenience method that is used in the commonly occuring situation where a node has exactly zero or one relationships of a given type and direction to another node.
129
- # Typically this invariant is maintained by the rest of the code: if at any time more than one such relationships exist, it is a fatal error that should generate an exception.
130
- #
131
- # This method reflects that semantics and returns either:
132
- # * nil if there are zero relationships of the given type and direction,
133
- # * the relationship if there's exactly one, or
134
- # * throws an exception in all other cases.
135
- #
136
- # This method should be used only in situations with an invariant as described above. In those situations, a "state-checking" method (e.g. #rel?) is not required,
137
- # because this method behaves correctly "out of the box."
138
- #
139
- # @param (see #rel)
140
- def node(specs = {})
141
- fail 'not implemented'
142
- end
143
-
144
- # Same as #node but returns the relationship. Notice it may raise an exception if there are more then one relationship matching.
145
- def rel(spec = {})
146
- fail 'not implemented'
147
- end
148
-
149
- def _rel(spec = {})
150
- fail 'not implemented'
151
- end
152
-
153
- # Returns true or false if there is one or more relationships
154
- # @return [Boolean]
155
- def rel?(spec = {})
156
- fail 'not implemented'
157
- end
158
-
159
- # Works like #rels method but instead returns the nodes.
160
- # It does try to load a Ruby wrapper around each node
161
- # @abstract
162
- # @param (see #rels)
163
- # @return [Enumerable<Neo4j::Node>] an Enumeration of either Neo4j::Node objects or wrapped Neo4j::Node objects
164
- # @note it's possible that the same node is returned more than once because of several relationship reaching to the same node, see #outgoing for alternative
165
- def nodes(specs = {})
166
- # rels(specs).map{|n| n.other_node(self)}
167
- end
168
-
169
- class << self
170
- # Creates a node
171
- def create(props = nil, *labels_or_db)
172
- session = if labels_or_db.last.is_a?(Neo4j::Session)
173
- labels_or_db.pop
174
- else
175
- Neo4j::Session.current!
176
- end
177
-
178
- session.create_node(props, labels_or_db)
179
- end
180
-
181
- # Loads a node from the database with given id
182
- def load(neo_id, session = Neo4j::Session.current!)
183
- node = _load(neo_id, session)
184
- node && node.wrapper
185
- end
186
-
187
- # Same as #load but does not try to return a wrapped node
188
- # @return [Neo4j::Node] an unwrapped node
189
- def _load(neo_id, session = Neo4j::Session.current!)
190
- session.load_node(neo_id)
191
- end
192
-
193
- # Find the node with given label and value
194
- def find_nodes(label, value = nil, session = Neo4j::Session.current!)
195
- session.find_nodes(label, value)
196
- end
197
-
198
- def validate_match!(match)
199
- invalid_match_keys = match.keys - %i[type dir between]
200
- fail "Invalid match keys: #{invalid_match_keys.inspect}" if !invalid_match_keys.empty?
201
-
202
- fail "Invalid dir: #{match[:dir]}" if ![nil, :incoming, :outgoing, :both].include?(match[:dir])
203
- end
204
- end
205
-
206
- def initialize
207
- fail "Can't instantiate abstract class" if abstract_class?
208
- end
209
-
210
- private
211
-
212
- def abstract_class?
213
- self.class == Node
214
- end
215
- end
216
- end