mongoid-report 0.1.3 → 0.1.5

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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTcxMTUyOWNhZWVmNjVhOWY1MGRkMDhhYTQ2YTI5YTY4YzE5NzhlOA==
4
+ NDE4Y2Y5ODExNDhkNTdmMWNlNzIwMTg1MWU0OTcwYWJlZTg0YTIzYw==
5
5
  data.tar.gz: !binary |-
6
- YWQ1YTZjMTMwZjJkMzU0MjE5ZGZlMDNlNDdhNzdiZmM5MjE0YTM1Yw==
6
+ NzBhN2ZiY2RhMDhjOTcwZDA2ZjBkNjQ4NDEzN2I2MDZhNmVhZjZhOQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YzAxMjg1Yjc4MDQyZjU3MTJiNTJhNzhiMmY4N2U5YTA3NGJkZGQwZDc4Yjll
10
- MzMzZjdjNzZjYjI4N2UyNGJmMWRkZTUzZTA2MzEwZDY0MDI0NDgwZDU0MzEw
11
- ZjEyZmFkM2ViMzNhMDMyYjI2YTVmY2FiMzRiZjJjYzI5YjRkMTk=
9
+ MzY3ZDQ5ZTI2YjJmNmE4OWU2Y2JmZWRkMTkzMzYwNmQ0NzJlNGFjNGI3OTY4
10
+ Mzc3MjBiNGE3ZjRiMTJjZDY3Nzk1NWFlNzBkMmM4YjA4YzM3MWM3NDQzMDhk
11
+ MWRiMTRhMzNkZDg1NDk3ZjUxOTdiMDAxZWZkNmNiMGU0ZGZiOWY=
12
12
  data.tar.gz: !binary |-
13
- NGMzZTJiYWQ4OGZmODRkYjhjNTBmYmQwNTZmNDNhM2M2ZTdkYTVkMGRlMGVl
14
- OTcwYjVmZGNjODkyZDE4YTc2YjJmMDhiMmJiODg3ZDI2ODdhZjUzOTZkM2Vj
15
- ZjY5ZTliYzdlMDY4MDExMmRlMmE3NTVlZDI0ODFiZTdiZGY0NzI=
13
+ M2NkYmJlYTU0YjcyZmRhODE3OTY5Yjg0Y2MzMmRhYzU0NTc5YmVkZTc0M2Zh
14
+ OWE4MWJjNWY5YjZkMjZkMGZmZGVkZTBkZjIwNzVjM2E4M2FhZTE1MjViYzhk
15
+ NTAzYTU5NGJhYjY4MGRkYjA3ZTc5YmQ3NjdiOGFlZWRiZjk5OTc=
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mongoid-report (0.1.3)
4
+ mongoid-report (0.1.5)
5
5
  mongoid (> 3.0.1)
6
6
 
7
7
  GEM
@@ -4,12 +4,18 @@ module Mongoid
4
4
  module Report
5
5
 
6
6
  class Collection < SimpleDelegator
7
- def initialize(rows, fields, columns)
7
+ def initialize(context, rows, fields, columns)
8
+ @context = context
8
9
  @rows = rows
9
10
  @fields = fields
10
11
  @columns = columns
12
+
13
+ # Apply dyncamic columns in context of row and apply indifferent access
14
+ # for the rows.
15
+ rows = compile_dynamic_fields(rows, columns)
16
+
17
+ # Collection should behave like Array using delegator method.
11
18
  super(rows)
12
- compile_dynamic_fields(columns)
13
19
  end
14
20
 
15
21
  def summary
@@ -24,11 +30,13 @@ module Mongoid
24
30
 
25
31
  private
26
32
 
27
- def compile_dynamic_fields(columns)
28
- self.each do |row|
33
+ def compile_dynamic_fields(rows, columns)
34
+ rows.map do |row|
29
35
  @columns.each do |name, function|
30
- row[name] = function.call(row)
36
+ row[name] = function.call(@context, row)
31
37
  end
38
+
39
+ row.with_indifferent_access
32
40
  end
33
41
  end
34
42
  end
@@ -0,0 +1,11 @@
1
+ module Mongoid
2
+ module Report
3
+
4
+ class Config
5
+ class_attribute :use_threads_on_aggregate
6
+
7
+ self.use_threads_on_aggregate = false
8
+ end
9
+
10
+ end
11
+ end
@@ -13,13 +13,7 @@ module Mongoid
13
13
  private
14
14
 
15
15
  def groups
16
- @group_by ||= begin
17
- if settings[:group_by].size == 0
18
- [:_id]
19
- else
20
- settings[:group_by]
21
- end
22
- end
16
+ @group_by ||= settings.fetch(:group_by, [])
23
17
  end
24
18
 
25
19
  def fields
@@ -19,23 +19,32 @@ module Mongoid
19
19
 
20
20
  def all
21
21
  self.yield unless yielded?
22
- queries = compile_queries
23
- Collection.new(klass.collection.aggregate(queries), fields, columns)
22
+
23
+ aggregation_queries = compile_queries
24
+ rows = klass.collection.aggregate(aggregation_queries)
25
+
26
+ Collection.new(context, rows, fields, columns)
24
27
  end
25
28
 
26
29
  private
27
30
 
28
31
  def compile_queries
29
- queries.dup.map do |query|
30
- query.each do |function_name, values|
31
- values.each do |name, value|
32
- value = value.call(context) if value.respond_to?(:call)
33
- query[function_name][name] = value
32
+ compiled = queries.map do |query|
33
+ next query unless query.has_key?("$match")
34
+
35
+ query.deep_dup.tap do |new_query|
36
+ new_query.each do |function_name, values|
37
+ values.each do |name, value|
38
+ if value.respond_to?(:call)
39
+ value = value.call(context)
40
+ end
41
+ new_query[function_name][name] = value
42
+ end
34
43
  end
35
44
  end
36
-
37
- query
38
45
  end
46
+
47
+ compiled
39
48
  end
40
49
 
41
50
  def yielded?
@@ -47,17 +56,17 @@ module Mongoid
47
56
  end
48
57
 
49
58
  def klass
50
- context.class.settings_property(report_name, :for)
59
+ context.report_module_settings[report_name][:for]
51
60
  end
52
61
 
53
62
  def fields
54
63
  # We need to use here only output field names it could be different
55
64
  # than defined colunms, Example: field1: 'report-field-name'
56
- context.class.settings_property(report_name, :fields).values
65
+ context.report_module_settings[report_name][:fields].values
57
66
  end
58
67
 
59
68
  def columns
60
- context.class.settings_property(report_name, :columns)
69
+ context.report_module_settings[report_name][:columns]
61
70
  end
62
71
  end
63
72
 
@@ -1,7 +1,14 @@
1
+ require 'thread'
2
+
1
3
  module Mongoid
2
4
  module Report
3
5
 
4
6
  ScopeCollection = Struct.new(:context) do
7
+ def initialize(context)
8
+ @mutex = Mutex.new
9
+ super
10
+ end
11
+
5
12
  def scopes
6
13
  @scopes ||= modules.map do |key|
7
14
  Scope.new(context, key)
@@ -23,9 +30,22 @@ module Mongoid
23
30
  end
24
31
 
25
32
  def all
26
- scopes.inject({}) do |hash, scope|
27
- hash[scope.report_name] = scope.all
28
- hash
33
+ {}.tap do |hash|
34
+ if Mongoid::Report::Config.use_threads_on_aggregate
35
+ scopes.map do |scope|
36
+ Thread.new do
37
+ rows = scope.all
38
+
39
+ @mutex.synchronize do
40
+ hash[scope.report_name] = rows
41
+ end
42
+ end
43
+ end.map(&:join)
44
+ else
45
+ scopes.each do |scope|
46
+ hash[scope.report_name] = scope.all
47
+ end
48
+ end
29
49
  end
30
50
  end
31
51
 
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module Report
3
- VERSION = "0.1.3"
3
+ VERSION = "0.1.5"
4
4
  end
5
5
  end
@@ -1,6 +1,7 @@
1
1
  require 'active_support/concern'
2
2
  require 'active_support/core_ext/class/attribute'
3
3
 
4
+ require_relative 'report/config'
4
5
  require_relative 'report/queries_builder'
5
6
  require_relative 'report/attach_proxy'
6
7
  require_relative 'report/collection'
@@ -19,8 +20,20 @@ module Mongoid
19
20
 
20
21
  self.settings = {}
21
22
 
23
+ def self.inherited(subclass)
24
+ subclass.settings = self.settings.dup
25
+ end
26
+
27
+ # Variable for copying internal class settings to the instance because of
28
+ # possible modifications in case of using filters with lambda
29
+ # expressions.
30
+ attr_reader :report_module_settings
31
+
22
32
  def initialize_report_module
23
- self.class.settings.each do |klass, configuration|
33
+ # Lets store settings under created instance.
34
+ @report_module_settings = self.class.settings.dup
35
+
36
+ @report_module_settings.each do |klass, configuration|
24
37
  builder = QueriesBuilder.new(configuration)
25
38
 
26
39
  # Prepare group queries depends on the configuration in the included
@@ -35,7 +48,7 @@ module Mongoid
35
48
  alias :initialize :initialize_report_module
36
49
 
37
50
  def queries(klass)
38
- self.class.settings[klass][:queries]
51
+ report_module_settings[klass][:queries]
39
52
  end
40
53
 
41
54
  # We should pass here mongoid document
@@ -140,6 +153,7 @@ module Mongoid
140
153
  fields: ActiveSupport::OrderedHash.new,
141
154
  group_by: [],
142
155
  queries: [],
156
+ compiled: false,
143
157
  columns: ActiveSupport::OrderedHash.new,
144
158
  }
145
159
  end
@@ -7,19 +7,25 @@ describe Mongoid::Report do
7
7
  let(:two_days_ago) { Date.parse("18-12-2004") }
8
8
 
9
9
  describe '.aggregate_for' do
10
- it 'aggregates fields by default group _id as well' do
11
- instance1 = klass.create!(day: today , field1: 1)
12
- instance2 = klass.create!(day: today , field1: 1)
13
- instance3 = klass.create!(day: yesterday , field1: 1)
10
+ it 'aggregates fields by app' do
11
+ Report = Class.new do
12
+ include Mongoid::Report
14
13
 
15
- example = Report2.new
14
+ attach_to Model do
15
+ aggregation_field :field1
16
+ end
17
+ end
18
+
19
+ klass.create!(field1: 1)
20
+ klass.create!(field1: 1)
21
+ klass.create!(field1: 1)
22
+
23
+ example = Report.new
16
24
  rows = example.aggregate_for(klass)
17
25
  rows = rows.all
18
26
 
19
- expect(rows.size).to eq(3)
20
- expect(rows[0]['field1']).to eq(1)
21
- expect(rows[1]['field1']).to eq(1)
22
- expect(rows[2]['field1']).to eq(1)
27
+ expect(rows.size).to eq(1)
28
+ expect(rows[0]['field1']).to eq(3)
23
29
  end
24
30
 
25
31
  it 'aggregates field by defined field of the mode' do
@@ -13,7 +13,7 @@ describe Mongoid::Report do
13
13
  attach_to Model do
14
14
  group_by :day
15
15
  aggregation_field :field1
16
- column 'dynamic-field1' => ->(row) { row['field1'] * 10 }
16
+ column 'dynamic-field1' => ->(context, row) { row['field1'] * 10 }
17
17
  end
18
18
  end
19
19
  end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Report::Config do
4
+ it 'allows to set aggregation threads' do
5
+ expect(Mongoid::Report::Config.use_threads_on_aggregate).to eq(false)
6
+ Mongoid::Report::Config.use_threads_on_aggregate = true
7
+ expect(Mongoid::Report::Config.use_threads_on_aggregate).to eq(true)
8
+ end
9
+ end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Mongoid::Report::QueriesBuilder do
4
4
 
5
5
  describe '.queries' do
6
- it 'builds queries for aggregation using default group _id field' do
6
+ it 'builds queries for aggregation' do
7
7
  queries = Report1.new.queries(Model)
8
8
  expect(queries.size).to eq(3)
9
9
  expect(queries[0]).to eq(
@@ -13,12 +13,12 @@ describe Mongoid::Report::QueriesBuilder do
13
13
  })
14
14
  expect(queries[1]).to eq(
15
15
  '$group' => {
16
- :_id => { :_id => '$_id' },
16
+ :_id => { },
17
17
  :field1 => { '$sum' => '$field1' },
18
18
  })
19
19
  expect(queries[2]).to eq(
20
20
  '$project' => {
21
- :_id => '$_id',
21
+ :_id => 0,
22
22
  :field1 => '$field1',
23
23
  })
24
24
  end
@@ -20,6 +20,30 @@ describe Mongoid::Report do
20
20
  expect(rows.summary[:field1]).to eq(3)
21
21
  expect(rows.summary['field1']).to eq(3)
22
22
  end
23
+
24
+ it 'should support dynamic columns as well' do
25
+ Report = Class.new do
26
+ include Mongoid::Report
27
+
28
+ report 'example' do
29
+ attach_to Model do
30
+ aggregation_field :field1
31
+ column 'new-field1' => ->(context, row) { row['field1'] * 10 }
32
+ end
33
+ end
34
+ end
35
+
36
+ klass.create!(field1: 1)
37
+ klass.create!(field1: 1)
38
+ klass.create!(field1: 1)
39
+
40
+ example = Report.new
41
+ rows = example.aggregate_for('example-models')
42
+ rows = rows.all
43
+
44
+ expect(rows[0]['field1']).to eq(3)
45
+ expect(rows[0]['new-field1']).to eq(30)
46
+ end
23
47
  end
24
48
 
25
49
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'benchmark'
3
+
4
+ describe Mongoid::Report do
5
+ let(:klass) { Model }
6
+
7
+ it 'aggregates fields by app in threads' do
8
+ Report = Class.new do
9
+ include Mongoid::Report
10
+
11
+ attach_to Model, as: 'field1-aggregation' do
12
+ aggregation_field :field1
13
+ end
14
+
15
+ attach_to Model, as: 'field2-aggregation' do
16
+ aggregation_field :field2
17
+ end
18
+ end
19
+
20
+ 30000.times { klass.create!(field1: 1, field2: 1) }
21
+
22
+ report = Report.new
23
+ scoped = report.aggregate
24
+
25
+ Mongoid::Report::Config.use_threads_on_aggregate = true
26
+ time1 = Benchmark.measure do
27
+ rows = scoped.all
28
+ end
29
+
30
+ Mongoid::Report::Config.use_threads_on_aggregate = false
31
+ time2 = Benchmark.measure do
32
+ rows = scoped.all
33
+ end
34
+
35
+ puts time2
36
+ puts time1
37
+
38
+ time2.real.should > time1.real
39
+ end
40
+ end
@@ -88,4 +88,46 @@ describe Mongoid::Report do
88
88
  end
89
89
  end
90
90
 
91
+
92
+ describe 'two report classes' do
93
+ it 'should have different settings' do
94
+ ReportKlass1 = Class.new do
95
+ include Mongoid::Report
96
+
97
+ attach_to Model do
98
+ aggregation_field :field1
99
+ end
100
+ end
101
+
102
+ ReportKlass2 = Class.new do
103
+ include Mongoid::Report
104
+
105
+ attach_to Model do
106
+ aggregation_field :field2
107
+ end
108
+ end
109
+
110
+ expect(ReportKlass1.settings).not_to eq(ReportKlass2.settings)
111
+ end
112
+
113
+ class ReportKlass
114
+ include Mongoid::Report
115
+ end
116
+
117
+ class ReportKlass1 < ReportKlass
118
+ attach_to Model do
119
+ aggregation_field :field1
120
+ end
121
+ end
122
+
123
+ class ReportKlass2 < ReportKlass
124
+ attach_to Model do
125
+ aggregation_field :field2
126
+ end
127
+ end
128
+
129
+ it 'should have different settings for inherited classes' do
130
+ expect(ReportKlass1.fields(Model)).not_to eq(ReportKlass2.fields(Model))
131
+ end
132
+ end
91
133
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-report
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandr Korsak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-24 00:00:00.000000000 Z
11
+ date: 2014-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongoid
@@ -74,6 +74,7 @@ files:
74
74
  - lib/mongoid/report.rb
75
75
  - lib/mongoid/report/attach_proxy.rb
76
76
  - lib/mongoid/report/collection.rb
77
+ - lib/mongoid/report/config.rb
77
78
  - lib/mongoid/report/queries_builder.rb
78
79
  - lib/mongoid/report/report_proxy.rb
79
80
  - lib/mongoid/report/scope.rb
@@ -82,8 +83,10 @@ files:
82
83
  - mongoid-report.gemspec
83
84
  - spec/mongoid/report/aggregation_spec.rb
84
85
  - spec/mongoid/report/column_spec.rb
86
+ - spec/mongoid/report/config_spec.rb
85
87
  - spec/mongoid/report/queries_builder_spec.rb
86
88
  - spec/mongoid/report/summary_spec.rb
89
+ - spec/mongoid/report/threads_spec.rb
87
90
  - spec/mongoid/report_spec.rb
88
91
  - spec/spec_helper.rb
89
92
  - spec/support/models.rb
@@ -114,8 +117,10 @@ summary: Easily build mongoid reports using aggregation framework
114
117
  test_files:
115
118
  - spec/mongoid/report/aggregation_spec.rb
116
119
  - spec/mongoid/report/column_spec.rb
120
+ - spec/mongoid/report/config_spec.rb
117
121
  - spec/mongoid/report/queries_builder_spec.rb
118
122
  - spec/mongoid/report/summary_spec.rb
123
+ - spec/mongoid/report/threads_spec.rb
119
124
  - spec/mongoid/report_spec.rb
120
125
  - spec/spec_helper.rb
121
126
  - spec/support/models.rb