activegraph 10.0.0.pre.alpha.7 → 10.0.0.pre.alpha.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ee929286df7c03716620060ba6229dae865612f295c02e02872a8a1fa329c7f
4
- data.tar.gz: 7d3983fc31041476a7cc6e0b92119b6c1e6ad1207ae5d1e6293871a92f9f865a
3
+ metadata.gz: 6b7846af39d73f89f279284b358f2f4e5552709b6dd8812a5481944fdf479ec0
4
+ data.tar.gz: 71674f9403e7f9c88ab23631e138e6b901a3ae9dcfb672fbf7277ce20d26c61b
5
5
  SHA512:
6
- metadata.gz: 582aedd1880e79fb5983774d2fea5456b247f8c72b57caa676de5b5272766f4e7db44c6de927c3899a23a5bc38a92ef4b71bffc782ba44facf9086d818ca9c58
7
- data.tar.gz: 982dc47105a57220e1b1e09a1fb3ed18b47646dd9c7166cef754a30609e12f0487722b411b7efef2dcacc3fa5bb7ac149028b8ccb3d3c8abca4cb1b93f501d32
6
+ metadata.gz: a0ed7a4862b179f5b0b02a3dbccc440604ab309e744736247c2d6600848f29387e9ecd4504a6a170a4dcc287be42543d6443d52d8a12ec4544a0b1238af796d5
7
+ data.tar.gz: fa3b8f1d9db6aa4c5d3d3090dd74a761a9d8c8399f0c1e5fcafeac57a6402b37f3987ee99efcf7a07e9a775b9f1045a574892e340b21d504b11fc39fb663a04e
data/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file.
3
3
  This file should follow the standards specified on [http://keepachangelog.com/]
4
4
  This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
+ ## [10.0.x] 2020-01-23
7
+
8
+ - Executing relationship callbacks on relationship deletion.
9
+ - When assiging relationships, preserving earlier relationships.
10
+ - Enforcing has one constraint on relationships.
11
+
6
12
  ## [9.6.1] 2019-12-18
7
13
 
8
14
  ## Fixed
@@ -33,6 +33,3 @@ cache_class_names: true
33
33
  # class_name_property: :_classname
34
34
 
35
35
  transform_rel_type: :upcase
36
-
37
- # Enforce has_one relationship. When same reationship is created on reverse object with another object, raise the error.
38
- enforce_has_one: false
data/lib/neo4j.rb CHANGED
@@ -61,6 +61,7 @@ require 'neo4j/active_rel/related_node'
61
61
  require 'neo4j/active_rel/types'
62
62
  require 'neo4j/active_rel'
63
63
 
64
+ require 'neo4j/active_node/dependent_callbacks'
64
65
  require 'neo4j/active_node/node_list_formatter'
65
66
  require 'neo4j/active_node/dependent'
66
67
  require 'neo4j/active_node/dependent/query_proxy_methods'
@@ -45,6 +45,7 @@ module Neo4j
45
45
  include Neo4j::ActiveNode::Dependent
46
46
  include Neo4j::ActiveNode::Enum
47
47
  include Neo4j::Shared::PermittedAttributes
48
+ include Neo4j::ActiveNode::DependentCallbacks
48
49
 
49
50
  def initialize(args = nil)
50
51
  self.class.ensure_id_property_info! # So that we make sure all objects have an id_property
@@ -26,24 +26,25 @@ module Neo4j
26
26
  # @param [String, Symbol] other_node The identifier to use for the other end of the chain.
27
27
  # @param [String, Symbol] other_rel The identifier to use for the relationship in the optional match.
28
28
  # @return [Neo4j::ActiveNode::Query::QueryProxy]
29
- def unique_nodes(association, self_identifer, other_node, other_rel)
29
+ def unique_nodes(association, self_identifer, other_node, other_rel, ids = [])
30
30
  fail 'Only supported by in QueryProxy chains started by an instance' unless source_object
31
31
  return false if send(association.name).empty?
32
- unique_nodes_query(association, self_identifer, other_node, other_rel)
32
+ unique_nodes_query(association, self_identifer, other_node, other_rel, ids)
33
33
  .proxy_as(association.target_class, other_node)
34
34
  end
35
35
 
36
36
  private
37
37
 
38
- def unique_nodes_query(association, self_identifer, other_node, other_rel)
39
- query.with(identity).proxy_as_optional(source_object.class, self_identifer)
40
- .send(association.name, other_node, other_rel)
41
- .query
42
- .with(other_node)
43
- .match("()#{association.arrow_cypher(:orphan_rel)}(#{other_node})")
44
- .with(other_node, count: 'count(*)')
45
- .where('count = $one', one: 1)
46
- .break
38
+ def unique_nodes_query(association, self_identifer, other_node, other_rel, ids)
39
+ base = query.with(identity).proxy_as_optional(source_object.class, self_identifer)
40
+ .send(association.name, other_node, other_rel)
41
+ base = base.where(id: ids) if ids.present?
42
+ base.query
43
+ .with(other_node)
44
+ .match("()#{association.arrow_cypher(:orphan_rel)}(#{other_node})")
45
+ .with(other_node, count: 'count(*)')
46
+ .where('count = $one', one: 1)
47
+ .break
47
48
  end
48
49
  end
49
50
  end
@@ -0,0 +1,31 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module DependentCallbacks
4
+ extend ActiveSupport::Concern
5
+
6
+ def dependent_delete_callback(association, ids)
7
+ association_query_proxy(association.name).where(id: ids).delete_all
8
+ end
9
+
10
+ def dependent_delete_orphans_callback(association, ids)
11
+ unique_query = as(:self).unique_nodes(association, :self, :n, :other_rel, ids)
12
+ unique_query.query.optional_match('(n)-[r]-()').delete(:n, :r).exec if unique_query
13
+ end
14
+
15
+ def dependent_destroy_callback(association, ids)
16
+ unique_query = association_query_proxy(association.name).where(id: ids)
17
+ unique_query.each_for_destruction(self, &:destroy) if unique_query
18
+ end
19
+
20
+ def dependent_destroy_orphans_callback(association, ids)
21
+ unique_query = as(:self).unique_nodes(association, :self, :n, :other_rel, ids)
22
+ unique_query.each_for_destruction(self, &:destroy) if unique_query
23
+ end
24
+
25
+ def callbacks_from_active_rel(active_rel, direction, other_node)
26
+ rel = active_rel_corresponding_rel(active_rel, direction, other_node.class).try(:last)
27
+ public_send("dependent_#{rel.dependent}_callback", rel, [other_node.id]) if rel && rel.dependent
28
+ end
29
+ end
30
+ end
31
+ end
@@ -229,10 +229,9 @@ module Neo4j::ActiveNode
229
229
  end
230
230
  end
231
231
 
232
- def validate_reverse_has_one_core_rel(association, other_node)
233
- return unless Neo4j::Config[:enforce_has_one]
232
+ def delete_reverse_has_one_core_rel(association)
234
233
  reverse_assoc = reverse_association(association)
235
- validate_has_one_rel!(reverse_assoc, other_node) if reverse_assoc && reverse_assoc.type == :has_one
234
+ delete_has_one_rel!(reverse_assoc) if reverse_assoc && reverse_assoc.type == :has_one
236
235
  end
237
236
 
238
237
  def reverse_association(association)
@@ -242,14 +241,14 @@ module Neo4j::ActiveNode
242
241
  reverse_assoc && reverse_assoc.last
243
242
  end
244
243
 
245
- def validate_reverse_has_one_active_rel(active_rel, direction, other_node)
244
+ def delete_reverse_has_one_active_rel(active_rel, direction, other_node)
246
245
  rel = active_rel_corresponding_rel(active_rel, direction, other_node.class)
247
- validate_has_one_rel!(rel.last, other_node) if rel && rel.last.type == :has_one
246
+ delete_has_one_rel!(rel.last) if rel && rel.last.type == :has_one
248
247
  end
249
248
 
250
- def validate_has_one_rel!(rel, other_node)
251
- raise_error = (node = send(rel.name.to_s)) && node != other_node
252
- fail(HasOneConstraintError, "node #{self.class}##{neo_id} has a has_one relationship with #{other_node.class}##{other_node.neo_id}") if raise_error
249
+ def delete_has_one_rel!(rel)
250
+ send("#{rel.name}", :n, :r, chainable: true).query.delete(:r).exec
251
+ association_proxy_cache.clear
253
252
  end
254
253
 
255
254
  def active_rel_corresponding_rel(active_rel, direction, target_class)
@@ -208,7 +208,7 @@ module Neo4j
208
208
  Neo4j::ActiveBase.run_transaction do
209
209
  other_nodes.each do |other_node|
210
210
  if other_node.neo_id
211
- other_node.try(:validate_reverse_has_one_core_rel, association, @start_object)
211
+ other_node.try(:delete_reverse_has_one_core_rel, association)
212
212
  else
213
213
  other_node.save
214
214
  end
@@ -49,13 +49,37 @@ module Neo4j
49
49
  # Deletes the relationships between all nodes for the last step in the QueryProxy chain and replaces them with relationships to the given nodes.
50
50
  # Executed in the database, callbacks will not be run.
51
51
  def replace_with(node_or_nodes)
52
- nodes = Array(node_or_nodes)
52
+ node_hash = idify_hash(node_or_nodes)
53
+ original_ids = self.pluck(:id)
54
+ new_nodes = add_rels(node_hash, original_ids)
55
+ delete_rels_for_nodes(original_ids - node_hash.keys)
56
+ new_nodes | node_hash.values
57
+ end
58
+
59
+ def idify_hash(args)
60
+ Array(args).reject(&:blank?).flatten.each_with_object({}).with_index do |(arg, hash), inx|
61
+ if arg.is_a?(Integer) || arg.is_a?(String)
62
+ hash[arg.to_i] = @model.find(arg)
63
+ else
64
+ key = arg.persisted? ? arg.id : "tmp_#{inx}"
65
+ hash[key] = arg
66
+ end
67
+ end
68
+ end
53
69
 
54
- self.delete_all_rels
55
- nodes.map do |node|
56
- node if _create_relation_or_defer(node)
70
+ def add_rels(node_hash, original_ids)
71
+ (node_hash.keys - original_ids).map do |id|
72
+ node_hash[id] if _create_relation_or_defer(node_hash[id])
57
73
  end.compact
58
- # nodes.each { |node| self << node }
74
+ end
75
+
76
+ def delete_rels_for_nodes(ids)
77
+ return unless ids.present?
78
+ if association.dependent
79
+ start_object.public_send("dependent_#{association.dependent}_callback", association, ids)
80
+ else
81
+ self.where(id: ids).delete_all_rels
82
+ end
59
83
  end
60
84
 
61
85
  # Returns all relationships between a node and its last link in the QueryProxy chain, destroys them in Ruby. Callbacks will be run.
@@ -10,6 +10,12 @@ module Neo4j
10
10
  end
11
11
  super(*args)
12
12
  end
13
+
14
+ def destroy
15
+ to_node.callbacks_from_active_rel(self, :in, from_node).try(:last)
16
+ from_node.callbacks_from_active_rel(self, :out, to_node).try(:last)
17
+ super
18
+ end
13
19
  end
14
20
  end
15
21
  end
@@ -45,17 +45,16 @@ module Neo4j::ActiveRel
45
45
 
46
46
  def create_model
47
47
  validate_node_classes!
48
- validate_has_one_rel
48
+ delete_has_one_rel
49
49
  rel = _create_rel
50
50
  return self unless rel.respond_to?(:props)
51
51
  init_on_load(rel, from_node, to_node, @rel_type)
52
52
  true
53
53
  end
54
54
 
55
- def validate_has_one_rel
56
- return unless Neo4j::Config[:enforce_has_one]
57
- to_node.validate_reverse_has_one_active_rel(self, :in, from_node) if to_node.persisted?
58
- from_node.validate_reverse_has_one_active_rel(self, :out, to_node) if from_node.persisted?
55
+ def delete_has_one_rel
56
+ to_node.delete_reverse_has_one_active_rel(self, :in, from_node) if to_node.persisted?
57
+ from_node.delete_reverse_has_one_active_rel(self, :out, to_node) if from_node.persisted?
59
58
  end
60
59
 
61
60
  def query_as(var)
@@ -58,7 +58,7 @@ module Neo4j
58
58
 
59
59
  def mark_versions_as_complete(versions)
60
60
  Neo4j::ActiveBase.new_query
61
- .with('{versions} AS versions').params(versions: versions).break
61
+ .with('$versions AS versions').params(versions: versions).break
62
62
  .unwind(version: :versions).break
63
63
  .merge('(:`Neo4j::Migrations::SchemaMigration` {migration_id: version})')
64
64
  .exec
@@ -23,12 +23,32 @@ module Neo4j
23
23
  end
24
24
 
25
25
  def fetch_index_descriptions(session)
26
- session.query('CALL db.indexes()').reject do |row|
26
+ result = session.query('CALL db.indexes()')
27
+ if result.columns.include?(:description)
28
+ v3_indexes(result)
29
+ else
30
+ v4_indexes(result)
31
+ end
32
+ end
33
+
34
+ def v3_indexes(result)
35
+ result.reject do |row|
27
36
  # These indexes are created automagically when the corresponding constraints are created
28
37
  row.type == 'node_unique_property'
29
38
  end.map(&:description)
30
39
  end
31
40
 
41
+ def v4_indexes(result)
42
+ result.reject do |row|
43
+ # These indexes are created automagically when the corresponding constraints are created
44
+ row.uniqueness == 'UNIQUE'
45
+ end.map(&method(:description))
46
+ end
47
+
48
+ def description(row)
49
+ "INDEX FOR (n:#{row.labelsOrTypes.first}) ON (#{row.properties.map{|prop| "n.#{prop}"}.join(', ')})"
50
+ end
51
+
32
52
  def drop_and_create_queries(existing, specified, remove_missing)
33
53
  [].tap do |queries|
34
54
  if remove_missing
@@ -97,11 +97,13 @@ COMMENT
97
97
 
98
98
  schema_data = YAML.safe_load(File.read(SCHEMA_YAML_PATH), [Symbol])
99
99
 
100
- Neo4j::Core::CypherSession::Adaptors::Base.subscribe_to_query(&method(:puts))
100
+ Neo4j::Transaction.subscribe_to_query(&method(:puts))
101
101
 
102
102
  Neo4j::ActiveBase.run_transaction do
103
103
  Neo4j::Migrations::Schema.synchronize_schema_data(Neo4j::ActiveBase.transaction, schema_data, args[:remove_missing])
104
+ end
104
105
 
106
+ Neo4j::ActiveBase.run_transaction do
105
107
  runner = Neo4j::Migrations::Runner.new
106
108
  runner.mark_versions_as_complete(schema_data[:versions]) # Run in test mode?
107
109
  end
data/lib/neo4j/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Neo4j
2
- VERSION = '10.0.0-alpha.7'
2
+ VERSION = '10.0.0-alpha.8'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activegraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.0.0.pre.alpha.7
4
+ version: 10.0.0.pre.alpha.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Ronge, Brian Underwood, Chris Grigg, Heinrich Klobuczek
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-06 00:00:00.000000000 Z
11
+ date: 2020-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -259,6 +259,7 @@ files:
259
259
  - lib/neo4j/active_node/dependent.rb
260
260
  - lib/neo4j/active_node/dependent/association_methods.rb
261
261
  - lib/neo4j/active_node/dependent/query_proxy_methods.rb
262
+ - lib/neo4j/active_node/dependent_callbacks.rb
262
263
  - lib/neo4j/active_node/enum.rb
263
264
  - lib/neo4j/active_node/has_n.rb
264
265
  - lib/neo4j/active_node/has_n/association.rb