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