neo4j-core 8.1.4 → 9.0.0.alpha.1

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