lib/mongoid/report/scope.rb in mongoid-report-0.1.9 vs lib/mongoid/report/scope.rb in mongoid-report-0.2.0
- old
+ new
@@ -1,78 +1,184 @@
+require 'set'
+
module Mongoid
module Report
- Scope = Struct.new(:context, :report_name) do
+ Scope = Struct.new(:context, :report_module, :report_name) do
def query(conditions = {})
queries.concat([conditions]) unless conditions.empty?
self
end
# We need to add grouping conditions when user needs it.
def yield
return self if @yielded
- queries.concat(context.queries(report_name))
+ queries.concat(context.queries(report_module, report_name))
@yielded = true
self
end
+ def out(collection_name, options = {})
+ output.collection_name = collection_name
+ output.options = options
+ self
+ end
+
+ def in(collection_name)
+ input.collection_name = collection_name
+ self
+ end
+
+ def all_in_batches(aggregation_queries)
+ # Lets assume we have only one field for making splits for the
+ # aggregation queries.
+ rows = []
+
+ threads = batches.map do |r|
+ # For now we are supporting only data fields for splitting up the
+ # queries.
+ range_match = r.map { |time| time.to_date.mongoize }
+
+ Thread.new do
+ q =
+ ['$match' => { batches.field => { '$gte' => range_match.first, '$lte' => range_match.last } }] +
+ aggregation_queries
+
+ # if groups == [batch.field]
+ rows.concat(Array(collection.aggregate(q)))
+ end
+ end
+ threads.map(&:join)
+
+ merger = Mongoid::Report::Merger.new(groups)
+ merger.do(rows)
+ end
+
+ def all_inline(aggregation_queries)
+ Array(collection.aggregate(aggregation_queries))
+ end
+
def all
self.yield unless yielded?
aggregation_queries = compile_queries
- rows = klass.collection.aggregate(aggregation_queries)
+ rows = if batches.present?
+ all_in_batches(aggregation_queries)
+ else
+ all_inline(aggregation_queries)
+ end
+
+ # in case if we want to store rows to collection
+ if output.present?
+ output.do(rows)
+ end
+
Collection.new(context, rows, fields, columns, mapping)
end
+ def in_batches(conditions)
+ batches.conditions = conditions
+ self
+ end
+
private
def compile_queries
- compiled = queries.map do |query|
- next query unless query.has_key?("$match")
+ compiled = Set.new
+ queries.each do |query|
+ next compiled << query if query.has_key?("$project") || query.has_key?('$group')
+
query.deep_dup.tap do |new_query|
new_query.each do |function_name, values|
- values.each do |name, value|
- if value.respond_to?(:call)
- value = value.call(context)
+
+ if values.respond_to?(:call)
+ new_query[function_name] = values.call(context)
+ else
+ values.each do |name, value|
+ if value.respond_to?(:call)
+ value = value.call(context)
+ end
+
+ unless value.present?
+ # In case we don't have value for applying match, lets skip
+ # this type of the queries.
+ new_query.delete(function_name)
+ else
+ new_query[function_name][name] = value
+ end
end
- new_query[function_name][name] = value
- end
- end
+ end # values.is_a?(Proc)
+ end # new_query.each
+
+ compiled << new_query if new_query.present?
end
end
- compiled
+ compiled.to_a
end
def yielded?
@yielded
end
def queries
@queries ||= []
end
- def klass
- context.report_module_settings[report_name][:for]
+ # Different usage for this method:
+ # - attach_to method contains collection name as first argument
+ # - attach_to method contains mongoid model
+ # - aggregate_for method contains attach_to proc option for calculating
+ # collection name.
+ def collection
+ @collection ||= begin
+ # In case if we are using dynamic collection name calculated by
+ # passing attach_to proc to the aggregate method.
+ if input.present?
+ # Using default session to mongodb we can automatically provide
+ # access to collection.
+ input.collection
+ else
+ klass = context.report_module_settings[report_module][:reports][report_name][:collection]
+ Collections.get(klass)
+ end
+ end
end
+ def batches
+ @batches ||= Mongoid::Report::Batches.new(
+ context.batches(report_module, report_name))
+ end
+
+ def output
+ @output ||= Mongoid::Report::Output.new
+ end
+
+ def input
+ @input ||= Mongoid::Report::Input.new
+ end
+
+ def groups
+ @groups ||= context.groups(report_module, report_name)
+ end
+
def fields
# We need to use here only output field names it could be different
# than defined colunms, Example: field1: 'report-field-name'
- context.report_module_settings[report_name][:fields].values
+ context.fields(report_module, report_name)
end
def columns
- context.report_module_settings[report_name][:columns]
+ context.columns(report_module, report_name)
end
def mapping
- context.report_module_settings[report_name][:mapping]
+ context.mapping(report_module, report_name)
end
end
end
end