mochigome 0.0.9 → 0.0.10
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/lib/mochigome_ver.rb +1 -1
- data/lib/model_extensions.rb +4 -4
- data/lib/query.rb +28 -11
- data/lib/subgroup_model.rb +1 -1
- data/test/app_root/app/models/sale.rb +5 -1
- data/test/unit/query_test.rb +17 -2
- metadata +4 -4
data/lib/mochigome_ver.rb
CHANGED
data/lib/model_extensions.rb
CHANGED
@@ -305,13 +305,13 @@ module Mochigome
|
|
305
305
|
|
306
306
|
if vals.size == 1
|
307
307
|
vals << :id # TODO : Use real primary key, only do this for appropriate agg funcs
|
308
|
-
elsif vals.size
|
308
|
+
elsif vals.empty? || vals.size > 3
|
309
309
|
raise ModelSetupError.new "Wrong # of components for agg: #{obj.inspect}"
|
310
310
|
end
|
311
311
|
|
312
312
|
{
|
313
|
-
:agg_proc => aggregation_proc(vals
|
314
|
-
:value_proc => value_proc(vals
|
315
|
-
}
|
313
|
+
:agg_proc => aggregation_proc(vals[0]),
|
314
|
+
:value_proc => value_proc(vals[1])
|
315
|
+
}.merge(vals[2] || {})
|
316
316
|
end
|
317
317
|
end
|
data/lib/query.rb
CHANGED
@@ -76,23 +76,41 @@ module Mochigome
|
|
76
76
|
|
77
77
|
agg_data_rel = self.class.relation_over_path(agg_path, focus_rel.dup)
|
78
78
|
agg_data_rel = access_filtered_relation(agg_data_rel, @layers_path + agg_path)
|
79
|
-
data_tbl = Arel::Table.new(data_model.table_name)
|
80
79
|
agg_fields = data_model.mochigome_aggregation_settings.options[:fields].reject{|a| a[:in_ruby]}
|
80
|
+
agg_joined_models = @layers_path + agg_path
|
81
81
|
agg_fields.each_with_index do |a, i|
|
82
|
-
|
82
|
+
(a[:joins] || []).each do |m|
|
83
|
+
unless agg_joined_models.include?(m)
|
84
|
+
cand = nil
|
85
|
+
agg_joined_models.each do |agg_join_src_m|
|
86
|
+
p = self.class.path_thru([agg_join_src_m, m])
|
87
|
+
if p && (cand.nil? || p.size < cand.size)
|
88
|
+
cand = p
|
89
|
+
end
|
90
|
+
end
|
91
|
+
if cand
|
92
|
+
agg_data_rel = self.class.relation_over_path(cand, agg_data_rel)
|
93
|
+
agg_joined_models += cand
|
94
|
+
else
|
95
|
+
raise QueryError.new("Can't join from query to agg join model #{m.name}") # TODO: Test this
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
d_expr = a[:value_proc].call(data_model.arel_table)
|
100
|
+
agg_data_rel.project(d_expr.as("d#{i}"))
|
83
101
|
end
|
84
102
|
|
85
103
|
@aggregate_rels[focus_model][data_model] = (0..key_cols.length).map{|n|
|
86
104
|
lambda {|cond|
|
87
105
|
d_rel = agg_data_rel.dup
|
88
|
-
d_cols = key_cols.take(n) + [
|
106
|
+
d_cols = key_cols.take(n) + [data_model.arel_primary_key]
|
89
107
|
d_cols.each_with_index do |col, i|
|
90
108
|
d_rel.project(col.as("g#{i}")).group(col)
|
91
109
|
end
|
92
110
|
d_rel.where(cond) if cond
|
93
111
|
|
94
|
-
# FIXME: This subtable won't be necessary for all
|
95
|
-
# When we can avoid it, we should,
|
112
|
+
# FIXME: This subtable won't be necessary for all aggregation funcs.
|
113
|
+
# When we can avoid it, we should, for performance.
|
96
114
|
a_rel = Arel::SelectManager.new(
|
97
115
|
Arel::Table.engine,
|
98
116
|
Arel.sql("(#{d_rel.to_sql}) as mochigome_data")
|
@@ -122,16 +140,14 @@ module Mochigome
|
|
122
140
|
if cond.is_a?(Array)
|
123
141
|
return root if cond.empty?
|
124
142
|
cond = cond.inject(nil) do |expr, obj|
|
125
|
-
|
126
|
-
tbl = Arel::Table.new(cls.table_name)
|
127
|
-
subexpr = tbl[cls.primary_key].eq(obj.id)
|
143
|
+
subexpr = obj.class.arel_primary_key.eq(obj.id)
|
128
144
|
expr ? expr.or(subexpr) : subexpr
|
129
145
|
end
|
130
146
|
end
|
131
147
|
if cond
|
132
148
|
self.class.expr_tables(cond).each do |t|
|
133
149
|
raise QueryError.new("Condition table #{t} not in layer list") unless
|
134
|
-
@layers_path.any?{|m| m.table_name == t}
|
150
|
+
@layers_path.any?{|m| m.real_model? && m.table_name == t}
|
135
151
|
end
|
136
152
|
end
|
137
153
|
|
@@ -270,6 +286,7 @@ module Mochigome
|
|
270
286
|
|
271
287
|
# TODO: Move the stuff below into its own module
|
272
288
|
|
289
|
+
# Take an expression and return a Set of all tables it references
|
273
290
|
def self.expr_tables(e)
|
274
291
|
# TODO: This is kind of hacky, Arel probably has a better way
|
275
292
|
# to do this with its API.
|
@@ -287,9 +304,9 @@ module Mochigome
|
|
287
304
|
@@shortest_paths = {}
|
288
305
|
|
289
306
|
def self.relation_over_path(path, rel = nil)
|
290
|
-
# Project ensures that we don't return a Table even if path is empty
|
291
307
|
real_path = path.map{|e| (e.real_model? ? e : e.model)}.uniq
|
292
|
-
|
308
|
+
# Project ensures that we return a Rel, not a Table, even if path is empty
|
309
|
+
rel ||= real_path.first.arel_table.project
|
293
310
|
(0..(real_path.size-2)).each do |i|
|
294
311
|
rel = relation_func(real_path[i], real_path[i+1]).call(rel)
|
295
312
|
end
|
data/lib/subgroup_model.rb
CHANGED
@@ -54,7 +54,7 @@ module Mochigome
|
|
54
54
|
unless c.is_a?(Hash) && c.size == 1 && c[@attr].is_a?(Array)
|
55
55
|
raise QueryError.new("Invalid conditions given to SubgroupModel#all")
|
56
56
|
end
|
57
|
-
recs = c[@attr].map do |val|
|
57
|
+
recs = c[@attr].compact.map do |val|
|
58
58
|
SubgroupPseudoRecord.new(self, val)
|
59
59
|
end
|
60
60
|
# TODO: Support some kind of custom ordering
|
data/test/unit/query_test.rb
CHANGED
@@ -200,6 +200,7 @@ describe Mochigome::Query do
|
|
200
200
|
# Store X, Product C
|
201
201
|
assert_equal "Product C", (data_node/0/0/1).name
|
202
202
|
assert_equal 3, (data_node/0/0/1)['Sales count']
|
203
|
+
assert_equal (3*@product_c.price).to_s, (data_node/0/0/1)['Gross'].to_s
|
203
204
|
# Store Z, Product C
|
204
205
|
assert_equal "Product C", (data_node/1/1/0).name
|
205
206
|
assert_equal 2, (data_node/1/1/0)['Sales count']
|
@@ -211,6 +212,7 @@ describe Mochigome::Query do
|
|
211
212
|
# Store Z, Product C
|
212
213
|
assert_equal "Product C", (data_node/1/0/0).name
|
213
214
|
assert_equal 2, (data_node/1/0/0)['Sales count']
|
215
|
+
assert_equal (2*@product_c.price).to_s, (data_node/1/0/0)['Gross'].to_s
|
214
216
|
end
|
215
217
|
|
216
218
|
it "collects aggregate data in subgroups" do
|
@@ -325,6 +327,19 @@ describe Mochigome::Query do
|
|
325
327
|
assert_nil (data_node/0/0/0/0)['Sales count']
|
326
328
|
end
|
327
329
|
|
330
|
+
it "can collect aggregate data involving joins to tables not on path" do
|
331
|
+
q = Mochigome::Query.new(
|
332
|
+
[Owner, Store],
|
333
|
+
:aggregate_sources => [Sale]
|
334
|
+
)
|
335
|
+
|
336
|
+
data_node = q.run()
|
337
|
+
assert_equal "John's Store", (data_node/0/0).name
|
338
|
+
assert_equal 8, (data_node/0/0)['Sales count']
|
339
|
+
assert_equal (5*@product_a.price + 3*@product_c.price).to_s,
|
340
|
+
(data_node/0/0)['Gross'].to_s
|
341
|
+
end
|
342
|
+
|
328
343
|
it "can do conditional counts" do
|
329
344
|
q = Mochigome::Query.new(
|
330
345
|
[Category],
|
@@ -447,7 +462,7 @@ describe Mochigome::Query do
|
|
447
462
|
}
|
448
463
|
end
|
449
464
|
q = Mochigome::Query.new([Product], :access_filter => af)
|
450
|
-
dn = q.run
|
465
|
+
dn = q.run
|
451
466
|
assert_equal 4, dn.children.size
|
452
467
|
refute dn.children.any?{|c| c.name == "Product E"}
|
453
468
|
end
|
@@ -461,7 +476,7 @@ describe Mochigome::Query do
|
|
461
476
|
}
|
462
477
|
end
|
463
478
|
q = Mochigome::Query.new([Product], :access_filter => af)
|
464
|
-
dn = q.run
|
479
|
+
dn = q.run
|
465
480
|
assert_equal 2, dn.children.size
|
466
481
|
refute dn.children.any?{|c| c.name == "Product E"}
|
467
482
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mochigome
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 10
|
10
|
+
version: 0.0.10
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- David Mike Simon
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-04-01 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
version_requirements: &id001 !ruby/object:Gem::Requirement
|