mongoid-report 0.1.9 → 0.2.0
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 +8 -8
- data/Gemfile.lock +30 -18
- data/README.md +6 -36
- data/lib/mongoid/report/attach_proxy.rb +26 -5
- data/lib/mongoid/report/batches.rb +41 -0
- data/lib/mongoid/report/collection.rb +4 -3
- data/lib/mongoid/report/collections.rb +16 -0
- data/lib/mongoid/report/input.rb +17 -0
- data/lib/mongoid/report/merger.rb +29 -0
- data/lib/mongoid/report/output.rb +32 -0
- data/lib/mongoid/report/queries_builder.rb +14 -16
- data/lib/mongoid/report/report_proxy.rb +16 -4
- data/lib/mongoid/report/scope.rb +123 -17
- data/lib/mongoid/report/scope_collection.rb +16 -10
- data/lib/mongoid/report/version.rb +1 -1
- data/lib/mongoid/report.rb +180 -61
- data/spec/mongoid/report/aggregation_spec.rb +72 -57
- data/spec/mongoid/report/attach_to_spec.rb +16 -0
- data/spec/mongoid/report/collection_spec.rb +12 -7
- data/spec/mongoid/report/column_spec.rb +1 -1
- data/spec/mongoid/report/dynamic_attach_to_spec.rb +86 -0
- data/spec/mongoid/report/integration_spec.rb +129 -0
- data/spec/mongoid/report/module_configuration_spec.rb +35 -0
- data/spec/mongoid/report/out_spec.rb +122 -0
- data/spec/mongoid/report/queries_builder_spec.rb +54 -16
- data/spec/mongoid/report/set_spec.rb +3 -3
- data/spec/mongoid/report/summary_spec.rb +87 -16
- data/spec/mongoid/report/threads_spec.rb +132 -4
- data/spec/mongoid/report_spec.rb +58 -34
- data/spec/spec_helper.rb +2 -0
- metadata +17 -2
@@ -10,8 +10,12 @@ module Mongoid
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def scopes
|
13
|
-
@scopes ||=
|
14
|
-
|
13
|
+
@scopes ||= [].tap do |collection|
|
14
|
+
context.settings.each do |report_module, module_settings|
|
15
|
+
module_settings[:reports].each do |report_name, _report_settings|
|
16
|
+
collection << Scope.new(context, report_module, report_name)
|
17
|
+
end
|
18
|
+
end
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
@@ -29,31 +33,33 @@ module Mongoid
|
|
29
33
|
self
|
30
34
|
end
|
31
35
|
|
36
|
+
def in_batches(conditions)
|
37
|
+
scopes.each do |scope|
|
38
|
+
scope.in_batches(conditions)
|
39
|
+
end
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
32
43
|
def all
|
33
|
-
{}.tap do |hash|
|
44
|
+
Hash.new { |h, k| h[k] = {} }.tap do |hash|
|
34
45
|
if Mongoid::Report::Config.use_threads_on_aggregate
|
35
46
|
scopes.map do |scope|
|
36
47
|
Thread.new do
|
37
48
|
rows = scope.all
|
38
49
|
|
39
50
|
@mutex.synchronize do
|
40
|
-
hash[scope.report_name] = rows
|
51
|
+
hash[scope.report_module][scope.report_name] = rows
|
41
52
|
end
|
42
53
|
end
|
43
54
|
end.map(&:join)
|
44
55
|
else
|
45
56
|
scopes.each do |scope|
|
46
|
-
hash[scope.report_name] = scope.all
|
57
|
+
hash[scope.report_module][scope.report_name] = scope.all
|
47
58
|
end
|
48
59
|
end
|
49
60
|
end
|
50
61
|
end
|
51
62
|
|
52
|
-
private
|
53
|
-
|
54
|
-
def modules
|
55
|
-
context.settings.keys
|
56
|
-
end
|
57
63
|
end
|
58
64
|
|
59
65
|
end
|
data/lib/mongoid/report.rb
CHANGED
@@ -5,6 +5,11 @@ require_relative 'report/config'
|
|
5
5
|
require_relative 'report/queries_builder'
|
6
6
|
require_relative 'report/attach_proxy'
|
7
7
|
require_relative 'report/collection'
|
8
|
+
require_relative 'report/batches'
|
9
|
+
require_relative 'report/merger'
|
10
|
+
require_relative 'report/collections'
|
11
|
+
require_relative 'report/output'
|
12
|
+
require_relative 'report/input'
|
8
13
|
require_relative 'report/scope'
|
9
14
|
require_relative 'report/scope_collection'
|
10
15
|
require_relative 'report/report_proxy'
|
@@ -18,46 +23,116 @@ module Mongoid
|
|
18
23
|
|
19
24
|
class_attribute :settings
|
20
25
|
|
26
|
+
# TODO: rewrite this module adding clone method for the all settings
|
27
|
+
# defined for the mongoid-report. for now it's creating the duplicates.
|
28
|
+
# check out the mongoid library for the best example.
|
21
29
|
self.settings = {}
|
22
30
|
|
23
31
|
def self.inherited(subclass)
|
24
|
-
subclass.settings =
|
32
|
+
subclass.settings = {}
|
25
33
|
end
|
26
34
|
|
27
35
|
# Variable for copying internal class settings to the instance because of
|
28
|
-
# possible modifications in case of using
|
36
|
+
# possible modifications in case of using maches with lambda
|
29
37
|
# expressions.
|
30
38
|
attr_reader :report_module_settings
|
31
39
|
|
32
40
|
def initialize_report_module
|
33
41
|
# Lets store settings under created instance.
|
34
|
-
@report_module_settings = self.
|
42
|
+
@report_module_settings = self.settings.inject({}) do |hash_module, (report_module, module_settings)|
|
43
|
+
hash_module.merge!(
|
44
|
+
report_module =>
|
45
|
+
{
|
46
|
+
fields: module_settings[:fields],
|
47
|
+
group_by: module_settings[:group_by],
|
48
|
+
batches: module_settings[:batches],
|
49
|
+
columns: module_settings[:columns],
|
50
|
+
mapping: module_settings[:mapping],
|
51
|
+
queries: (module_settings[:queries] || []).dup,
|
52
|
+
reports: (module_settings[:reports] || {}).inject({}) do |hash_report, (report_name, report_settings)|
|
53
|
+
hash_report.merge!(
|
54
|
+
report_name => {
|
55
|
+
collection: report_settings[:collection],
|
56
|
+
fields: report_settings[:fields],
|
57
|
+
group_by: report_settings[:group_by],
|
58
|
+
batches: report_settings[:batches],
|
59
|
+
columns: report_settings[:columns],
|
60
|
+
mapping: report_settings[:mapping],
|
61
|
+
queries: (report_settings[:queries] || []).dup,
|
62
|
+
})
|
63
|
+
end
|
64
|
+
})
|
65
|
+
end
|
66
|
+
|
67
|
+
@report_module_settings.each do |report_module, module_configuration|
|
68
|
+
# Lets do not run queries builder in case of missing queries or group
|
69
|
+
# by parameters
|
70
|
+
unless module_configuration[:queries].empty? && module_configuration[:group_by].empty?
|
71
|
+
builder = QueriesBuilder.new(module_configuration)
|
72
|
+
|
73
|
+
# Prepare group queries depends on the configuration in the included
|
74
|
+
# class.
|
75
|
+
queries = builder.do
|
35
76
|
|
36
|
-
|
37
|
-
|
77
|
+
# Now we have access to compiled queries to run it in aggregation
|
78
|
+
# framework.
|
79
|
+
module_configuration[:queries] = module_configuration[:queries] + queries
|
80
|
+
end
|
81
|
+
|
82
|
+
# For now we are filtering by $match queries only.
|
83
|
+
matches = module_configuration[:queries].select do |query|
|
84
|
+
query['$match'].present?
|
85
|
+
end
|
38
86
|
|
39
|
-
|
40
|
-
|
41
|
-
|
87
|
+
module_configuration[:reports].each do |report_name, report_configuration|
|
88
|
+
# Lets merge report and module settings together.
|
89
|
+
report_configuration[:fields] = report_configuration[:fields] | module_configuration[:fields]
|
90
|
+
report_configuration[:group_by] = report_configuration[:group_by] | module_configuration[:group_by]
|
91
|
+
report_configuration[:columns] = report_configuration[:columns].merge(module_configuration[:columns])
|
92
|
+
report_configuration[:mapping] = report_configuration[:mapping].merge(module_configuration[:mapping])
|
42
93
|
|
43
|
-
|
44
|
-
|
45
|
-
|
94
|
+
builder = QueriesBuilder.new(report_configuration)
|
95
|
+
|
96
|
+
# Prepare group queries depends on the configuration in the included
|
97
|
+
# class.
|
98
|
+
queries = builder.do
|
99
|
+
|
100
|
+
# Now we have access to compiled queries to run it in aggregation
|
101
|
+
# framework.
|
102
|
+
report_configuration[:queries] = report_configuration[:queries] + matches + queries
|
103
|
+
end
|
46
104
|
end
|
47
105
|
end
|
48
106
|
alias :initialize :initialize_report_module
|
49
107
|
|
50
|
-
def queries(
|
51
|
-
report_module_settings[
|
108
|
+
def queries(report_module, report_name)
|
109
|
+
report_module_settings[report_module][:reports][report_name][:queries]
|
110
|
+
end
|
111
|
+
|
112
|
+
def mapping(report_module, report_name)
|
113
|
+
report_module_settings[report_module][:reports][report_name][:mapping]
|
52
114
|
end
|
53
115
|
|
54
|
-
def
|
55
|
-
report_module_settings[
|
116
|
+
def batches(report_module, report_name)
|
117
|
+
report_module_settings[report_module][:reports][report_name][:batches]
|
56
118
|
end
|
57
119
|
|
58
|
-
|
59
|
-
|
60
|
-
|
120
|
+
def groups(report_module, report_name)
|
121
|
+
report_module_settings[report_module][:reports][report_name][:group_by]
|
122
|
+
end
|
123
|
+
|
124
|
+
def fields(report_module, report_name)
|
125
|
+
report_module_settings[report_module][:reports][report_name][:fields]
|
126
|
+
end
|
127
|
+
|
128
|
+
def columns(report_module, report_name)
|
129
|
+
report_module_settings[report_module][:reports][report_name][:columns]
|
130
|
+
end
|
131
|
+
|
132
|
+
# Method for preparing of aggregation scope where you can apply query,
|
133
|
+
# yield and other grouping methods.
|
134
|
+
def aggregate_for(report_module, report_name)
|
135
|
+
Scope.new(self, report_module, report_name)
|
61
136
|
end
|
62
137
|
|
63
138
|
def aggregate
|
@@ -71,14 +146,27 @@ module Mongoid
|
|
71
146
|
proxy.instance_eval(&block)
|
72
147
|
end
|
73
148
|
|
74
|
-
def attach_to(
|
75
|
-
|
76
|
-
|
149
|
+
def attach_to(*fields, &block)
|
150
|
+
options = fields.extract_options!
|
151
|
+
collection = fields[0]
|
152
|
+
|
153
|
+
options.merge!(report_name: options[:as]) if options[:as]
|
154
|
+
|
155
|
+
define_report_method(options.merge(collection: collection)) do
|
156
|
+
proxy = AttachProxy.new(self, collection, options)
|
157
|
+
proxy.instance_eval(&block)
|
158
|
+
end
|
77
159
|
end
|
78
160
|
|
79
|
-
def
|
80
|
-
define_report_method(*fields) do |_, report_name,
|
81
|
-
|
161
|
+
def batches(*fields)
|
162
|
+
define_report_method(*fields) do |_, report_module, report_name, batches|
|
163
|
+
self.set_settings(report_module, report_name, :batches, batches.stringify_keys!)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def match(*fields)
|
168
|
+
define_report_method(*fields) do |_, report_module, report_name, options|
|
169
|
+
queries = self.get_settings(report_module, report_name, :queries)
|
82
170
|
|
83
171
|
options.each do |key, value|
|
84
172
|
queries
|
@@ -89,51 +177,62 @@ module Mongoid
|
|
89
177
|
end
|
90
178
|
end
|
91
179
|
|
180
|
+
def query(*fields)
|
181
|
+
define_report_method(*fields) do |_, report_module, report_name, options|
|
182
|
+
queries = self.get_settings(report_module, report_name, :queries)
|
183
|
+
|
184
|
+
options.each do |key, value|
|
185
|
+
queries.concat([{ key => value }])
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
92
190
|
def group_by(*fields)
|
93
|
-
define_report_method(*fields) do |groups, report_name, _|
|
94
|
-
|
191
|
+
define_report_method(*fields) do |groups, report_module, report_name, _|
|
192
|
+
self.set_settings(report_module, report_name, :group_by, groups.map(&:to_s))
|
95
193
|
end
|
96
194
|
end
|
97
195
|
|
98
196
|
def column(*fields)
|
99
|
-
define_report_method(*fields) do |columns, report_name,
|
100
|
-
columns.each do |
|
101
|
-
|
102
|
-
add_field(report_name, column, name)
|
197
|
+
define_report_method(*fields) do |columns, report_module, report_name, _|
|
198
|
+
columns.each do |field|
|
199
|
+
self.get_settings(report_module, report_name, :fields) << field.to_s
|
103
200
|
end
|
104
201
|
end
|
105
202
|
end
|
106
203
|
|
107
204
|
def columns(*fields)
|
108
|
-
define_report_method(*fields) do |_, report_name, columns|
|
109
|
-
self.
|
205
|
+
define_report_method(*fields) do |_, report_module, report_name, columns|
|
206
|
+
self.set_settings(report_module, report_name, :columns, columns.stringify_keys!)
|
110
207
|
end
|
111
208
|
end
|
112
209
|
|
113
210
|
def mapping(*fields)
|
114
|
-
define_report_method(*fields) do |_, report_name, mapping|
|
211
|
+
define_report_method(*fields) do |_, report_module, report_name, mapping|
|
115
212
|
mapping.stringify_keys!
|
116
213
|
|
117
214
|
mapping.each do |key, value|
|
118
215
|
mapping[key] = value.to_s
|
119
216
|
end
|
120
217
|
|
121
|
-
self.
|
218
|
+
self.set_settings(report_module, report_name, :mapping, mapping)
|
122
219
|
end
|
123
220
|
end
|
124
221
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
222
|
+
def get_settings(report_module, report_name, field)
|
223
|
+
unless report_name
|
224
|
+
self.settings[report_module][field]
|
225
|
+
else
|
226
|
+
self.settings[report_module][:reports][report_name][field]
|
227
|
+
end
|
131
228
|
end
|
132
229
|
|
133
|
-
def
|
134
|
-
|
135
|
-
.
|
136
|
-
|
230
|
+
def set_settings(report_module, report_name, field, value)
|
231
|
+
unless report_name
|
232
|
+
self.settings[report_module][field] = value
|
233
|
+
else
|
234
|
+
self.settings[report_module][:reports][report_name][field] = value
|
235
|
+
end
|
137
236
|
end
|
138
237
|
|
139
238
|
private
|
@@ -142,38 +241,58 @@ module Mongoid
|
|
142
241
|
options = fields.extract_options!
|
143
242
|
|
144
243
|
# We should always specify model to attach fields, groups
|
145
|
-
collection = options.fetch(:
|
146
|
-
options.delete(:
|
244
|
+
collection = options.fetch(:collection)
|
245
|
+
options.delete(:collection)
|
246
|
+
|
247
|
+
# In case if user passed mongoid model we should get name of collection
|
248
|
+
# instead of using mongoid models. on deep_dup operations it will work
|
249
|
+
# find with strings.
|
250
|
+
collection = Collections.name(collection)
|
147
251
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
options.delete(:
|
252
|
+
report_module = options.delete(:report_module)
|
253
|
+
report_module ||= self.name
|
254
|
+
|
255
|
+
report_name = options.delete(:report_name)
|
256
|
+
report_name ||= Collections.name(collection)
|
152
257
|
|
153
258
|
# We should always have for option
|
154
|
-
initialize_settings_by(
|
259
|
+
initialize_settings_by(report_module, report_name, collection)
|
155
260
|
|
156
261
|
# Because of modifying fields(usign exract options method of
|
157
262
|
# ActiveSupport) lets pass fields to the next block with collection.
|
158
|
-
yield fields,
|
263
|
+
yield fields, report_module, report_name, options || {}
|
159
264
|
end
|
160
265
|
|
161
|
-
def initialize_settings_by(
|
162
|
-
|
266
|
+
def initialize_settings_by(report_module, report_name, collection)
|
267
|
+
# Global settings for the report block
|
268
|
+
settings[report_module] ||= settings.fetch(report_module) do
|
163
269
|
{
|
164
|
-
|
165
|
-
fields:
|
270
|
+
reports: {},
|
271
|
+
fields: [],
|
166
272
|
group_by: [],
|
167
|
-
|
273
|
+
batches: {},
|
168
274
|
columns: {},
|
169
275
|
mapping: {},
|
170
|
-
|
276
|
+
# needs to be cloned
|
277
|
+
queries: [],
|
171
278
|
}
|
172
279
|
end
|
173
|
-
end
|
174
280
|
|
175
|
-
|
176
|
-
|
281
|
+
return unless report_name
|
282
|
+
|
283
|
+
settings[report_module][:reports][report_name] ||=
|
284
|
+
settings[report_module][:reports].fetch(report_name) do
|
285
|
+
{
|
286
|
+
collection: collection,
|
287
|
+
fields: [],
|
288
|
+
group_by: [],
|
289
|
+
batches: {},
|
290
|
+
columns: {},
|
291
|
+
mapping: {},
|
292
|
+
# needs to be cloned
|
293
|
+
queries: [],
|
294
|
+
}
|
295
|
+
end
|
177
296
|
end
|
178
297
|
end
|
179
298
|
|