cubicle 0.1.29 → 0.1.30

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