dagnabit 2.2.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.
- data/.autotest +5 -0
- data/.document +5 -0
- data/.gitignore +6 -0
- data/LICENSE +20 -0
- data/README.rdoc +186 -0
- data/Rakefile +69 -0
- data/VERSION.yml +5 -0
- data/bin/dagnabit-test +53 -0
- data/dagnabit.gemspec +124 -0
- data/init.rb +1 -0
- data/lib/dagnabit.rb +17 -0
- data/lib/dagnabit/activation.rb +60 -0
- data/lib/dagnabit/link/associations.rb +18 -0
- data/lib/dagnabit/link/class_methods.rb +43 -0
- data/lib/dagnabit/link/configuration.rb +40 -0
- data/lib/dagnabit/link/cycle_prevention.rb +31 -0
- data/lib/dagnabit/link/named_scopes.rb +65 -0
- data/lib/dagnabit/link/transitive_closure_link_model.rb +86 -0
- data/lib/dagnabit/link/transitive_closure_recalculation.rb +17 -0
- data/lib/dagnabit/link/transitive_closure_recalculation/on_create.rb +104 -0
- data/lib/dagnabit/link/transitive_closure_recalculation/on_destroy.rb +125 -0
- data/lib/dagnabit/link/transitive_closure_recalculation/on_update.rb +13 -0
- data/lib/dagnabit/link/transitive_closure_recalculation/utilities.rb +56 -0
- data/lib/dagnabit/link/validations.rb +26 -0
- data/lib/dagnabit/node/associations.rb +84 -0
- data/lib/dagnabit/node/class_methods.rb +74 -0
- data/lib/dagnabit/node/configuration.rb +26 -0
- data/lib/dagnabit/node/neighbors.rb +73 -0
- data/test/connections/native_postgresql/connection.rb +17 -0
- data/test/connections/native_sqlite3/connection.rb +24 -0
- data/test/dagnabit/link/test_associations.rb +61 -0
- data/test/dagnabit/link/test_class_methods.rb +102 -0
- data/test/dagnabit/link/test_configuration.rb +38 -0
- data/test/dagnabit/link/test_cycle_prevention.rb +64 -0
- data/test/dagnabit/link/test_named_scopes.rb +32 -0
- data/test/dagnabit/link/test_transitive_closure_link_model.rb +69 -0
- data/test/dagnabit/link/test_transitive_closure_recalculation.rb +139 -0
- data/test/dagnabit/link/test_validations.rb +39 -0
- data/test/dagnabit/node/test_associations.rb +147 -0
- data/test/dagnabit/node/test_class_methods.rb +49 -0
- data/test/dagnabit/node/test_configuration.rb +29 -0
- data/test/dagnabit/node/test_neighbors.rb +91 -0
- data/test/helper.rb +27 -0
- data/test/models/beta_node.rb +3 -0
- data/test/models/custom_data_link.rb +4 -0
- data/test/models/customized_link.rb +7 -0
- data/test/models/customized_link_node.rb +4 -0
- data/test/models/link.rb +4 -0
- data/test/models/node.rb +3 -0
- data/test/schema/schema.rb +51 -0
- metadata +165 -0
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'dagnabit'
|
data/lib/dagnabit.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'dagnabit/link/configuration'
|
2
|
+
require 'dagnabit/link/validations'
|
3
|
+
require 'dagnabit/link/associations'
|
4
|
+
require 'dagnabit/link/class_methods'
|
5
|
+
require 'dagnabit/link/cycle_prevention'
|
6
|
+
require 'dagnabit/link/named_scopes'
|
7
|
+
require 'dagnabit/link/transitive_closure_recalculation'
|
8
|
+
require 'dagnabit/link/transitive_closure_link_model'
|
9
|
+
|
10
|
+
require 'dagnabit/node/configuration'
|
11
|
+
require 'dagnabit/node/class_methods'
|
12
|
+
require 'dagnabit/node/associations'
|
13
|
+
require 'dagnabit/node/neighbors'
|
14
|
+
|
15
|
+
require 'dagnabit/activation'
|
16
|
+
|
17
|
+
ActiveRecord::Base.extend(Dagnabit::Activation)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Dagnabit
|
2
|
+
#
|
3
|
+
# Class methods for mixing in ("activating") dag functionality.
|
4
|
+
#
|
5
|
+
module Activation
|
6
|
+
#
|
7
|
+
# Marks an ActiveRecord model as a link model.
|
8
|
+
#
|
9
|
+
# == Supported options
|
10
|
+
#
|
11
|
+
# [:ancestor_id_column]
|
12
|
+
# Name of the column in the link tables that will hold the ID of the
|
13
|
+
# ancestor object. Defaults to +ancestor_id+.
|
14
|
+
# [:descendant_id_column]
|
15
|
+
# Name of the column in the link tables that will hold the ID of the
|
16
|
+
# descendant object. Defaults to +descendant_id+.
|
17
|
+
# [:transitive_closure_table_name]
|
18
|
+
# Name of the table that will hold the tuples comprising the transitive
|
19
|
+
# closure of the dag. Defaults to the edge model's table name affixed by
|
20
|
+
# "<tt>_transitive_closure_tuples</tt>".
|
21
|
+
# [:transitive_closure_class_name]
|
22
|
+
# Name of the generated class that will represent tuples in the transitive
|
23
|
+
# closure tuple table. This class is created inside the link model class.
|
24
|
+
# Defaults to +TransitiveClosureLink+.
|
25
|
+
#
|
26
|
+
def acts_as_dag_link(options = {})
|
27
|
+
extend Dagnabit::Link::Configuration
|
28
|
+
configure_acts_as_dag_link(options)
|
29
|
+
|
30
|
+
extend Dagnabit::Link::TransitiveClosureLinkModel
|
31
|
+
generate_transitive_closure_link_model(options)
|
32
|
+
|
33
|
+
extend Dagnabit::Link::ClassMethods
|
34
|
+
extend Dagnabit::Link::Associations
|
35
|
+
extend Dagnabit::Link::NamedScopes
|
36
|
+
extend Dagnabit::Link::Validations
|
37
|
+
include Dagnabit::Link::CyclePrevention
|
38
|
+
include Dagnabit::Link::TransitiveClosureRecalculation
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Adds convenience methods to dag nodes.
|
43
|
+
#
|
44
|
+
# Strictly speaking, it's not necessary to call this method inside classes
|
45
|
+
# you want to act as nodes. +acts_as_dag_node_linked_by+ merely provides
|
46
|
+
# convenience methods for finding and traversing links from/to this node.
|
47
|
+
#
|
48
|
+
# The +link_class_name+ parameter determines the the link model to be used
|
49
|
+
# for nodes of this type.
|
50
|
+
#
|
51
|
+
def acts_as_dag_node_linked_by(link_class_name)
|
52
|
+
extend Dagnabit::Node::Configuration
|
53
|
+
configure_acts_as_dag_node(link_class_name)
|
54
|
+
|
55
|
+
extend Dagnabit::Node::ClassMethods
|
56
|
+
extend Dagnabit::Node::Associations
|
57
|
+
include Dagnabit::Node::Neighbors
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Dagnabit
|
2
|
+
module Link
|
3
|
+
#
|
4
|
+
# Adds associations useful for link classes.
|
5
|
+
#
|
6
|
+
# This module mixes in the following associations to link classes:
|
7
|
+
#
|
8
|
+
# * +ancestor+: the source of this link, or where this link begins
|
9
|
+
# * +descendant+: the target of this link, or where this link ends
|
10
|
+
#
|
11
|
+
module Associations
|
12
|
+
def self.extended(base)
|
13
|
+
base.send(:belongs_to, :ancestor, :polymorphic => true, :foreign_key => base.ancestor_id_column)
|
14
|
+
base.send(:belongs_to, :descendant, :polymorphic => true, :foreign_key => base.descendant_id_column)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Dagnabit
|
2
|
+
module Link
|
3
|
+
#
|
4
|
+
# Handy class methods for creating and querying paths.
|
5
|
+
#
|
6
|
+
module ClassMethods
|
7
|
+
#
|
8
|
+
# Constructs a new edge. The direction of the edge runs from +from+ to +to+.
|
9
|
+
#
|
10
|
+
def build_edge(from, to, attributes = {})
|
11
|
+
new(attributes.merge(:ancestor => from, :descendant => to))
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Like +build_edge+, but saves the edge after it is instantiated.
|
16
|
+
# Returns true if the endpoints could be connected, false otherwise.
|
17
|
+
#
|
18
|
+
# See Dagnabit::Link::Validations for more information on built-in link
|
19
|
+
# validations.
|
20
|
+
#
|
21
|
+
def connect(from, to, attributes = {})
|
22
|
+
build_edge(from, to, attributes).save
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Returns true if there is a path from +a+ to +b+, false otherwise.
|
27
|
+
#
|
28
|
+
def path?(a, b)
|
29
|
+
paths(a, b).count > 0
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Returns all paths from +a+ to +b+.
|
34
|
+
#
|
35
|
+
# These paths are returned as transitive closure links, which aren't
|
36
|
+
# guaranteed to have the same methods as your link class.
|
37
|
+
#
|
38
|
+
def paths(a, b)
|
39
|
+
transitive_closure_class.linking(a, b)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Dagnabit
|
2
|
+
module Link
|
3
|
+
#
|
4
|
+
# Dagnabit::Edge::Configuration - dag edge configuration
|
5
|
+
#
|
6
|
+
module Configuration
|
7
|
+
attr_accessor :ancestor_id_column
|
8
|
+
attr_accessor :descendant_id_column
|
9
|
+
attr_writer :transitive_closure_table_name
|
10
|
+
attr_accessor :transitive_closure_class_name
|
11
|
+
|
12
|
+
#
|
13
|
+
# Configure an ActiveRecord model as a dag link. See Dagnabit::Activation
|
14
|
+
# for options description.
|
15
|
+
#
|
16
|
+
def configure_acts_as_dag_link(options)
|
17
|
+
self.ancestor_id_column = options[:ancestor_id_column] || 'ancestor_id'
|
18
|
+
self.descendant_id_column = options[:descendant_id_column] || 'descendant_id'
|
19
|
+
self.transitive_closure_table_name = options[:transitive_closure_table_name] || table_name + '_transitive_closure_tuples'
|
20
|
+
self.transitive_closure_class_name = options[:transitive_closure_class_name] || 'TransitiveClosureLink'
|
21
|
+
end
|
22
|
+
|
23
|
+
def transitive_closure_table_name
|
24
|
+
connection.quote_table_name(unquoted_transitive_closure_table_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def unquoted_transitive_closure_table_name
|
28
|
+
@transitive_closure_table_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def ancestor_type_column
|
32
|
+
'ancestor_type'
|
33
|
+
end
|
34
|
+
|
35
|
+
def descendant_type_column
|
36
|
+
'descendant_type'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Dagnabit
|
2
|
+
module Link
|
3
|
+
#
|
4
|
+
# Installs a callback into the link model to check for cycles. If a cycle
|
5
|
+
# would be created by the addition of this link, prevents the link from
|
6
|
+
# being saved.
|
7
|
+
#
|
8
|
+
module CyclePrevention
|
9
|
+
#
|
10
|
+
# Performs cycle detection.
|
11
|
+
#
|
12
|
+
# Given an edge (A, B), insertion of that edge will create a cycle if
|
13
|
+
#
|
14
|
+
# * there is a path (B, A), or
|
15
|
+
# * A == B
|
16
|
+
#
|
17
|
+
def before_save
|
18
|
+
super
|
19
|
+
check_for_cycles
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def check_for_cycles
|
25
|
+
if ancestor && descendant
|
26
|
+
false if self.class.path?(descendant, ancestor) || descendant == ancestor
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Dagnabit
|
2
|
+
module Link
|
3
|
+
#
|
4
|
+
# Adds named scopes to Link models.
|
5
|
+
#
|
6
|
+
# This module provides two named scopes for finding links scoped by
|
7
|
+
# ancestor and descendant type. They were designed as support for node
|
8
|
+
# neighbor queries such as ancestors_of_type and descendants_as_type, but
|
9
|
+
# can be used on their own.
|
10
|
+
#
|
11
|
+
# These links are imported into the generated transitive closure link model.
|
12
|
+
# See Dagnabit::Link::TransitiveClosureLinkModel for more information.
|
13
|
+
#
|
14
|
+
# == Supplied scopes
|
15
|
+
#
|
16
|
+
# [ancestor_type]
|
17
|
+
# Returns all links having a specified ancestor type.
|
18
|
+
#
|
19
|
+
# [descendant_type]
|
20
|
+
# Returns all links having a specified descendant type.
|
21
|
+
#
|
22
|
+
# == A note on type matching
|
23
|
+
#
|
24
|
+
# Types are stored in links using ActiveRecord's polymorphic association
|
25
|
+
# typing logic, and are matched using string matching. Therefore, subclass
|
26
|
+
# matching and namespacing aren't provided.
|
27
|
+
#
|
28
|
+
# To elaborate on this, let's say you have the following model structure:
|
29
|
+
#
|
30
|
+
# class Link < ActiveRecord::Base
|
31
|
+
# acts_as_dag_link
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# module Foo
|
35
|
+
# class Bar < ActiveRecord::Base
|
36
|
+
# ...
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# A link linking Foo::Bars will record Foo::Bar as ancestor or descendant
|
41
|
+
# type, not just 'Bar'. The following will therefore not work:
|
42
|
+
#
|
43
|
+
# Link.ancestor_type('Bar')
|
44
|
+
#
|
45
|
+
# You have to do:
|
46
|
+
#
|
47
|
+
# Link.ancestor_type('Foo::Bar')
|
48
|
+
#
|
49
|
+
# or, if you'd like to hide the details of deriving a full class name:
|
50
|
+
#
|
51
|
+
# Link.ancestor_type(Bar.name)
|
52
|
+
#
|
53
|
+
module NamedScopes
|
54
|
+
def self.extended(base)
|
55
|
+
base.send(:named_scope,
|
56
|
+
:ancestor_type,
|
57
|
+
lambda { |type| { :conditions => { :ancestor_type => type } } })
|
58
|
+
|
59
|
+
base.send(:named_scope,
|
60
|
+
:descendant_type,
|
61
|
+
lambda { |type| { :conditions => { :descendant_type => type } } })
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Dagnabit
|
2
|
+
module Link
|
3
|
+
#
|
4
|
+
# Builds a model for transitive closure tuples.
|
5
|
+
#
|
6
|
+
# The transitive closure model is generated inside the link class.
|
7
|
+
# Therefore, if your link model class was called Link, the transitive
|
8
|
+
# closure model would be named Link::(class name here). The name of the
|
9
|
+
# transitive closure model class is determined when the link class model is
|
10
|
+
# activated; see Dagnabit::Activation#acts_as_dag_link for more information.
|
11
|
+
#
|
12
|
+
# == Model class details
|
13
|
+
#
|
14
|
+
# === Construction details
|
15
|
+
#
|
16
|
+
# The transitive closure model is constructed as a subclass of
|
17
|
+
# ActiveRecord::Base, _not_ as a subclass of your link model class. The
|
18
|
+
# transitive closure model also acts as a dag link (via
|
19
|
+
# Dagnabit::Activation#acts_as_dag_link) and is configured using the same
|
20
|
+
# configuration options as your link model class.
|
21
|
+
#
|
22
|
+
# This means:
|
23
|
+
#
|
24
|
+
# * The transitive closure tuple table and your link table must have the
|
25
|
+
# same column names for ancestor id/type and descendant id/type.
|
26
|
+
# * You will not be able to use any methods defined on your link model on
|
27
|
+
# the transitive closure model.
|
28
|
+
#
|
29
|
+
# === Available methods
|
30
|
+
#
|
31
|
+
# The following class methods are available on transitive closure link
|
32
|
+
# models:
|
33
|
+
#
|
34
|
+
# [linking(a, b)]
|
35
|
+
# Returns all links (direct or indirect) linking +a+ and +b+.
|
36
|
+
# [ancestor_type(type)]
|
37
|
+
# Behaves identically to the ancestor_type named scope defined in
|
38
|
+
# Dagnabit::Link::NamedScopes.
|
39
|
+
# [descendant_type(type)]
|
40
|
+
# Behaves identically to the descendant_type named scope defined in
|
41
|
+
# Dagnabit::Link::NamedScopes.
|
42
|
+
#
|
43
|
+
# The following instance methods are available on transitive closure link
|
44
|
+
# models:
|
45
|
+
#
|
46
|
+
# [ancestor]
|
47
|
+
# Returns the ancestor of this link. Behaves identically to the
|
48
|
+
# ancestor association defined in Dagnabit::Link::Associations.
|
49
|
+
# [descendant]
|
50
|
+
# Returns the descendant of this link. Behaves identically to the
|
51
|
+
# descendant association defined in Dagnabit::Link::Associations.
|
52
|
+
#
|
53
|
+
module TransitiveClosureLinkModel
|
54
|
+
attr_reader :transitive_closure_class
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
#
|
59
|
+
# Generates the transitive closure model.
|
60
|
+
#
|
61
|
+
def generate_transitive_closure_link_model(options)
|
62
|
+
original_class = self
|
63
|
+
|
64
|
+
klass = Class.new(ActiveRecord::Base) do
|
65
|
+
extend Dagnabit::Link::Configuration
|
66
|
+
|
67
|
+
configure_acts_as_dag_link(options)
|
68
|
+
set_table_name original_class.unquoted_transitive_closure_table_name
|
69
|
+
end
|
70
|
+
|
71
|
+
@transitive_closure_class = const_set(transitive_closure_class_name, klass)
|
72
|
+
|
73
|
+
# reflections and named scopes aren't properly created in anonymous
|
74
|
+
# models, so we need to do that work after the model has been named
|
75
|
+
@transitive_closure_class.extend(Dagnabit::Link::Associations)
|
76
|
+
@transitive_closure_class.extend(Dagnabit::Link::NamedScopes)
|
77
|
+
@transitive_closure_class.named_scope :linking, lambda { |from, to|
|
78
|
+
{ :conditions => { ancestor_id_column => from.id,
|
79
|
+
ancestor_type_column => from.class.name,
|
80
|
+
descendant_id_column => to.id,
|
81
|
+
descendant_type_column => to.class.name } }
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'dagnabit/link/transitive_closure_recalculation/on_create'
|
2
|
+
require 'dagnabit/link/transitive_closure_recalculation/on_destroy'
|
3
|
+
require 'dagnabit/link/transitive_closure_recalculation/on_update'
|
4
|
+
|
5
|
+
module Dagnabit
|
6
|
+
module Link
|
7
|
+
#
|
8
|
+
# Code to do the heavy lifting of maintaining the transitive closure of the
|
9
|
+
# dag after edge create, destroy, and update.
|
10
|
+
#
|
11
|
+
module TransitiveClosureRecalculation
|
12
|
+
include OnCreate
|
13
|
+
include OnDestroy
|
14
|
+
include OnUpdate
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'dagnabit/link/transitive_closure_recalculation/utilities'
|
2
|
+
|
3
|
+
module Dagnabit
|
4
|
+
module Link
|
5
|
+
module TransitiveClosureRecalculation
|
6
|
+
module OnCreate
|
7
|
+
include Utilities
|
8
|
+
|
9
|
+
def after_create
|
10
|
+
super
|
11
|
+
update_transitive_closure_for_create
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def update_transitive_closure_for_create
|
17
|
+
tc = self.class.transitive_closure_table_name
|
18
|
+
tc_aid, tc_did, tc_atype, tc_dtype = quoted_dag_link_column_names
|
19
|
+
aid, did, atype, dtype = quoted_dag_link_values
|
20
|
+
all_columns = all_quoted_column_names.join(',')
|
21
|
+
all_values = all_quoted_column_values.join(',')
|
22
|
+
|
23
|
+
with_temporary_edge_tables('new', 'delta') do |new, delta|
|
24
|
+
extend_connected_paths(new, tc_aid, tc_did, tc_atype, tc_dtype, tc, aid, did, atype, dtype)
|
25
|
+
append_created_edge(new, all_columns, all_values)
|
26
|
+
synchronize_transitive_closure(new, delta, all_columns, tc, tc_aid, tc_did, tc_atype, tc_dtype)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# determine:
|
32
|
+
# * all paths constructed by adding (a, b) to the back of paths
|
33
|
+
# ending at a (first subselect)
|
34
|
+
# * all paths constructed by adding (a, b) to the front of paths
|
35
|
+
# starting at b (second subselect)
|
36
|
+
# * all paths constructed by adding (a, b) in the middle of paths
|
37
|
+
# starting at a and ending at b (third subselect)
|
38
|
+
#
|
39
|
+
def extend_connected_paths(new, tc_aid, tc_did, tc_atype, tc_dtype, tc, aid, did, atype, dtype)
|
40
|
+
connection.execute <<-END
|
41
|
+
INSERT INTO #{new} (#{tc_aid}, #{tc_did}, #{tc_atype}, #{tc_dtype})
|
42
|
+
SELECT * FROM (
|
43
|
+
SELECT
|
44
|
+
TC.#{tc_aid}, #{did}, TC.#{tc_atype}, #{dtype}
|
45
|
+
FROM
|
46
|
+
#{tc} AS TC
|
47
|
+
WHERE
|
48
|
+
TC.#{tc_did} = #{aid} AND TC.#{tc_dtype} = #{atype}
|
49
|
+
UNION
|
50
|
+
SELECT
|
51
|
+
#{aid}, TC.#{tc_did}, #{atype}, TC.#{tc_dtype}
|
52
|
+
FROM
|
53
|
+
#{tc} AS TC
|
54
|
+
WHERE
|
55
|
+
TC.#{tc_aid} = #{did} AND TC.#{tc_atype} = #{dtype}
|
56
|
+
UNION
|
57
|
+
SELECT
|
58
|
+
TC1.#{tc_aid}, TC2.#{tc_did}, TC1.#{tc_atype}, TC2.#{tc_dtype}
|
59
|
+
FROM
|
60
|
+
#{tc} AS TC1, #{tc} AS TC2
|
61
|
+
WHERE
|
62
|
+
TC1.#{tc_did} = #{aid} AND TC1.#{tc_dtype} = #{atype}
|
63
|
+
AND
|
64
|
+
TC2.#{tc_aid} = #{did} AND TC2.#{tc_atype} = #{dtype}
|
65
|
+
) AS tmp0
|
66
|
+
END
|
67
|
+
end
|
68
|
+
|
69
|
+
def append_created_edge(new, all_columns, all_values)
|
70
|
+
connection.execute <<-END
|
71
|
+
INSERT INTO #{new} (#{all_columns}) VALUES (#{all_values})
|
72
|
+
END
|
73
|
+
end
|
74
|
+
|
75
|
+
def synchronize_transitive_closure(new, delta, all_columns, tc, tc_aid, tc_did, tc_atype, tc_dtype)
|
76
|
+
#
|
77
|
+
# ...filter out duplicates...
|
78
|
+
#
|
79
|
+
connection.execute <<-END
|
80
|
+
INSERT INTO #{delta}
|
81
|
+
SELECT * FROM #{new} AS T
|
82
|
+
WHERE NOT EXISTS (
|
83
|
+
SELECT *
|
84
|
+
FROM
|
85
|
+
#{tc} AS TC
|
86
|
+
WHERE
|
87
|
+
TC.#{tc_aid} = T.#{tc_aid} AND TC.#{tc_did} = T.#{tc_did}
|
88
|
+
AND
|
89
|
+
TC.#{tc_atype} = T.#{tc_atype} AND TC.#{tc_dtype} = T.#{tc_dtype}
|
90
|
+
)
|
91
|
+
END
|
92
|
+
|
93
|
+
#
|
94
|
+
# ...and update the transitive closure table
|
95
|
+
#
|
96
|
+
connection.execute <<-END
|
97
|
+
INSERT INTO #{tc} (#{all_columns})
|
98
|
+
SELECT * FROM #{delta}
|
99
|
+
END
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|