activefacts 0.7.0 → 0.7.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/README.rdoc +0 -3
- data/Rakefile +7 -5
- data/bin/afgen +5 -2
- data/bin/cql +3 -2
- data/examples/CQL/Genealogy.cql +3 -3
- data/examples/CQL/Metamodel.cql +10 -7
- data/examples/CQL/MultiInheritance.cql +2 -1
- data/examples/CQL/OilSupply.cql +4 -4
- data/examples/CQL/Orienteering.cql +2 -2
- data/lib/activefacts.rb +2 -1
- data/lib/activefacts/api.rb +21 -2
- data/lib/activefacts/api/concept.rb +52 -39
- data/lib/activefacts/api/constellation.rb +8 -6
- data/lib/activefacts/api/entity.rb +41 -37
- data/lib/activefacts/api/instance.rb +5 -3
- data/lib/activefacts/api/numeric.rb +28 -21
- data/lib/activefacts/api/role.rb +29 -43
- data/lib/activefacts/api/standard_types.rb +8 -3
- data/lib/activefacts/api/support.rb +4 -4
- data/lib/activefacts/api/value.rb +9 -3
- data/lib/activefacts/api/vocabulary.rb +17 -7
- data/lib/activefacts/cql.rb +10 -7
- data/lib/activefacts/cql/CQLParser.treetop +6 -0
- data/lib/activefacts/cql/Concepts.treetop +32 -26
- data/lib/activefacts/cql/DataTypes.treetop +6 -0
- data/lib/activefacts/cql/Expressions.treetop +6 -0
- data/lib/activefacts/cql/FactTypes.treetop +6 -0
- data/lib/activefacts/cql/Language/English.treetop +9 -3
- data/lib/activefacts/cql/LexicalRules.treetop +6 -0
- data/lib/activefacts/cql/Rakefile +8 -0
- data/lib/activefacts/cql/parser.rb +4 -2
- data/lib/activefacts/generate/absorption.rb +20 -28
- data/lib/activefacts/generate/cql.rb +28 -16
- data/lib/activefacts/generate/cql/html.rb +327 -321
- data/lib/activefacts/generate/null.rb +7 -3
- data/lib/activefacts/generate/oo.rb +19 -15
- data/lib/activefacts/generate/ordered.rb +457 -461
- data/lib/activefacts/generate/ruby.rb +12 -4
- data/lib/activefacts/generate/sql/server.rb +42 -10
- data/lib/activefacts/generate/text.rb +7 -3
- data/lib/activefacts/input/cql.rb +55 -28
- data/lib/activefacts/input/orm.rb +32 -22
- data/lib/activefacts/persistence.rb +5 -0
- data/lib/activefacts/persistence/columns.rb +66 -32
- data/lib/activefacts/persistence/foreignkey.rb +29 -5
- data/lib/activefacts/persistence/index.rb +57 -25
- data/lib/activefacts/persistence/reference.rb +65 -30
- data/lib/activefacts/persistence/tables.rb +28 -17
- data/lib/activefacts/support.rb +8 -0
- data/lib/activefacts/version.rb +7 -1
- data/lib/activefacts/vocabulary.rb +4 -2
- data/lib/activefacts/vocabulary/extensions.rb +12 -10
- data/lib/activefacts/vocabulary/metamodel.rb +24 -23
- data/spec/api/autocounter.rb +2 -2
- data/spec/api/entity_type.rb +2 -2
- data/spec/api/instance.rb +61 -30
- data/spec/api/roles.rb +9 -9
- data/spec/cql_parse_spec.rb +1 -0
- data/spec/norma_tables_spec.rb +3 -3
- metadata +8 -4
@@ -1,4 +1,9 @@
|
|
1
1
|
#
|
2
|
+
# ActiveFacts Relational mapping and persistence.
|
3
|
+
# Columns in a relational table; each is derived from a sequence of References.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
2
7
|
# Each Reference from a Concept creates one or more Columns.
|
3
8
|
# A reference to a simple valuetype creates a single column, as
|
4
9
|
# does a reference to a table entity identified by a single value.
|
@@ -10,15 +15,18 @@
|
|
10
15
|
# table, a reference to that entity creates multiple columns,
|
11
16
|
# a multi-part foreign key.
|
12
17
|
#
|
13
|
-
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
14
|
-
#
|
15
18
|
|
16
19
|
module ActiveFacts
|
17
|
-
module
|
20
|
+
module Persistence #:nodoc:
|
18
21
|
|
19
22
|
class Column
|
20
|
-
|
23
|
+
include Metamodel
|
24
|
+
|
25
|
+
def initialize(reference = nil) #:nodoc:
|
26
|
+
references << reference if reference
|
27
|
+
end
|
21
28
|
|
29
|
+
# A Column is created from a path through an array of References to a ValueType
|
22
30
|
def references
|
23
31
|
@references ||= []
|
24
32
|
end
|
@@ -33,6 +41,7 @@ module ActiveFacts
|
|
33
41
|
end
|
34
42
|
end
|
35
43
|
|
44
|
+
# How many of the initial references are involved in full absorption of an EntityType into this column's table
|
36
45
|
def absorption_level
|
37
46
|
l = 0
|
38
47
|
@references.detect do |ref|
|
@@ -42,15 +51,13 @@ module ActiveFacts
|
|
42
51
|
l
|
43
52
|
end
|
44
53
|
|
45
|
-
def
|
46
|
-
references << reference if reference
|
47
|
-
end
|
48
|
-
|
49
|
-
def prepend reference
|
54
|
+
def prepend reference #:nodoc:
|
50
55
|
references.insert 0, reference
|
51
56
|
self
|
52
57
|
end
|
53
58
|
|
59
|
+
# A Column name is a sequence of names (derived from the to_roles of the References)
|
60
|
+
# joined by a joiner string (pass nil to get the original array of names)
|
54
61
|
def name(joiner = "")
|
55
62
|
last_name = ""
|
56
63
|
names = @references.
|
@@ -61,7 +68,7 @@ module ActiveFacts
|
|
61
68
|
ref.to and
|
62
69
|
ref.to.is_a?(EntityType) and
|
63
70
|
(role_refs = ref.to.preferred_identifier.role_sequence.all_role_ref).size == 1 and
|
64
|
-
role_refs
|
71
|
+
role_refs.only.role == ref.from_role
|
65
72
|
end.
|
66
73
|
inject([]) do |a, ref|
|
67
74
|
names = ref.to_names
|
@@ -85,7 +92,7 @@ module ActiveFacts
|
|
85
92
|
if names.size > 1 and
|
86
93
|
(et = @references.last.from).is_a?(EntityType) and
|
87
94
|
(role_refs = et.preferred_identifier.role_sequence.all_role_ref).size == 1 and
|
88
|
-
role_refs
|
95
|
+
role_refs.only.role == @references.last.to_role and
|
89
96
|
names.last[0...et.name.size].downcase == et.name.downcase
|
90
97
|
names[-1] = names.last[et.name.size..-1]
|
91
98
|
names.pop if names.last == ''
|
@@ -95,14 +102,17 @@ module ActiveFacts
|
|
95
102
|
joiner ? name_array * joiner : name_array
|
96
103
|
end
|
97
104
|
|
105
|
+
# Is this column mandatory or nullable?
|
98
106
|
def is_mandatory
|
99
107
|
!@references.detect{|ref| !ref.is_mandatory}
|
100
108
|
end
|
101
109
|
|
110
|
+
# Is this column an auto-assigned value type?
|
102
111
|
def is_auto_assigned
|
103
112
|
(to = references[-1].to) && to.is_auto_assigned
|
104
113
|
end
|
105
114
|
|
115
|
+
# What's the underlying SQL data type of this column?
|
106
116
|
def type
|
107
117
|
params = {}
|
108
118
|
restrictions = []
|
@@ -126,6 +136,7 @@ module ActiveFacts
|
|
126
136
|
return [vt.name, params, restrictions]
|
127
137
|
end
|
128
138
|
|
139
|
+
# The comment is the readings from the References expressed as a join
|
129
140
|
def comment
|
130
141
|
@references.map do |ref|
|
131
142
|
(ref.is_mandatory ? "" : "maybe ") +
|
@@ -134,13 +145,13 @@ module ActiveFacts
|
|
134
145
|
end * " and "
|
135
146
|
end
|
136
147
|
|
137
|
-
def to_s
|
148
|
+
def to_s #:nodoc:
|
138
149
|
"#{@references[0].from.name} column #{name('.')}"
|
139
150
|
end
|
140
151
|
end
|
141
152
|
|
142
153
|
class Reference
|
143
|
-
def columns(excluded_supertypes)
|
154
|
+
def columns(excluded_supertypes) #:nodoc:
|
144
155
|
kind = ""
|
145
156
|
cols =
|
146
157
|
if is_unary
|
@@ -167,16 +178,24 @@ module ActiveFacts
|
|
167
178
|
end
|
168
179
|
end
|
169
180
|
end
|
181
|
+
end
|
170
182
|
|
183
|
+
module Metamodel #:nodoc:
|
184
|
+
# The Concept class is defined in the metamodel; full documentation is not generated.
|
185
|
+
# This section shows the features relevant to relational Persistence.
|
171
186
|
class Concept
|
172
|
-
|
187
|
+
# The array of columns for this Concept's table
|
188
|
+
def columns; @columns; end
|
173
189
|
|
174
|
-
def populate_columns
|
190
|
+
def populate_columns #:nodoc:
|
175
191
|
@columns = all_columns({})
|
176
192
|
end
|
177
193
|
end
|
178
194
|
|
179
|
-
class
|
195
|
+
# The ValueType class is defined in the metamodel; full documentation is not generated.
|
196
|
+
# This section shows the features relevant to relational Persistence.
|
197
|
+
class ValueType < Concept
|
198
|
+
# The identifier_columns for a ValueType can only ever be the self-value role that was injected
|
180
199
|
def identifier_columns
|
181
200
|
debug :columns, "Identifier Columns for #{name}" do
|
182
201
|
raise "Illegal call to identifier_columns for absorbed ValueType #{name}" unless is_table
|
@@ -184,23 +203,27 @@ module ActiveFacts
|
|
184
203
|
end
|
185
204
|
end
|
186
205
|
|
187
|
-
|
206
|
+
# When creating a foreign key to this ValueType, what columns must we include?
|
207
|
+
# This must be a fresh copy, because the columns will have References prepended
|
208
|
+
def reference_columns(excluded_supertypes) #:nodoc:
|
188
209
|
debug :columns, "Reference Columns for #{name}" do
|
189
210
|
if is_table
|
190
|
-
[Column.new(self_value_reference)]
|
211
|
+
[ActiveFacts::Persistence::Column.new(self_value_reference)]
|
191
212
|
else
|
192
|
-
[Column.new]
|
213
|
+
[ActiveFacts::Persistence::Column.new]
|
193
214
|
end
|
194
215
|
end
|
195
216
|
end
|
196
217
|
|
197
|
-
|
218
|
+
# When absorbing this ValueType, what columns must be absorbed?
|
219
|
+
# This must be a fresh copy, because the columns will have References prepended.
|
220
|
+
def all_columns(excluded_supertypes) #:nodoc:
|
198
221
|
columns = []
|
199
222
|
debug :columns, "All Columns for #{name}" do
|
200
223
|
if is_table
|
201
224
|
self_value_reference
|
202
225
|
else
|
203
|
-
columns << Column.new
|
226
|
+
columns << ActiveFacts::Persistence::Column.new
|
204
227
|
end
|
205
228
|
references_from.each do |ref|
|
206
229
|
debug :columns, "Columns absorbed via #{ref}" do
|
@@ -211,19 +234,23 @@ module ActiveFacts
|
|
211
234
|
columns
|
212
235
|
end
|
213
236
|
|
214
|
-
|
237
|
+
# If someone asks for this, it's because it's needed, so create it.
|
238
|
+
def self_value_reference #:nodoc:
|
215
239
|
# Make a reference for the self-value column
|
216
|
-
@self_value_reference ||= Reference.new(self, nil).tabulate
|
240
|
+
@self_value_reference ||= ActiveFacts::Persistence::Reference.new(self, nil).tabulate
|
217
241
|
end
|
218
242
|
end
|
219
243
|
|
220
|
-
class
|
244
|
+
# The EntityType class is defined in the metamodel; full documentation is not generated.
|
245
|
+
# This section shows the features relevant to relational Persistence.
|
246
|
+
class EntityType < Concept
|
247
|
+
# The identifier_columns for an EntityType are the columns that result from the identifying roles
|
221
248
|
def identifier_columns
|
222
249
|
debug :columns, "Identifier Columns for #{name}" do
|
223
250
|
if absorbed_via and
|
224
251
|
# If this is a subtype that has its own identification, use that.
|
225
|
-
(
|
226
|
-
|
252
|
+
(all_type_inheritance_as_subtype.size == 0 ||
|
253
|
+
all_type_inheritance_as_subtype.detect{|ti| ti.provides_identification })
|
227
254
|
return absorbed_via.from.identifier_columns
|
228
255
|
end
|
229
256
|
|
@@ -235,13 +262,15 @@ module ActiveFacts
|
|
235
262
|
end
|
236
263
|
end
|
237
264
|
|
238
|
-
|
265
|
+
# When creating a foreign key to this EntityType, what columns must we include (the identifier columns)?
|
266
|
+
# This must be a fresh copy, because the columns will have References prepended
|
267
|
+
def reference_columns(excluded_supertypes) #:nodoc:
|
239
268
|
debug :columns, "Reference Columns for #{name}" do
|
240
269
|
|
241
270
|
if absorbed_via and
|
242
271
|
# If this is a subtype that has its own identification, use that.
|
243
|
-
(
|
244
|
-
|
272
|
+
(all_type_inheritance_as_subtype.size == 0 ||
|
273
|
+
all_type_inheritance_as_subtype.detect{|ti| ti.provides_identification })
|
245
274
|
return absorbed_via.from.reference_columns(excluded_supertypes)
|
246
275
|
end
|
247
276
|
|
@@ -257,7 +286,9 @@ module ActiveFacts
|
|
257
286
|
end
|
258
287
|
end
|
259
288
|
|
260
|
-
|
289
|
+
# When absorbing this EntityType, what columns must be absorbed?
|
290
|
+
# This must be a fresh copy, because the columns will have References prepended.
|
291
|
+
def all_columns(excluded_supertypes) #:nodoc:
|
261
292
|
debug :columns, "All Columns for #{name}" do
|
262
293
|
columns = []
|
263
294
|
sups = supertypes
|
@@ -287,15 +318,18 @@ module ActiveFacts
|
|
287
318
|
end
|
288
319
|
end
|
289
320
|
|
321
|
+
# The Vocabulary class is defined in the metamodel; full documentation is not generated.
|
322
|
+
# This section shows the features relevant to relational Persistence.
|
290
323
|
class Vocabulary
|
291
|
-
#
|
324
|
+
# Make schema transformations like adding ValueType self-value columns (and later, Rails-friendly ID fields).
|
325
|
+
# Override this method to change the transformations
|
292
326
|
def finish_schema
|
293
327
|
all_feature.each do |feature|
|
294
328
|
feature.self_value_reference if feature.is_a?(ValueType) && feature.is_table
|
295
329
|
end
|
296
330
|
end
|
297
331
|
|
298
|
-
def populate_all_columns
|
332
|
+
def populate_all_columns #:nodoc:
|
299
333
|
# REVISIT: Is now a good time to apply schema transforms or should this be more explicit?
|
300
334
|
finish_schema
|
301
335
|
|
@@ -1,15 +1,38 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts Relational mapping and persistence.
|
3
|
+
# A ForeignKey exists for every Reference from a Concept to another Concept that's a table.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
|
+
#
|
1
7
|
module ActiveFacts
|
2
|
-
module
|
3
|
-
|
8
|
+
module Persistence
|
4
9
|
class ForeignKey
|
5
|
-
|
6
|
-
def
|
10
|
+
# What table (Concept) is the FK from?
|
11
|
+
def from; @from; end
|
12
|
+
|
13
|
+
# What table (Concept) is the FK to?
|
14
|
+
def to; @to; end
|
15
|
+
|
16
|
+
# What reference created the FK?
|
17
|
+
def reference; @reference; end
|
18
|
+
|
19
|
+
# What columns in the *from* table form the FK
|
20
|
+
def from_columns; @from_columns; end
|
21
|
+
|
22
|
+
# What columns in the *to* table form the identifier
|
23
|
+
def to_columns; @to_columns; end
|
24
|
+
|
25
|
+
def initialize(from, to, fk_ref, from_columns, to_columns) #:nodoc:
|
7
26
|
@from, @to, @fk_ref, @from_columns, @to_columns =
|
8
27
|
from, to, fk_ref, from_columns, to_columns
|
9
28
|
end
|
10
29
|
end
|
30
|
+
end
|
11
31
|
|
32
|
+
module Metamodel #:nodoc:
|
12
33
|
class Concept
|
34
|
+
# When an EntityType is fully absorbed, its foreign keys are too.
|
35
|
+
# Return an Array of Reference paths for such absorbed FKs
|
13
36
|
def all_absorbed_foreign_key_reference_path
|
14
37
|
references_from.inject([]) do |array, ref|
|
15
38
|
if ref.is_simple_reference
|
@@ -23,6 +46,7 @@ module ActiveFacts
|
|
23
46
|
end
|
24
47
|
end
|
25
48
|
|
49
|
+
# Return an array of all the foreign keys from this table
|
26
50
|
def foreign_keys
|
27
51
|
fk_ref_paths = all_absorbed_foreign_key_reference_path
|
28
52
|
|
@@ -78,7 +102,7 @@ module ActiveFacts
|
|
78
102
|
end
|
79
103
|
debug :fk, "to_columns in #{to.name}: #{to_columns.map { |column| column ? column.name : "OOPS!" }*", "}"
|
80
104
|
|
81
|
-
ForeignKey.new(self, to, fk_ref_path[-1], from_columns, to_columns)
|
105
|
+
ActiveFacts::Persistence::ForeignKey.new(self, to, fk_ref_path[-1], from_columns, to_columns)
|
82
106
|
end
|
83
107
|
end
|
84
108
|
end
|
@@ -1,31 +1,49 @@
|
|
1
1
|
#
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# ActiveFacts Relational mapping and persistence.
|
3
|
+
# An Index on a Concept is used to represent a unique constraint across roles absorbed
|
4
|
+
# into that concept's table.
|
4
5
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
6
|
+
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
8
7
|
#
|
9
8
|
|
10
9
|
module ActiveFacts
|
11
|
-
module
|
10
|
+
module Persistence
|
12
11
|
class Index
|
13
|
-
|
12
|
+
# The UniquenessConstraint that created this index
|
13
|
+
def uniqueness_constraint; @uniqueness_constraint; end
|
14
|
+
|
15
|
+
# The table that the index is on
|
16
|
+
def on; @on; end
|
17
|
+
|
18
|
+
# If a non-mandatory reference was absorbed, only the non-nil instances are unique.
|
19
|
+
# Return the Concept that was absorbed, which might differ from this Index's table.
|
20
|
+
def over; @over; end
|
21
|
+
|
22
|
+
# Return the array of columns in this index
|
23
|
+
def columns; @columns; end
|
24
|
+
|
25
|
+
# Is this index the primary key for this table?
|
26
|
+
def is_primary; @is_primary; end
|
27
|
+
|
28
|
+
# Is this index unique?
|
29
|
+
def is_unique; @is_unique; end
|
14
30
|
|
15
31
|
# An Index arises from a uniqueness constraint and applies to a table,
|
16
32
|
# but because the UC may actually be over an object absorbed into the table,
|
17
33
|
# we must record that object also.
|
18
34
|
# We record the columns it's over, whether it's primary (for 'over'),
|
19
35
|
# and whether it's unique (always, at present)
|
20
|
-
def initialize(uc, on, over, columns, is_primary, is_unique = true)
|
36
|
+
def initialize(uc, on, over, columns, is_primary, is_unique = true) #:nodoc:
|
21
37
|
@uniqueness_constraint, @on, @over, @columns, @is_primary, @is_unique =
|
22
38
|
uc, on, over, columns, is_primary, is_unique
|
23
39
|
end
|
24
40
|
|
41
|
+
# The name that was assigned (perhaps implicitly by NORMA)
|
25
42
|
def real_name
|
26
43
|
@uniqueness_constraint.name && @uniqueness_constraint.name != '' ? @uniqueness_constraint.name : nil
|
27
44
|
end
|
28
45
|
|
46
|
+
# This name is either the name explicitly assigned (if any) or is constructed to form a unique index name.
|
29
47
|
def name
|
30
48
|
uc = @uniqueness_constraint
|
31
49
|
r = real_name
|
@@ -35,35 +53,41 @@ module ActiveFacts
|
|
35
53
|
(uc.is_preferred_identifier ? "" : "By"+column_names*"")
|
36
54
|
end
|
37
55
|
|
38
|
-
|
39
|
-
columns.map{|column| column.name.sub(/^#{over.name}/,'')}
|
40
|
-
end
|
41
|
-
|
56
|
+
# An array of the names of the columns this index covers
|
42
57
|
def column_names
|
43
58
|
columns.map{|column| column.name}
|
44
59
|
end
|
45
60
|
|
61
|
+
# An array of the names of the columns this index covers, with some lexical truncations.
|
62
|
+
def abbreviated_column_names
|
63
|
+
columns.map{|column| column.name.sub(/^#{over.name}/,'')}
|
64
|
+
end
|
65
|
+
|
66
|
+
# The name of a view that can be created to enforce uniqueness over non-null key values
|
46
67
|
def view_name
|
47
68
|
"#{over.name}#{on == over ? "" : "In"+on.name}"
|
48
69
|
end
|
49
70
|
|
50
|
-
def to_s
|
71
|
+
def to_s #:nodoc:
|
51
72
|
name = @uniqueness_constraint.name
|
52
73
|
colnames = @columns.map(&:name)*", "
|
53
74
|
preferred = @uniqueness_constraint.is_preferred_identifier ? " (preferred)" : ""
|
54
75
|
"Index #{name} on #{@on.name} over #{@over.name}(#{colnames})#{preferred}"
|
55
76
|
end
|
56
77
|
end
|
78
|
+
end
|
57
79
|
|
80
|
+
module Metamodel #:nodoc:
|
58
81
|
class Concept
|
59
|
-
|
82
|
+
# An array of each Index for this table
|
83
|
+
def indices; @indices; end
|
60
84
|
|
61
|
-
def clear_indices
|
85
|
+
def clear_indices #:nodoc:
|
62
86
|
# Clear any previous indices
|
63
87
|
@indices = nil
|
64
88
|
end
|
65
89
|
|
66
|
-
def populate_indices
|
90
|
+
def populate_indices #:nodoc:
|
67
91
|
# The absorption path of a column indicates how it came to be in this table.
|
68
92
|
# It might be a direct many:one valuetype relationship, or it might be in such
|
69
93
|
# a relationship to an entity that was absorbed into this table (and so on).
|
@@ -96,14 +120,20 @@ module ActiveFacts
|
|
96
120
|
!pc.max_frequency or # No maximum freq; cannot be a uniqueness constraint
|
97
121
|
pc.max_frequency != 1 or # maximum is not 1
|
98
122
|
pc.role_sequence.all_role_ref.size == 1 && # UniquenessConstraint is over one role
|
99
|
-
(pc.role_sequence.all_role_ref
|
100
|
-
|
123
|
+
((fact_type = pc.role_sequence.all_role_ref.only.role.fact_type).is_a?(TypeInheritance) || # Inheritance
|
124
|
+
fact_type.all_role.size == 1) # Unary
|
101
125
|
# The preceeeding two restrictions exclude the internal UCs created within NORMA.
|
102
126
|
end
|
103
127
|
next unless pcs.size > 0
|
104
128
|
# The columns for this ref_path support the UCs in "pcs".
|
105
129
|
pcs.each do |pc|
|
106
|
-
|
130
|
+
ref_columns = all_column_by_ref_path[ref_path]
|
131
|
+
ordinal = role_ref.ordinal # Position in priority order
|
132
|
+
ref_columns.each_with_index do |column, index|
|
133
|
+
#puts "Adding index column #{column.name} in rank[#{ordinal},#{index}]"
|
134
|
+
# REVISIT: the "index" here might be a duplicate in some cases: change sort_by below to just sort and run the SeparateSubtypes CQL model for example.
|
135
|
+
(columns_by_unique_constraint[pc] ||= []) << [ordinal, index, column]
|
136
|
+
end
|
107
137
|
end
|
108
138
|
hash[role_ref] = all_column_by_ref_path[ref_path]
|
109
139
|
end
|
@@ -112,17 +142,19 @@ module ActiveFacts
|
|
112
142
|
end
|
113
143
|
|
114
144
|
debug :index, "All Indices in #{name}:" do
|
115
|
-
@indices = columns_by_unique_constraint.map do |uc,
|
145
|
+
@indices = columns_by_unique_constraint.map do |uc, columns_with_ordinal|
|
146
|
+
#puts "Index on #{name} over (#{columns_with_ordinal.sort.map{|ca| [ca[0], ca[1], ca[2].name].inspect}})"
|
147
|
+
columns = columns_with_ordinal.sort_by{|ca| [ca[0,2], ca[2].name]}.map{|ca| ca[2]}
|
116
148
|
absorption_level = columns.map(&:absorption_level).min
|
117
149
|
over = columns[0].references[absorption_level].from
|
118
150
|
|
119
151
|
# Absorption through a one-to-one forms a UC that we don't need to enforce using an index:
|
120
|
-
next if over != self and
|
152
|
+
next nil if over != self and
|
121
153
|
over.absorbed_via == columns[0].references[absorption_level-1] and
|
122
154
|
(rrs = uc.role_sequence.all_role_ref).size == 1 and
|
123
|
-
over.absorbed_via.fact_type.all_role.include?(rrs
|
155
|
+
over.absorbed_via.fact_type.all_role.include?(rrs.only.role)
|
124
156
|
|
125
|
-
index = Index.new(
|
157
|
+
index = ActiveFacts::Persistence::Index.new(
|
126
158
|
uc,
|
127
159
|
self,
|
128
160
|
over,
|
@@ -131,14 +163,14 @@ module ActiveFacts
|
|
131
163
|
)
|
132
164
|
debug :index, index
|
133
165
|
index
|
134
|
-
end
|
166
|
+
end.compact
|
135
167
|
end
|
136
168
|
end
|
137
169
|
|
138
170
|
end
|
139
171
|
|
140
172
|
class Vocabulary
|
141
|
-
def populate_all_indices
|
173
|
+
def populate_all_indices #:nodoc:
|
142
174
|
debug :index, "Populating all concept indices" do
|
143
175
|
all_feature.each do |feature|
|
144
176
|
next unless feature.is_a? Concept
|