activegraph 11.5.0.alpha.1 → 12.0.0.beta.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/CHANGELOG.md +20 -1
- data/Gemfile +0 -4
- data/activegraph.gemspec +3 -4
- data/lib/active_graph/base.rb +4 -0
- data/lib/active_graph/core/element.rb +142 -0
- data/lib/active_graph/core/entity.rb +4 -0
- data/lib/active_graph/core/label.rb +5 -166
- data/lib/active_graph/core/node.rb +0 -4
- data/lib/active_graph/core/query_clauses.rb +4 -4
- data/lib/active_graph/core/schema.rb +23 -28
- data/lib/active_graph/core/type.rb +13 -0
- data/lib/active_graph/migrations/helpers/schema.rb +10 -13
- data/lib/active_graph/model_schema.rb +1 -1
- data/lib/active_graph/node/has_n.rb +6 -2
- data/lib/active_graph/node/labels.rb +15 -12
- data/lib/active_graph/node/persistence.rb +0 -11
- data/lib/active_graph/node/query/query_proxy/link.rb +15 -4
- data/lib/active_graph/node/query/query_proxy.rb +3 -3
- data/lib/active_graph/node/query/query_proxy_eager_loading.rb +1 -1
- data/lib/active_graph/node/query/query_proxy_enumerable.rb +4 -4
- data/lib/active_graph/node/query/query_proxy_methods.rb +14 -16
- data/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb +2 -5
- data/lib/active_graph/node/query.rb +1 -1
- data/lib/active_graph/node/query_methods.rb +9 -12
- data/lib/active_graph/railtie.rb +13 -0
- data/lib/active_graph/relationship/initialize.rb +2 -2
- data/lib/active_graph/relationship/persistence.rb +3 -1
- data/lib/active_graph/relationship/property.rb +1 -5
- data/lib/active_graph/relationship/query.rb +14 -9
- data/lib/active_graph/relationship/related_node.rb +4 -8
- data/lib/active_graph/relationship/types.rb +8 -0
- data/lib/active_graph/relationship/wrapping.rb +1 -1
- data/lib/active_graph/relationship.rb +4 -1
- data/lib/active_graph/shared/identity.rb +6 -3
- data/lib/active_graph/shared/persistence.rb +12 -1
- data/lib/active_graph/shared/query_factory.rb +1 -1
- data/lib/active_graph/shared/type_converters.rb +3 -2
- data/lib/active_graph/transactions.rb +4 -0
- data/lib/active_graph/version.rb +1 -1
- data/lib/active_graph.rb +2 -14
- data/lib/{active_graph/generators → rails/generators/active_graph/migration}/migration_generator.rb +5 -2
- data/lib/{active_graph/generators → rails/generators/active_graph/model}/model_generator.rb +5 -2
- data/lib/{active_graph/generators → rails/generators/active_graph/upgrade_v8}/upgrade_v8_generator.rb +5 -2
- data/lib/{active_graph → rails}/generators/migration_helper.rb +57 -0
- data/lib/{active_graph → rails}/generators/source_path_helper.rb +1 -1
- metadata +34 -54
- data/lib/active_graph/generators/active_model.rb +0 -33
- data/lib/active_graph/generators/generated_attribute.rb +0 -17
- /data/lib/{active_graph/generators → rails/generators/active_graph}/migration/templates/migration.erb +0 -0
- /data/lib/{active_graph/generators → rails/generators/active_graph}/model/templates/migration.erb +0 -0
- /data/lib/{active_graph/generators → rails/generators/active_graph}/model/templates/model.erb +0 -0
- /data/lib/{active_graph/generators → rails/generators/active_graph}/upgrade_v8/templates/migration.erb +0 -0
@@ -108,7 +108,7 @@ module ActiveGraph
|
|
108
108
|
|
109
109
|
# Deletes all nodes and connected relationships from Cypher.
|
110
110
|
def delete_all
|
111
|
-
neo4j_query("MATCH (n:`#{mapped_label_name}`)
|
111
|
+
neo4j_query("MATCH (n:`#{mapped_label_name}`) DETACH DELETE n")
|
112
112
|
end
|
113
113
|
|
114
114
|
# Returns each node to Ruby and calls `destroy`. Be careful, as this can be a very slow operation if you have many nodes. It will generate at least
|
@@ -127,11 +127,15 @@ module ActiveGraph
|
|
127
127
|
@mapped_label_name || label_for_model
|
128
128
|
end
|
129
129
|
|
130
|
+
alias mapped_element_name mapped_label_name
|
131
|
+
|
130
132
|
# @return [ActiveGraph::Label] the label for this class
|
131
133
|
def mapped_label
|
132
134
|
ActiveGraph::Core::Label.new(mapped_label_name)
|
133
135
|
end
|
134
136
|
|
137
|
+
alias mapped_element mapped_label
|
138
|
+
|
135
139
|
def base_class
|
136
140
|
unless self < ActiveGraph::Node
|
137
141
|
fail "#{name} doesn't belong in a hierarchy descending from Node"
|
@@ -160,6 +164,7 @@ module ActiveGraph
|
|
160
164
|
|
161
165
|
self.mapped_label_name = name
|
162
166
|
end
|
167
|
+
|
163
168
|
# rubocop:enable Naming/AccessorMethodName
|
164
169
|
|
165
170
|
private
|
@@ -184,20 +189,18 @@ module ActiveGraph
|
|
184
189
|
end
|
185
190
|
|
186
191
|
def label_for_model
|
187
|
-
|
192
|
+
name.nil? ? object_id.to_s.to_sym : decorated_label_name
|
188
193
|
end
|
189
194
|
|
190
195
|
def decorated_label_name
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
name.to_sym
|
196
|
+
case ActiveGraph::Config[:module_handling]
|
197
|
+
when :demodulize
|
198
|
+
name.demodulize
|
199
|
+
when Proc
|
200
|
+
ActiveGraph::Config[:module_handling].call name
|
201
|
+
else
|
202
|
+
name
|
203
|
+
end.to_sym
|
201
204
|
end
|
202
205
|
end
|
203
206
|
end
|
@@ -70,17 +70,6 @@ module ActiveGraph::Node
|
|
70
70
|
neo4j_query(query, {props: node_props}, wrap: false).to_a[0][:n]
|
71
71
|
end
|
72
72
|
|
73
|
-
# As the name suggests, this inserts the primary key (id property) into the properties hash.
|
74
|
-
# The method called here, `default_property_values`, is a holdover from an earlier version of the gem. It does NOT
|
75
|
-
# contain the default values of properties, it contains the Default Property, which we now refer to as the ID Property.
|
76
|
-
# It will be deprecated and renamed in a coming refactor.
|
77
|
-
# @param [Hash] converted_props A hash of properties post-typeconversion, ready for insertion into the DB.
|
78
|
-
def inject_primary_key!(converted_props)
|
79
|
-
self.class.default_property_values(self).tap do |destination_props|
|
80
|
-
destination_props.merge!(converted_props) if converted_props.is_a?(Hash)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
73
|
# @return [Array] Labels to be set on the node during a create event
|
85
74
|
def labels_for_create
|
86
75
|
self.class.mapped_label_names
|
@@ -147,8 +147,8 @@ module ActiveGraph
|
|
147
147
|
|
148
148
|
val = if !model
|
149
149
|
value
|
150
|
-
elsif key == model.id_property_name
|
151
|
-
value
|
150
|
+
elsif key == model.id_property_name
|
151
|
+
try_id(value)
|
152
152
|
else
|
153
153
|
converted_value(model, key, value)
|
154
154
|
end
|
@@ -156,13 +156,24 @@ module ActiveGraph
|
|
156
156
|
new(:where, ->(v, _) { {v => {key => val}} })
|
157
157
|
end
|
158
158
|
|
159
|
+
private def try_id(value)
|
160
|
+
case value
|
161
|
+
when Shared::Identity
|
162
|
+
value.id
|
163
|
+
when Enumerable
|
164
|
+
value.map(&method(:try_id))
|
165
|
+
else
|
166
|
+
value
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
159
170
|
def for_association(name, value, n_string, model)
|
160
171
|
neo_id = value.try(:neo_id) || value
|
161
|
-
fail ArgumentError, "Invalid value for '#{name}' condition"
|
172
|
+
fail ArgumentError, "Invalid value for '#{name}' condition" unless neo_id.is_a?(String)
|
162
173
|
|
163
174
|
[
|
164
175
|
new(:match, ->(v, _) { "(#{v})#{model.associations[name].arrow_cypher}(#{n_string})" }),
|
165
|
-
new(:where, ->(_, _) { {"
|
176
|
+
new(:where, ->(_, _) { {"elementId(#{n_string})" => neo_id} })
|
166
177
|
]
|
167
178
|
end
|
168
179
|
|
@@ -223,7 +223,7 @@ module ActiveGraph
|
|
223
223
|
|
224
224
|
ActiveGraph::Base.transaction do
|
225
225
|
other_nodes.each do |other_node|
|
226
|
-
if other_node.
|
226
|
+
if other_node.element_id
|
227
227
|
other_node.try(:delete_reverse_has_one_core_rel, association)
|
228
228
|
else
|
229
229
|
other_node.save
|
@@ -238,7 +238,7 @@ module ActiveGraph
|
|
238
238
|
|
239
239
|
def _nodeify!(*args)
|
240
240
|
other_nodes = [args].flatten!.map! do |arg|
|
241
|
-
|
241
|
+
arg.is_a?(String) ? @model.find_by(id: arg) : arg
|
242
242
|
end.compact
|
243
243
|
|
244
244
|
if @model && other_nodes.any? { |other_node| !other_node.class.mapped_label_names.include?(@model.mapped_label_name) }
|
@@ -351,7 +351,7 @@ module ActiveGraph
|
|
351
351
|
fail 'Crazy error' if !(start_object || @query_proxy)
|
352
352
|
|
353
353
|
if start_object
|
354
|
-
:"#{start_object.class.name.gsub('::', '_').downcase}#{start_object.neo_id}"
|
354
|
+
:"#{start_object.class.name.gsub('::', '_').downcase}#{start_object.neo_id&.gsub(/[:\-]/, '_')}"
|
355
355
|
else
|
356
356
|
@query_proxy.node_var || :"node#{_chain_level}"
|
357
357
|
end
|
@@ -62,7 +62,7 @@ module ActiveGraph
|
|
62
62
|
if rel.is_a?(ActiveGraph::Relationship)
|
63
63
|
rel.instance_variable_set(direction == :in ? '@from_node' : '@to_node', node)
|
64
64
|
end
|
65
|
-
@_cache[direction == :out ? rel.
|
65
|
+
@_cache[direction == :out ? rel.start_node_element_id : rel.end_node_element_id]
|
66
66
|
.association_proxy(element.name).add_to_cache(node, rel)
|
67
67
|
end
|
68
68
|
|
@@ -62,22 +62,22 @@ module ActiveGraph
|
|
62
62
|
# Does exactly what you would hope. Without it, comparing `bobby.lessons == sandy.lessons` would evaluate to false because it
|
63
63
|
# would be comparing the QueryProxy objects, not the lessons themselves.
|
64
64
|
def ==(other)
|
65
|
-
|
65
|
+
to_a == other
|
66
66
|
end
|
67
67
|
|
68
68
|
# For getting variables which have been defined as part of the association chain
|
69
69
|
def pluck(*args)
|
70
|
-
transformable_attributes = (model ? model.attribute_names : []) + %w(uuid neo_id)
|
70
|
+
transformable_attributes = (model ? model.attribute_names + [model.id_property_name.to_s] : []) + %w(uuid neo_id)
|
71
71
|
arg_list = args.map do |arg|
|
72
72
|
arg = ActiveGraph::Node::Query::QueryProxy::Link.converted_key(model, arg)
|
73
73
|
if transformable_attributes.include?(arg.to_s)
|
74
|
-
{identity => arg}
|
74
|
+
{ identity => arg }
|
75
75
|
else
|
76
76
|
arg
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
|
80
|
+
query.pluck(*arg_list)
|
81
81
|
end
|
82
82
|
|
83
83
|
protected
|
@@ -88,7 +88,7 @@ module ActiveGraph
|
|
88
88
|
def include?(other, target = nil)
|
89
89
|
query_with_target(target) do |var|
|
90
90
|
where_filter = if other.respond_to?(:neo_id) || association_id_key == :neo_id
|
91
|
-
"
|
91
|
+
"elementId(#{var}) = $other_node_id"
|
92
92
|
else
|
93
93
|
"#{var}.#{association_id_key} = $other_node_id"
|
94
94
|
end
|
@@ -99,12 +99,12 @@ module ActiveGraph
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def exists?(node_condition = nil, target = nil)
|
102
|
-
unless [
|
102
|
+
unless [String, Hash, NilClass].any? { |c| node_condition.is_a?(c) }
|
103
103
|
fail(ActiveGraph::InvalidParameterError, ':exists? only accepts ids or conditions')
|
104
104
|
end
|
105
105
|
query_with_target(target) do |var|
|
106
106
|
start_q = exists_query_start(node_condition, var)
|
107
|
-
result = start_q.query.reorder.return("
|
107
|
+
result = start_q.query.reorder.return("elementId(#{var}) AS proof_of_life LIMIT 1").first
|
108
108
|
!!result
|
109
109
|
end
|
110
110
|
end
|
@@ -119,9 +119,9 @@ module ActiveGraph
|
|
119
119
|
def match_to(node)
|
120
120
|
first_node = node.is_a?(Array) ? node.first : node
|
121
121
|
where_arg = if first_node.respond_to?(:neo_id)
|
122
|
-
{neo_id: node.is_a?(Array) ? node.map(&:neo_id) : node}
|
122
|
+
{ neo_id: node.is_a?(Array) ? node.map(&:neo_id) : node }
|
123
123
|
elsif !node.nil?
|
124
|
-
{association_id_key => node.is_a?(Array) ? ids_array(node) : node}
|
124
|
+
{ association_id_key => node.is_a?(Array) ? ids_array(node) : node }
|
125
125
|
else
|
126
126
|
# support for null object pattern
|
127
127
|
'1 = 2'
|
@@ -130,7 +130,6 @@ module ActiveGraph
|
|
130
130
|
self.where(where_arg)
|
131
131
|
end
|
132
132
|
|
133
|
-
|
134
133
|
# Gives you the first relationship between the last link of a QueryProxy chain and a given node
|
135
134
|
# Shorthand for `MATCH (start)-[r]-(other_node) WHERE ID(other_node) = #{other_node.neo_id} RETURN r`
|
136
135
|
# @param [#neo_id, String, Enumerable] node An object to be sent to `match_to`. See params for that method.
|
@@ -145,6 +144,7 @@ module ActiveGraph
|
|
145
144
|
def rels_to(node)
|
146
145
|
self.match_to(node).pluck(rel_var)
|
147
146
|
end
|
147
|
+
|
148
148
|
alias all_rels_to rels_to
|
149
149
|
|
150
150
|
# When called, this method returns a single node that satisfies the match specified in the params hash.
|
@@ -260,7 +260,7 @@ module ActiveGraph
|
|
260
260
|
[self.query.with(identity),
|
261
261
|
proc { |var| "#{func}(COLLECT(#{var})) as #{var}" }]
|
262
262
|
else
|
263
|
-
ord_prop = (func == LAST ? {order_property => :DESC} : order_property)
|
263
|
+
ord_prop = (func == LAST ? { order_property => :DESC } : order_property)
|
264
264
|
[self.order(ord_prop).limit(1),
|
265
265
|
proc { |var| var }]
|
266
266
|
end
|
@@ -285,16 +285,14 @@ module ActiveGraph
|
|
285
285
|
yield(target || identity)
|
286
286
|
end
|
287
287
|
|
288
|
-
def exists_query_start(condition, target)
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
when String
|
295
|
-
self.where(model.primary_key => condition)
|
288
|
+
def exists_query_start(condition, target = nil)
|
289
|
+
return self unless condition
|
290
|
+
return exists_query_start(model.primary_key => condition) if condition.is_a?(String)
|
291
|
+
|
292
|
+
if condition.key?(:neo_id)
|
293
|
+
where("elementId(#{target}) = $neo_id").params(**condition.slice(:neo_id))
|
296
294
|
else
|
297
|
-
|
295
|
+
where(**condition)
|
298
296
|
end
|
299
297
|
end
|
300
298
|
end
|
@@ -25,11 +25,7 @@ module ActiveGraph
|
|
25
25
|
# @param identifier [String,Symbol] the optional identifier of the link in the chain to delete.
|
26
26
|
def delete_all(identifier = nil)
|
27
27
|
query_with_target(identifier) do |target|
|
28
|
-
|
29
|
-
self.query.with(target).optional_match("(#{target})-[#{target}_rel]-()").delete("#{target}, #{target}_rel").exec
|
30
|
-
rescue Neo4j::Driver::Exceptions::ClientException # <=- Seems hacky
|
31
|
-
self.query.delete(target).exec
|
32
|
-
end
|
28
|
+
query.detach_delete(target).exec
|
33
29
|
clear_source_object_cache
|
34
30
|
end
|
35
31
|
end
|
@@ -51,6 +47,7 @@ module ActiveGraph
|
|
51
47
|
def replace_with(node_or_nodes)
|
52
48
|
node_or_nodes = Array(node_or_nodes).map { |arg| arg.is_a?(ActiveGraph::Node) ? arg : @model.find(arg) }
|
53
49
|
original_ids = self.pluck(:id)
|
50
|
+
ActiveGraph::Base.lock_node(start_object) unless start_object.new_record?
|
54
51
|
delete_rels_for_nodes(original_ids, node_or_nodes.collect(&:id))
|
55
52
|
add_rels(node_or_nodes, original_ids)
|
56
53
|
end
|
@@ -16,7 +16,7 @@ module ActiveGraph
|
|
16
16
|
# @param node_var [Symbol, String] The variable name to specify in the query
|
17
17
|
# @return [ActiveGraph::Core::Query]
|
18
18
|
def query_as(node_var)
|
19
|
-
self.class.query_as(node_var, false).where("
|
19
|
+
self.class.query_as(node_var, false).where("elementId(#{node_var})" => neo_id)
|
20
20
|
end
|
21
21
|
|
22
22
|
# Starts a new QueryProxy with the starting identifier set to the given argument and QueryProxy source_object set to the node instance.
|
@@ -2,12 +2,12 @@ module ActiveGraph
|
|
2
2
|
module Node
|
3
3
|
module QueryMethods
|
4
4
|
def exists?(node_condition = nil)
|
5
|
-
unless [
|
5
|
+
unless [String, Hash, NilClass].any? { |c| node_condition.is_a?(c) }
|
6
6
|
fail(ActiveGraph::InvalidParameterError, ':exists? only accepts ids or conditions')
|
7
7
|
end
|
8
8
|
query_start = exists_query_start(node_condition)
|
9
9
|
start_q = query_start.respond_to?(:query_as) ? query_start.query_as(:n) : query_start
|
10
|
-
result = start_q.return('
|
10
|
+
result = start_q.return('elementId(n) AS proof_of_life LIMIT 1').first
|
11
11
|
!!result
|
12
12
|
end
|
13
13
|
|
@@ -18,7 +18,7 @@ module ActiveGraph
|
|
18
18
|
|
19
19
|
# Returns the last node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
|
20
20
|
def last
|
21
|
-
self.query_as(:n).limit(1).order(n: {primary_key => :desc}).pluck(:n).first
|
21
|
+
self.query_as(:n).limit(1).order(n: { primary_key => :desc }).pluck(:n).first
|
22
22
|
end
|
23
23
|
|
24
24
|
# @return [Integer] number of nodes of this class
|
@@ -51,16 +51,13 @@ module ActiveGraph
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
-
def exists_query_start(
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
self.query_as(:n).where(n: {primary_key => node_condition})
|
60
|
-
when Hash
|
61
|
-
self.where(node_condition.keys.first => node_condition.values.first)
|
54
|
+
def exists_query_start(condition)
|
55
|
+
return exists_query_start(primary_key => condition) if condition&.is_a?(String)
|
56
|
+
|
57
|
+
if condition&.key?(:neo_id)
|
58
|
+
query_as(:n).where('elementId(n)' => condition[:neo_id])
|
62
59
|
else
|
63
|
-
|
60
|
+
where(**condition)
|
64
61
|
end
|
65
62
|
end
|
66
63
|
end
|
data/lib/active_graph/railtie.rb
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
require 'active_graph'
|
2
|
+
|
3
|
+
if defined?(Rails)
|
4
|
+
# Need the action_dispatch railtie to have action_dispatch.rescue_responses initialized correctly
|
5
|
+
require 'action_dispatch/railtie'
|
6
|
+
require 'rails/generators'
|
7
|
+
require 'rails/generators/active_model'
|
8
|
+
require 'rails/generators/named_base'
|
9
|
+
require 'rails/railtie'
|
10
|
+
require File.expand_path('../rails/generators/migration_helper.rb', __dir__)
|
11
|
+
Rails::Generators::GeneratedAttribute.include ActiveGraph::Generators::GeneratedAttribute
|
12
|
+
end
|
13
|
+
|
1
14
|
module ActiveGraph
|
2
15
|
class Railtie < ::Rails::Railtie
|
3
16
|
def empty_config
|
@@ -19,8 +19,8 @@ module ActiveGraph::Relationship
|
|
19
19
|
def init_on_reload(unwrapped_reloaded)
|
20
20
|
@attributes = nil
|
21
21
|
init_on_load(unwrapped_reloaded,
|
22
|
-
unwrapped_reloaded.
|
23
|
-
unwrapped_reloaded.
|
22
|
+
unwrapped_reloaded.start_node_element_id,
|
23
|
+
unwrapped_reloaded.end_node_element_id,
|
24
24
|
unwrapped_reloaded.type)
|
25
25
|
self
|
26
26
|
end
|
@@ -5,7 +5,9 @@ module ActiveGraph::Relationship
|
|
5
5
|
include ActiveGraph::Shared::Persistence
|
6
6
|
|
7
7
|
class RelInvalidError < RuntimeError; end
|
8
|
+
|
8
9
|
class ModelClassInvalidError < RuntimeError; end
|
10
|
+
|
9
11
|
class RelCreateFailedError < RuntimeError; end
|
10
12
|
|
11
13
|
def from_node_identifier
|
@@ -84,7 +86,7 @@ module ActiveGraph::Relationship
|
|
84
86
|
end
|
85
87
|
|
86
88
|
def query_as(neo_id, var = :r)
|
87
|
-
ActiveGraph::Base.new_query.match("()-[#{var}]->()").where(var => {neo_id:
|
89
|
+
ActiveGraph::Base.new_query.match("()-[#{var}]->()").where(var => { neo_id: })
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
@@ -15,7 +15,7 @@ module ActiveGraph::Relationship
|
|
15
15
|
alias end_node to_node
|
16
16
|
|
17
17
|
%w(start_node end_node).each do |direction|
|
18
|
-
define_method("#{direction}
|
18
|
+
define_method("#{direction}_element_id") { send(direction).neo_id if direction }
|
19
19
|
end
|
20
20
|
|
21
21
|
# @return [String] a string representing the relationship type that will be created
|
@@ -45,10 +45,6 @@ module ActiveGraph::Relationship
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
def id_property_name
|
49
|
-
false
|
50
|
-
end
|
51
|
-
|
52
48
|
%w(to_class from_class).each do |direction|
|
53
49
|
define_method(direction.to_s) do |argument = nil|
|
54
50
|
if !argument.nil?
|
@@ -6,18 +6,17 @@ module ActiveGraph::Relationship
|
|
6
6
|
|
7
7
|
module ClassMethods
|
8
8
|
# Returns the object with the specified neo4j id.
|
9
|
-
# @param [String
|
9
|
+
# @param [String] id of node to find
|
10
10
|
def find(id)
|
11
|
-
fail "Unknown argument #{id.class} in find method (expected String
|
12
|
-
find_by_id(id)
|
11
|
+
fail "Unknown argument #{id.class} in find method (expected String)" unless [Integer, String].any?(&id.method(:is_a?))
|
12
|
+
find_by_id(id) || fail(RecordNotFound.new("Couldn't find #{name} with 'id'=#{id.inspect}", name, id))
|
13
13
|
end
|
14
14
|
|
15
15
|
# Loads the relationship using its neo_id.
|
16
16
|
def find_by_id(key)
|
17
17
|
query = ActiveGraph::Base.new_query
|
18
|
-
result = query.match('()-[r]-()').where(
|
19
|
-
|
20
|
-
result[:r]
|
18
|
+
result = query.match('()-[r]-()').where("r.#{id_property_name}" => key).limit(1).return(:r).first
|
19
|
+
result&.send(:[], :r)
|
21
20
|
end
|
22
21
|
|
23
22
|
# Performs a very basic match on the relationship.
|
@@ -29,6 +28,10 @@ module ActiveGraph::Relationship
|
|
29
28
|
where_query.where(where_string(args)).pluck(:r1)
|
30
29
|
end
|
31
30
|
|
31
|
+
def find_by(args)
|
32
|
+
where(args).first
|
33
|
+
end
|
34
|
+
|
32
35
|
# Performs a basic match on the relationship, returning all results.
|
33
36
|
# This is not executed lazily, it will immediately return matching objects.
|
34
37
|
def all
|
@@ -36,11 +39,11 @@ module ActiveGraph::Relationship
|
|
36
39
|
end
|
37
40
|
|
38
41
|
def first
|
39
|
-
all_query.limit(1).order('
|
42
|
+
all_query.limit(1).order('r1.created_at').pluck(:r1).first
|
40
43
|
end
|
41
44
|
|
42
45
|
def last
|
43
|
-
all_query.limit(1).order('
|
46
|
+
all_query.limit(1).order('r1.created_at DESC').pluck(:r1).first
|
44
47
|
end
|
45
48
|
|
46
49
|
private
|
@@ -89,7 +92,9 @@ module ActiveGraph::Relationship
|
|
89
92
|
def where_string(args)
|
90
93
|
case args
|
91
94
|
when Hash
|
92
|
-
args.
|
95
|
+
args.transform_keys { |key| key == :neo_id ? 'elementId(r1)' : "r1.#{key}" }
|
96
|
+
.transform_values { |v| v.is_a?(Integer) ? v : "'#{v}'" }
|
97
|
+
.map { |k, v| "#{k} = #{v}" }.join(', ')
|
93
98
|
else
|
94
99
|
args
|
95
100
|
end
|
@@ -17,7 +17,7 @@ module ActiveGraph::Relationship
|
|
17
17
|
|
18
18
|
# Loads the node if needed, then conducts comparison.
|
19
19
|
def ==(other)
|
20
|
-
loaded if @node.is_a?(
|
20
|
+
loaded if @node.is_a?(String)
|
21
21
|
@node == other
|
22
22
|
end
|
23
23
|
|
@@ -62,15 +62,11 @@ module ActiveGraph::Relationship
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def method_missing(*args, **kwargs, &block)
|
65
|
-
|
66
|
-
loaded.send(*args, &block)
|
67
|
-
else
|
68
|
-
loaded.send(*args, **kwargs, &block)
|
69
|
-
end
|
65
|
+
loaded.send(*args, **kwargs, &block)
|
70
66
|
end
|
71
67
|
|
72
68
|
def respond_to_missing?(method_name, include_private = false)
|
73
|
-
loaded if @node.is_a?(
|
69
|
+
loaded if @node.is_a?(String)
|
74
70
|
@node.respond_to?(method_name) ? true : super
|
75
71
|
end
|
76
72
|
|
@@ -85,7 +81,7 @@ module ActiveGraph::Relationship
|
|
85
81
|
end
|
86
82
|
|
87
83
|
def valid_node_param?(node)
|
88
|
-
node.nil? || node.is_a?(
|
84
|
+
node.nil? || node.is_a?(String) || node.respond_to?(:neo_id)
|
89
85
|
end
|
90
86
|
end
|
91
87
|
end
|
@@ -10,6 +10,7 @@ module ActiveGraph
|
|
10
10
|
include ActiveGraph::Relationship::Initialize
|
11
11
|
include ActiveGraph::Shared::Identity
|
12
12
|
include ActiveGraph::Shared::Marshal
|
13
|
+
include ActiveGraph::Node::IdProperty
|
13
14
|
include ActiveGraph::Shared::SerializedProperties
|
14
15
|
include ActiveGraph::Relationship::Property
|
15
16
|
include ActiveGraph::Relationship::Persistence
|
@@ -24,6 +25,8 @@ module ActiveGraph
|
|
24
25
|
class FrozenRelError < ActiveGraph::Error; end
|
25
26
|
|
26
27
|
def initialize(from_node = nil, to_node = nil, args = nil)
|
28
|
+
self.class.ensure_id_property_info! # So that we make sure all objects have an id_property
|
29
|
+
|
27
30
|
load_nodes(node_or_nil(from_node), node_or_nil(to_node))
|
28
31
|
resolved_args = hash_or_nil(from_node, args)
|
29
32
|
symbol_args = sanitize_input_parameters(resolved_args)
|
@@ -58,7 +61,7 @@ module ActiveGraph
|
|
58
61
|
private
|
59
62
|
|
60
63
|
def node_or_nil(node)
|
61
|
-
node.is_a?(ActiveGraph::Node) || node.is_a?(
|
64
|
+
node.is_a?(ActiveGraph::Node) || node.is_a?(String) ? node : nil
|
62
65
|
end
|
63
66
|
|
64
67
|
def hash_or_nil(node_or_hash, hash_or_nil)
|
@@ -4,6 +4,7 @@ module ActiveGraph
|
|
4
4
|
def ==(other)
|
5
5
|
other.class == self.class && other.id == id
|
6
6
|
end
|
7
|
+
|
7
8
|
alias eql? ==
|
8
9
|
|
9
10
|
# Returns an Enumerable of all (primary) key attributes
|
@@ -12,11 +13,13 @@ module ActiveGraph
|
|
12
13
|
_persisted_obj ? [id] : nil
|
13
14
|
end
|
14
15
|
|
15
|
-
# @return [
|
16
|
-
def
|
17
|
-
_persisted_obj
|
16
|
+
# @return [String, nil] the neo4j id of the node if persisted or nil
|
17
|
+
def element_id
|
18
|
+
_persisted_obj&.element_id
|
18
19
|
end
|
19
20
|
|
21
|
+
alias neo_id element_id
|
22
|
+
|
20
23
|
def id
|
21
24
|
if self.class.id_property_name
|
22
25
|
send(self.class.id_property_name)
|
@@ -134,7 +134,7 @@ module ActiveGraph::Shared
|
|
134
134
|
def exist?
|
135
135
|
return if !_persisted_obj
|
136
136
|
|
137
|
-
neo4j_query(query_as(:n).return('
|
137
|
+
neo4j_query(query_as(:n).return('elementId(n)')).any?
|
138
138
|
end
|
139
139
|
|
140
140
|
# Returns +true+ if the object was destroyed.
|
@@ -223,6 +223,17 @@ module ActiveGraph::Shared
|
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
+
# As the name suggests, this inserts the primary key (id property) into the properties hash.
|
227
|
+
# The method called here, `default_property_values`, is a holdover from an earlier version of the gem. It does NOT
|
228
|
+
# contain the default values of properties, it contains the Default Property, which we now refer to as the ID Property.
|
229
|
+
# It will be deprecated and renamed in a coming refactor.
|
230
|
+
# @param [Hash] converted_props A hash of properties post-typeconversion, ready for insertion into the DB.
|
231
|
+
def inject_primary_key!(converted_props)
|
232
|
+
self.class.default_property_values(self).tap do |destination_props|
|
233
|
+
destination_props.merge!(converted_props) if converted_props.is_a?(Hash)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
226
237
|
protected
|
227
238
|
|
228
239
|
def increment_by_query!(match_query, attribute, by, element_name = :n)
|
@@ -47,7 +47,7 @@ module ActiveGraph::Shared
|
|
47
47
|
|
48
48
|
def match_query
|
49
49
|
base_query
|
50
|
-
.match(match_string).where("
|
50
|
+
.match(match_string).where("elementId(#{identifier}) = $#{identifier_id}")
|
51
51
|
.params(identifier_id.to_sym => graph_object.neo_id)
|
52
52
|
end
|
53
53
|
|
@@ -389,7 +389,7 @@ module ActiveGraph::Shared
|
|
389
389
|
def included(_)
|
390
390
|
ActiveGraph::Shared::TypeConverters.constants.each do |constant_name|
|
391
391
|
constant = ActiveGraph::Shared::TypeConverters.const_get(constant_name)
|
392
|
-
register_converter(constant) if constant.respond_to?(:convert_type)
|
392
|
+
register_converter(constant, force: false) if constant.respond_to?(:convert_type)
|
393
393
|
end
|
394
394
|
end
|
395
395
|
|
@@ -425,7 +425,8 @@ module ActiveGraph::Shared
|
|
425
425
|
found_converter.respond_to?(:converted?) ? found_converter.converted?(value) : value.is_a?(found_converter.db_type)
|
426
426
|
end
|
427
427
|
|
428
|
-
def register_converter(converter)
|
428
|
+
def register_converter(converter, force: true)
|
429
|
+
return if CONVERTERS.key?(converter.convert_type) && !force
|
429
430
|
CONVERTERS[converter.convert_type] = converter
|
430
431
|
end
|
431
432
|
end
|