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

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