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