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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -1
  3. data/Gemfile +0 -4
  4. data/activegraph.gemspec +3 -4
  5. data/lib/active_graph/base.rb +4 -0
  6. data/lib/active_graph/core/element.rb +142 -0
  7. data/lib/active_graph/core/entity.rb +4 -0
  8. data/lib/active_graph/core/label.rb +5 -166
  9. data/lib/active_graph/core/node.rb +0 -4
  10. data/lib/active_graph/core/query_clauses.rb +4 -4
  11. data/lib/active_graph/core/schema.rb +23 -28
  12. data/lib/active_graph/core/type.rb +13 -0
  13. data/lib/active_graph/migrations/helpers/schema.rb +10 -13
  14. data/lib/active_graph/model_schema.rb +1 -1
  15. data/lib/active_graph/node/has_n.rb +6 -2
  16. data/lib/active_graph/node/labels.rb +15 -12
  17. data/lib/active_graph/node/persistence.rb +0 -11
  18. data/lib/active_graph/node/query/query_proxy/link.rb +15 -4
  19. data/lib/active_graph/node/query/query_proxy.rb +3 -3
  20. data/lib/active_graph/node/query/query_proxy_eager_loading.rb +1 -1
  21. data/lib/active_graph/node/query/query_proxy_enumerable.rb +4 -4
  22. data/lib/active_graph/node/query/query_proxy_methods.rb +14 -16
  23. data/lib/active_graph/node/query/query_proxy_methods_of_mass_updating.rb +2 -5
  24. data/lib/active_graph/node/query.rb +1 -1
  25. data/lib/active_graph/node/query_methods.rb +9 -12
  26. data/lib/active_graph/railtie.rb +13 -0
  27. data/lib/active_graph/relationship/initialize.rb +2 -2
  28. data/lib/active_graph/relationship/persistence.rb +3 -1
  29. data/lib/active_graph/relationship/property.rb +1 -5
  30. data/lib/active_graph/relationship/query.rb +14 -9
  31. data/lib/active_graph/relationship/related_node.rb +4 -8
  32. data/lib/active_graph/relationship/types.rb +8 -0
  33. data/lib/active_graph/relationship/wrapping.rb +1 -1
  34. data/lib/active_graph/relationship.rb +4 -1
  35. data/lib/active_graph/shared/identity.rb +6 -3
  36. data/lib/active_graph/shared/persistence.rb +12 -1
  37. data/lib/active_graph/shared/query_factory.rb +1 -1
  38. data/lib/active_graph/shared/type_converters.rb +3 -2
  39. data/lib/active_graph/transactions.rb +4 -0
  40. data/lib/active_graph/version.rb +1 -1
  41. data/lib/active_graph.rb +2 -14
  42. data/lib/{active_graph/generators → rails/generators/active_graph/migration}/migration_generator.rb +5 -2
  43. data/lib/{active_graph/generators → rails/generators/active_graph/model}/model_generator.rb +5 -2
  44. data/lib/{active_graph/generators → rails/generators/active_graph/upgrade_v8}/upgrade_v8_generator.rb +5 -2
  45. data/lib/{active_graph → rails}/generators/migration_helper.rb +57 -0
  46. data/lib/{active_graph → rails}/generators/source_path_helper.rb +1 -1
  47. metadata +34 -54
  48. data/lib/active_graph/generators/active_model.rb +0 -33
  49. data/lib/active_graph/generators/generated_attribute.rb +0 -17
  50. /data/lib/{active_graph/generators → rails/generators/active_graph}/migration/templates/migration.erb +0 -0
  51. /data/lib/{active_graph/generators → rails/generators/active_graph}/model/templates/migration.erb +0 -0
  52. /data/lib/{active_graph/generators → rails/generators/active_graph}/model/templates/model.erb +0 -0
  53. /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}`) OPTIONAL MATCH (n)-[r]-() DELETE n,r")
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
- (self.name.nil? ? object_id.to_s.to_sym : decorated_label_name)
192
+ name.nil? ? object_id.to_s.to_sym : decorated_label_name
188
193
  end
189
194
 
190
195
  def decorated_label_name
191
- name = case ActiveGraph::Config[:module_handling]
192
- when :demodulize
193
- self.name.demodulize
194
- when Proc
195
- ActiveGraph::Config[:module_handling].call self.name
196
- else
197
- self.name
198
- end
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 && value.is_a?(ActiveGraph::Node)
151
- value.id
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" if not neo_id.is_a?(Integer)
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, ->(_, _) { {"ID(#{n_string})" => neo_id.to_i} })
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.neo_id
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
- (arg.is_a?(Integer) || arg.is_a?(String)) ? @model.find_by(id: arg) : arg
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.start_node_id : rel.end_node_id]
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
- self.to_a == other
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
- self.query.pluck(*arg_list)
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
- "ID(#{var}) = $other_node_id"
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 [Integer, String, Hash, NilClass].any? { |c| node_condition.is_a?(c) }
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("ID(#{var}) AS proof_of_life LIMIT 1").first
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
- case condition
290
- when Integer
291
- self.where("ID(#{target}) = $exists_condition").params(exists_condition: condition)
292
- when Hash
293
- self.where(condition.keys.first => condition.values.first)
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
- self
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
- begin
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("ID(#{node_var})" => self.neo_id)
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 [Integer, String, Hash, NilClass].any? { |c| node_condition.is_a?(c) }
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('ID(n) AS proof_of_life LIMIT 1').first
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(node_condition)
55
- case node_condition
56
- when Integer
57
- self.query_as(:n).where('ID(n)' => node_condition)
58
- when String
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
- self.query_as(:n)
60
+ where(**condition)
64
61
  end
65
62
  end
66
63
  end
@@ -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.start_node_id,
23
- unwrapped_reloaded.end_node_id,
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: 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}_id") { send(direction).neo_id if 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,Integer] id of node to find
9
+ # @param [String] id of node to find
10
10
  def find(id)
11
- fail "Unknown argument #{id.class} in find method (expected String or Integer)" if !(id.is_a?(String) || id.is_a?(Integer))
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('ID(r)' => key.to_i).limit(1).return(:r).first
19
- fail RecordNotFound.new("Couldn't find #{name} with 'id'=#{key.inspect}", name, key) if result.blank?
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('ID(r1)').pluck(:r1).first
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('ID(r1) DESC').pluck(:r1).first
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.map { |k, v| v.is_a?(Integer) ? "r1.#{k} = #{v}" : "r1.#{k} = '#{v}'" }.join(', ')
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?(Integer)
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
- if RUBY_VERSION < '3' && kwargs.empty?
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?(Numeric)
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?(Integer) || node.respond_to?(:neo_id)
84
+ node.nil? || node.is_a?(String) || node.respond_to?(:neo_id)
89
85
  end
90
86
  end
91
87
  end
@@ -67,6 +67,14 @@ module ActiveGraph
67
67
  !!@type
68
68
  end
69
69
 
70
+ def mapped_element_name
71
+ @type
72
+ end
73
+
74
+ def mapped_element
75
+ Core::Type.new(mapped_element_name)
76
+ end
77
+
70
78
  private
71
79
 
72
80
  def assign_type!(given_type, auto)
@@ -13,7 +13,7 @@ module ActiveGraph
13
13
 
14
14
  return rel
15
15
  end.tap do |wrapped_rel|
16
- wrapped_rel.init_on_load(rel, rel.start_node_id, rel.end_node_id, rel.type)
16
+ wrapped_rel.init_on_load(rel, rel.start_node_element_id, rel.end_node_element_id, rel.type)
17
17
  end
18
18
  end
19
19
 
@@ -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?(Integer) ? node : nil
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 [Integer, nil] the neo4j id of the node if persisted or nil
16
- def neo_id
17
- _persisted_obj ? _persisted_obj.id : nil
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('ID(n)')).any?
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("ID(#{identifier}) = $#{identifier_id}")
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
@@ -28,6 +28,10 @@ module ActiveGraph
28
28
 
29
29
  alias transaction write_transaction
30
30
 
31
+ def lock_node(node)
32
+ node.as(:n).query.remove('n._AGLOCK_').exec if tx&.open?
33
+ end
34
+
31
35
  private
32
36
 
33
37
  def send_transaction(method, **config, &block)