cubicle 0.1.29 → 0.1.30

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.
@@ -1,122 +1,125 @@
1
- ==0.1.29
2
- *Enabled custom configuring cubicle's mongo connection even if MongoMapper is present, to allow running map reduce jobs
3
- on a slave while MongoMapper operates on the master.
4
-
5
- ==0.1.28
6
- *Updated to latest versions of MongoMapper, Mongo & BSON
7
- ==0.1.26,0.1.27
8
- *Included mustache dependency in gem (whoops) and fixed a bug where an error in cube processing would replace
9
- useful error information with nonsensical error information.
10
-
11
- ==0.1.25
12
- *Modified data extraction to ensure that member and query level aliases are respected in the final result. Also
13
- stopped automatically dropping temporary map reduce tables during query execution for performance reasons. Instead,
14
- added a static method to the Cubicle module, clear_temp_tables, which can be called at application startup or teardown:
15
- Cubicle.clear_temp_tables()
16
-
17
- ==0.1.24
18
- *Added more detail to the profile for the 'find' action
19
-
20
- ==0.1.23
21
- *Fixed profiler's capped collection sizing bug (off by x1000)
22
-
23
- ==0.1.22
24
- *Added profiler, which is off by default, that will profile every operation performed by the aggregation engine
25
- in a collection called cubicle.profiler. It uses a capped collection size at, by default, 250mb, but this can
26
- be configured. Also added basic metadata table for overall aggregation definitions that will record the last processing
27
- time, along with the duration of the processing job, in the collection cubicle.metadata
28
-
29
- ==0.1.21
30
- *Added metadata tables in the database for cubicle to manage aggregation info. This was necessary because previously
31
- I was trying to overload the collection name with metadata, which was making the names longer than MongoDb could support
32
- and causing errors. This change will enable richer monitoring and profiling and optimization in the near future.
33
-
34
- ==0.1.20
35
- *Updated to work with mongo driver 1.0 (and therefore latest versions of MongoMapper)
36
-
37
- ==0.1.19
38
- *Fixed bug that caused cubicle to hang when grouping by days
39
-
40
- ==0.1.18
41
- *Bug fixes
42
-
43
- ==0.1.16
44
- *Added a set of default constants for expression templates, such as date_today, date_today_iso, date_today_utc_iso, etc.
45
- Also added the 'define' method to the query DSL (instead of simply the aggregation DSL) so that you can either define new
46
- named expressions or override an existing named expression or date constant in a query context, instead of only at the
47
- global level
48
-
49
- ==0.1.15
50
- *Added 'difference' calculated measure to aggregation DSL, used in the same was as 'ratio' - so, in your aggregation
51
- definition, you can do 'difference :big_diff, :measure_one, :measure_two'
52
- and the value of :big_diff will be measure_one - measure_two. Also monkey patched Mustache (per suggestion in the
53
- mailing list) to turn of HTML escaping, so that the triple-curly syntax should not be required when using operators
54
- in templated expressions.
55
-
56
- ==0.1.14
57
- *Incorporated the Mustache templating library into aggregation definitions allowing expression to reference previously
58
- defined dimensions, measures, or named expressions (using the syntax: define :expression_name, 'this.my_expression != "that"')
59
- Allows for definitions such as this: avg :average_happy_defects, :expression=>'{{defects}} && this.type=="Happy"'
60
- Also available is 'time_now' to represent the Ruby's Time.now in the form of a javascript date, so '{{time_now}}' will
61
- become 'new Date(2342342)' where 2342342 is Time.now.to_i
62
-
63
- ==0.1.13
64
- *Previous feature broke ratios. Fixed now.
65
-
66
- ==0.1.12
67
- *Added distinct' option to 'count' measure, to allow measures which represent distinct counts of given field/expression value.
68
- ONLY works when the value being distinctly counted is a string.
69
-
70
- ==0.1.11
71
- *Fixed a bug when filtering categorized dimensions in transient queries
72
-
73
- ==0.1.10
74
- *enhanced cubicle aggregation manager to transparently convert several of the basic mongo operators (such as $ne,
75
- $gt, $gte, etc) into javascript for use in the generated $where clause for transient queries. In practice, this
76
- means that you can define a measure as an expression, say dimension :month, :expression=>"'2010-01'", and then
77
- use that dimension name in a standard mongo driver compatible filter expression, without concern for whether
78
- your query is a transient query or a regular query - cubicle will do the appropriate conversion to javascript
79
- in the former case, translating :month=>{"$gte"=>"'2009-12'", "$lt"=>"'2010-02'"} into the functional equivalent of
80
- "$where"=>"'2010-01' >= '2009-12' && '2010-01' < '2010-02'". Actually, an inline function is used to reduce IO
81
- as repeating the expression for each comparison can get out of hand if your expression is large.
82
-
83
- ==0.1.9
84
- * Fixed problems with missing number in bucket intervals. Moved resolution of source measure javascript to
85
- * last moment, to ensure dynamic values don't get accidentally cached too early.
86
-
87
- ==0.1.8
88
- * Added 'bucketize' aliased as 'categorize' to cubicle DSL to allow dimensions to be created from bucketizing
89
- (categorizing based on name) a measure value.
90
-
91
- ==0.1.7
92
- * Minor bug fixes
93
-
94
- ==0.1.6
95
- *Added generic 'condition' argument to member definitions, which allows the specification of a JavaScript expression
96
- which will determine if the measure (or dimension) expression will be evaluated. This capability was available
97
- before via the :expression option, but using a :condition modifier allows for more straightforward (readable) measure
98
- definitions and separates the definition of the actual value from the conditions under which the value is valid.
99
-
100
- ==0.1.5
101
- * Added duration_since (aliased as elapsed, age_since) to calculate the time elapsed since any
102
- given timestamp and the current execution time of the query.
103
-
104
- == 0.1.4
105
- * Cached aggregations were being stored on disk, but not properly utilized. This is now fixed.
106
-
107
- == 0.1.3
108
- * Formalized flat (Cubicle::Data::Table) and hierarchical (Cubicle::Data::Hierarchy) data results from cubicle queries,
109
- and added client side support for rolling up measure values in hierarchical data so that no matter how a given
110
- query is organized hierarchically, a summary of aggregated measure values for each level of data will be available.
111
- * Re-organized codebase
112
- * Bug fixes
113
-
114
- == 0.1.2
115
- * Added ability to calculate durations between two timestamps
116
- * Bug fixes
117
-
118
- == 0.1.1
119
- * Fixed a bug that required a logger to be initialized for the thing to work
120
-
121
- == 0.1.0
122
- * Initial release
1
+ ==0.1.30
2
+ *Added duration in weeks.
3
+
4
+ ==0.1.29
5
+ *Enabled custom configuring cubicle's mongo connection even if MongoMapper is present, to allow running map reduce jobs
6
+ on a slave while MongoMapper operates on the master.
7
+
8
+ ==0.1.28
9
+ *Updated to latest versions of MongoMapper, Mongo & BSON
10
+ ==0.1.26,0.1.27
11
+ *Included mustache dependency in gem (whoops) and fixed a bug where an error in cube processing would replace
12
+ useful error information with nonsensical error information.
13
+
14
+ ==0.1.25
15
+ *Modified data extraction to ensure that member and query level aliases are respected in the final result. Also
16
+ stopped automatically dropping temporary map reduce tables during query execution for performance reasons. Instead,
17
+ added a static method to the Cubicle module, clear_temp_tables, which can be called at application startup or teardown:
18
+ Cubicle.clear_temp_tables()
19
+
20
+ ==0.1.24
21
+ *Added more detail to the profile for the 'find' action
22
+
23
+ ==0.1.23
24
+ *Fixed profiler's capped collection sizing bug (off by x1000)
25
+
26
+ ==0.1.22
27
+ *Added profiler, which is off by default, that will profile every operation performed by the aggregation engine
28
+ in a collection called cubicle.profiler. It uses a capped collection size at, by default, 250mb, but this can
29
+ be configured. Also added basic metadata table for overall aggregation definitions that will record the last processing
30
+ time, along with the duration of the processing job, in the collection cubicle.metadata
31
+
32
+ ==0.1.21
33
+ *Added metadata tables in the database for cubicle to manage aggregation info. This was necessary because previously
34
+ I was trying to overload the collection name with metadata, which was making the names longer than MongoDb could support
35
+ and causing errors. This change will enable richer monitoring and profiling and optimization in the near future.
36
+
37
+ ==0.1.20
38
+ *Updated to work with mongo driver 1.0 (and therefore latest versions of MongoMapper)
39
+
40
+ ==0.1.19
41
+ *Fixed bug that caused cubicle to hang when grouping by days
42
+
43
+ ==0.1.18
44
+ *Bug fixes
45
+
46
+ ==0.1.16
47
+ *Added a set of default constants for expression templates, such as date_today, date_today_iso, date_today_utc_iso, etc.
48
+ Also added the 'define' method to the query DSL (instead of simply the aggregation DSL) so that you can either define new
49
+ named expressions or override an existing named expression or date constant in a query context, instead of only at the
50
+ global level
51
+
52
+ ==0.1.15
53
+ *Added 'difference' calculated measure to aggregation DSL, used in the same was as 'ratio' - so, in your aggregation
54
+ definition, you can do 'difference :big_diff, :measure_one, :measure_two'
55
+ and the value of :big_diff will be measure_one - measure_two. Also monkey patched Mustache (per suggestion in the
56
+ mailing list) to turn of HTML escaping, so that the triple-curly syntax should not be required when using operators
57
+ in templated expressions.
58
+
59
+ ==0.1.14
60
+ *Incorporated the Mustache templating library into aggregation definitions allowing expression to reference previously
61
+ defined dimensions, measures, or named expressions (using the syntax: define :expression_name, 'this.my_expression != "that"')
62
+ Allows for definitions such as this: avg :average_happy_defects, :expression=>'{{defects}} && this.type=="Happy"'
63
+ Also available is 'time_now' to represent the Ruby's Time.now in the form of a javascript date, so '{{time_now}}' will
64
+ become 'new Date(2342342)' where 2342342 is Time.now.to_i
65
+
66
+ ==0.1.13
67
+ *Previous feature broke ratios. Fixed now.
68
+
69
+ ==0.1.12
70
+ *Added distinct' option to 'count' measure, to allow measures which represent distinct counts of given field/expression value.
71
+ ONLY works when the value being distinctly counted is a string.
72
+
73
+ ==0.1.11
74
+ *Fixed a bug when filtering categorized dimensions in transient queries
75
+
76
+ ==0.1.10
77
+ *enhanced cubicle aggregation manager to transparently convert several of the basic mongo operators (such as $ne,
78
+ $gt, $gte, etc) into javascript for use in the generated $where clause for transient queries. In practice, this
79
+ means that you can define a measure as an expression, say dimension :month, :expression=>"'2010-01'", and then
80
+ use that dimension name in a standard mongo driver compatible filter expression, without concern for whether
81
+ your query is a transient query or a regular query - cubicle will do the appropriate conversion to javascript
82
+ in the former case, translating :month=>{"$gte"=>"'2009-12'", "$lt"=>"'2010-02'"} into the functional equivalent of
83
+ "$where"=>"'2010-01' >= '2009-12' && '2010-01' < '2010-02'". Actually, an inline function is used to reduce IO
84
+ as repeating the expression for each comparison can get out of hand if your expression is large.
85
+
86
+ ==0.1.9
87
+ * Fixed problems with missing number in bucket intervals. Moved resolution of source measure javascript to
88
+ * last moment, to ensure dynamic values don't get accidentally cached too early.
89
+
90
+ ==0.1.8
91
+ * Added 'bucketize' aliased as 'categorize' to cubicle DSL to allow dimensions to be created from bucketizing
92
+ (categorizing based on name) a measure value.
93
+
94
+ ==0.1.7
95
+ * Minor bug fixes
96
+
97
+ ==0.1.6
98
+ *Added generic 'condition' argument to member definitions, which allows the specification of a JavaScript expression
99
+ which will determine if the measure (or dimension) expression will be evaluated. This capability was available
100
+ before via the :expression option, but using a :condition modifier allows for more straightforward (readable) measure
101
+ definitions and separates the definition of the actual value from the conditions under which the value is valid.
102
+
103
+ ==0.1.5
104
+ * Added duration_since (aliased as elapsed, age_since) to calculate the time elapsed since any
105
+ given timestamp and the current execution time of the query.
106
+
107
+ == 0.1.4
108
+ * Cached aggregations were being stored on disk, but not properly utilized. This is now fixed.
109
+
110
+ == 0.1.3
111
+ * Formalized flat (Cubicle::Data::Table) and hierarchical (Cubicle::Data::Hierarchy) data results from cubicle queries,
112
+ and added client side support for rolling up measure values in hierarchical data so that no matter how a given
113
+ query is organized hierarchically, a summary of aggregated measure values for each level of data will be available.
114
+ * Re-organized codebase
115
+ * Bug fixes
116
+
117
+ == 0.1.2
118
+ * Added ability to calculate durations between two timestamps
119
+ * Bug fixes
120
+
121
+ == 0.1.1
122
+ * Fixed a bug that required a logger to be initialized for the thing to work
123
+
124
+ == 0.1.0
125
+ * Initial release
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{cubicle}
8
- s.version = "0.1.29"
8
+ s.version = "0.1.30"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nathan Stults"]
12
- s.date = %q{2010-07-06}
12
+ s.date = %q{2010-07-07}
13
13
  s.description = %q{Cubicle provides a dsl and aggregation caching framework for automating the generation, execution and caching of map reduce queries when using MongoDB in Ruby. Cubicle also includes a MongoMapper plugin for quickly performing ad-hoc, multi-level group-by queries against a MongoMapper model.}
14
14
  s.email = %q{hereiam@sonic.net}
15
15
  s.extra_rdoc_files = [
@@ -1,52 +1,53 @@
1
- module Cubicle
2
- class Duration < Measure
3
- attr_accessor :duration_unit, :begin_milestone, :end_milestone, :timestamp_prefix
4
-
5
- def initialize(*args)
6
- super
7
- self.duration_unit = options(:in) || :seconds
8
- self.timestamp_prefix = options :timestamp_prefix, :prefix
9
- self.expression_type = :javascript
10
- #only one item should be left in the hash, the duration map
11
- raise "duration must be provided with a hash with a single entry, where the key represents the starting milestone of a duration and the value represents the ending milestone." if options.length != 1
12
-
13
- self.begin_milestone, self.end_milestone = options.to_a[0]
14
- self.name ||= "#{begin_milestone}_to_#{end_milestone}_#{aggregation_method}".to_sym
15
- end
16
-
17
- def default_aggregation_method
18
- :average
19
- end
20
-
21
- def condition
22
- cond = " && (#{super})" unless super.blank?
23
- "#{milestone_js(:begin)} && #{milestone_js(:end)}#{cond}"
24
- end
25
-
26
- def expression
27
- #prefix these names for the expression
28
- # prefix = "#{self.timestamp_prefix}#{self.timestamp_prefix.blank? ? '' : '.'}"
29
- # ms1,ms2 = [self.begin_milestone,self.end_milestone].map{|ms|ms.to_s=='now' ? "new Date(#{Time.now.to_i*1000})" : "this.#{prefix}#{ms}"}
30
- @expression = "(#{milestone_js(:end)}-#{milestone_js(:begin)})/#{denominator}"
31
- end
32
-
33
- private
34
-
35
- def milestone_js(which)
36
- prefix = "#{self.timestamp_prefix}#{self.timestamp_prefix.blank? ? '' : '.'}"
37
- ms = self.send("#{which.to_s}_milestone")
38
- ms.to_s=='now' ? "new Date(#{Time.now.to_i*1000})" : "this.#{prefix}#{ms}"
39
- end
40
-
41
- def denominator
42
- #Date math results in milliseconds in javascript
43
- case self.duration_unit || :seconds
44
- when :days then "1000/60/60/24.0"
45
- when :hours then "1000/60/60.0"
46
- when :minutes then "1000/60.0"
47
- else "1000.0"
48
- end
49
- end
50
-
51
- end
1
+ module Cubicle
2
+ class Duration < Measure
3
+ attr_accessor :duration_unit, :begin_milestone, :end_milestone, :timestamp_prefix
4
+
5
+ def initialize(*args)
6
+ super
7
+ self.duration_unit = options(:in) || :seconds
8
+ self.timestamp_prefix = options :timestamp_prefix, :prefix
9
+ self.expression_type = :javascript
10
+ #only one item should be left in the hash, the duration map
11
+ raise "duration must be provided with a hash with a single entry, where the key represents the starting milestone of a duration and the value represents the ending milestone." if options.length != 1
12
+
13
+ self.begin_milestone, self.end_milestone = options.to_a[0]
14
+ self.name ||= "#{begin_milestone}_to_#{end_milestone}_#{aggregation_method}".to_sym
15
+ end
16
+
17
+ def default_aggregation_method
18
+ :average
19
+ end
20
+
21
+ def condition
22
+ cond = " && (#{super})" unless super.blank?
23
+ "#{milestone_js(:begin)} && #{milestone_js(:end)}#{cond}"
24
+ end
25
+
26
+ def expression
27
+ #prefix these names for the expression
28
+ # prefix = "#{self.timestamp_prefix}#{self.timestamp_prefix.blank? ? '' : '.'}"
29
+ # ms1,ms2 = [self.begin_milestone,self.end_milestone].map{|ms|ms.to_s=='now' ? "new Date(#{Time.now.to_i*1000})" : "this.#{prefix}#{ms}"}
30
+ @expression = "(#{milestone_js(:end)}-#{milestone_js(:begin)})/#{denominator}"
31
+ end
32
+
33
+ private
34
+
35
+ def milestone_js(which)
36
+ prefix = "#{self.timestamp_prefix}#{self.timestamp_prefix.blank? ? '' : '.'}"
37
+ ms = self.send("#{which.to_s}_milestone")
38
+ ms.to_s=='now' ? "new Date(#{Time.now.to_i*1000})" : "this.#{prefix}#{ms}"
39
+ end
40
+
41
+ def denominator
42
+ #Date math results in milliseconds in javascript
43
+ case self.duration_unit || :seconds
44
+ when :weeks then "1000/60/60/24/7.0"
45
+ when :days then "1000/60/60/24.0"
46
+ when :hours then "1000/60/60.0"
47
+ when :minutes then "1000/60.0"
48
+ else "1000.0"
49
+ end
50
+ end
51
+
52
+ end
52
53
  end
@@ -1,3 +1,3 @@
1
- module Cubicle
2
- VERSION = '0.1.29'
1
+ module Cubicle
2
+ VERSION = '0.1.30'
3
3
  end
@@ -3,8 +3,8 @@ require "test_helper"
3
3
  class DurationTest < ActiveSupport::TestCase
4
4
  context "Querying a cubicle with durations" do
5
5
  setup do
6
- #record 1, ms1 = 1/1/2000, ms2 = 1/2/2000, ms3=1/4/2000
7
- #record 2, ms1 = 1/1/2000, ms2 = 1/3/2000, ms3=1/5/2000
6
+ #record 1, ms1 = 1/1/2000, ms2 = 1/2/2000, ms3=1/4/2000, ms4=1/23/2000
7
+ #record 2, ms1 = 1/1/2000, ms2 = 1/3/2000, ms3=1/5/2000, ms4=1/29/2000
8
8
  Defect.create_duration_test_data
9
9
  end
10
10
  should "correctly calculate durations for each activity" do
@@ -18,10 +18,12 @@ class DurationTest < ActiveSupport::TestCase
18
18
  assert_equal 1 * 60 * 60 * 24, a1["ms1_to_ms2_average"]
19
19
  assert_equal 2 * 60 * 60 * 24, a1["ms2_to_ms3_sum"]
20
20
  assert_equal 3, a1["total_duration"]
21
+ assert_equal 22/7.0, a1["total_duration_in_weeks"]
21
22
 
22
23
  assert_equal 2 * 60 * 60 * 24, a2["ms1_to_ms2_average"]
23
24
  assert_equal 2 * 60 * 60 * 24, a2["ms2_to_ms3_sum"]
24
25
  assert_equal 4, a2["total_duration"]
26
+ assert_equal 28/7.0, a2["total_duration_in_weeks"]
25
27
  end
26
28
  should "correctly aggregate durations" do
27
29
  results = DefectCubicle.query do
@@ -1,57 +1,58 @@
1
- class DefectCubicle
2
- extend Cubicle::Aggregation
3
-
4
- define :preventable, "this.root_cause != 'act_of_god'"
5
- define :product_name, "this.product.name"
6
- define :current_year, "'{{date_today_iso}}'.substring(0,4)"
7
-
8
- date :manufacture_date, :field_name=>'manufacture_date', :alias=>:date
9
- dimension :month, :expression=>'this.manufacture_date.substring(0,7)'
10
- dimension :year, :expression=>'this.manufacture_date.substring(0,4)'
11
-
12
- dimension :manufacture_time
13
-
14
- dimension :product, :expression=>'{{product_name}}'
15
- dimension :region, :field_name=>'plant.address.region'
16
-
17
- dimensions :operator, :outcome
18
-
19
- count :total_defects, :field_name=>'defect_id'
20
- count :distinct_products, :expression=>'{{product}}', :distinct=>true
21
- count :preventable_defects, :expression=>'{{preventable}}'
22
- count :conditioned_preventable,:expression=>'1.0', :condition=>'{{preventable}}'
23
- count :defects_this_year, :expression=>'{{year}} == {{current_year}}'
24
- sum :total_cost, :field_name=>'cost'
25
- avg :avg_cost, :field_name=>'cost'
26
-
27
- #calculated fields
28
- ratio :preventable_pct, :preventable_defects, :total_defects
29
- ratio :distinct_ratio, :distinct_products, :total_defects
30
- difference :inevitable_defects, :total_defects, :preventable_defects
31
- #durations
32
- average_duration :ms1 => :ms2
33
- total_duration :ms2 => :ms3
34
- duration :total_duration, :ms1 => :ms3, :in=>:days
35
- duration :conditional_duration, :ms1 => :ms3, :in=>:days, :condition=>"this.defect_id != 2"
36
- elapsed :ms3, :in=>:days
37
- age_since :avg_time_since_ms3,:ms3, :in=>:days
38
-
39
- #bucketized fields
40
- categorize :avg_cost_category,
41
- :avg_cost, 1..5, :bucket_size=>0.5, :range_start_bump=>0.01 do |bucket_start,bucket_end|
42
- if bucket_start == :begin
43
- '< $1'
44
- elsif bucket_end == :end
45
- '> $5'
46
- else
47
- "$#{bucket_start} - $#{bucket_end}"
48
- end
49
- end
50
-
51
-
52
-
53
- #pre-cached aggregations
54
- aggregation :month, :year, :product
55
- aggregation :month, :region
56
-
1
+ class DefectCubicle
2
+ extend Cubicle::Aggregation
3
+
4
+ define :preventable, "this.root_cause != 'act_of_god'"
5
+ define :product_name, "this.product.name"
6
+ define :current_year, "'{{date_today_iso}}'.substring(0,4)"
7
+
8
+ date :manufacture_date, :field_name=>'manufacture_date', :alias=>:date
9
+ dimension :month, :expression=>'this.manufacture_date.substring(0,7)'
10
+ dimension :year, :expression=>'this.manufacture_date.substring(0,4)'
11
+
12
+ dimension :manufacture_time
13
+
14
+ dimension :product, :expression=>'{{product_name}}'
15
+ dimension :region, :field_name=>'plant.address.region'
16
+
17
+ dimensions :operator, :outcome
18
+
19
+ count :total_defects, :field_name=>'defect_id'
20
+ count :distinct_products, :expression=>'{{product}}', :distinct=>true
21
+ count :preventable_defects, :expression=>'{{preventable}}'
22
+ count :conditioned_preventable,:expression=>'1.0', :condition=>'{{preventable}}'
23
+ count :defects_this_year, :expression=>'{{year}} == {{current_year}}'
24
+ sum :total_cost, :field_name=>'cost'
25
+ avg :avg_cost, :field_name=>'cost'
26
+
27
+ #calculated fields
28
+ ratio :preventable_pct, :preventable_defects, :total_defects
29
+ ratio :distinct_ratio, :distinct_products, :total_defects
30
+ difference :inevitable_defects, :total_defects, :preventable_defects
31
+ #durations
32
+ average_duration :ms1 => :ms2
33
+ total_duration :ms2 => :ms3
34
+ duration :total_duration, :ms1 => :ms3, :in=>:days
35
+ duration :total_duration_in_weeks, :ms1 => :ms4, :in=>:weeks
36
+ duration :conditional_duration, :ms1 => :ms3, :in=>:days, :condition=>"this.defect_id != 2"
37
+ elapsed :ms3, :in=>:days
38
+ age_since :avg_time_since_ms3,:ms3, :in=>:days
39
+
40
+ #bucketized fields
41
+ categorize :avg_cost_category,
42
+ :avg_cost, 1..5, :bucket_size=>0.5, :range_start_bump=>0.01 do |bucket_start,bucket_end|
43
+ if bucket_start == :begin
44
+ '< $1'
45
+ elsif bucket_end == :end
46
+ '> $5'
47
+ else
48
+ "$#{bucket_start} - $#{bucket_end}"
49
+ end
50
+ end
51
+
52
+
53
+
54
+ #pre-cached aggregations
55
+ aggregation :month, :year, :product
56
+ aggregation :month, :region
57
+
57
58
  end