1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
# encoding: utf-8
module Origin
# Provides a DSL around crafting aggregation framework commands.
#
# @since 2.0.0
module Aggregable
extend Macroable
# @attribute [r] pipeline The aggregation pipeline.
attr_reader :pipeline
# @attribute [rw] aggregating Flag for whether or not we are aggregating.
attr_writer :aggregating
# Has the aggregable enter an aggregation state. Ie, are only aggregation
# operations allowed at this point on.
#
# @example Is the aggregable aggregating?
# aggregable.aggregating?
#
# @return [ true, false ] If the aggregable is aggregating.
#
# @since 2.0.0
def aggregating?
!!@aggregating
end
# Add a group ($group) operation to the aggregation pipeline.
#
# @example Add a group operation being verbose.
# aggregable.group(count: { "$sum" => 1 }, max: { "$max" => "likes" })
#
# @example Add a group operation using symbol shortcuts.
# aggregable.group(:count.sum => 1, :max.max => "likes")
#
# @param [ Hash ] operation The group operation.
#
# @return [ Aggregable ] The aggregable.
#
# @since 2.0.0
def group(operation)
aggregation(operation) do |pipeline|
pipeline.group(operation)
end
end
key :avg, :override, "$avg"
key :max, :override, "$max"
key :min, :override, "$min"
key :sum, :override, "$sum"
key :last, :override, "$last"
key :push, :override, "$push"
key :first, :override, "$first"
key :add_to_set, :override, "$addToSet"
# Add a projection ($project) to the aggregation pipeline.
#
# @example Add a projection to the pipeline.
# aggregable.project(author: 1, name: 0)
#
# @param [ Hash ] criterion The projection to make.
#
# @return [ Aggregable ] The aggregable.
#
# @since 2.0.0
def project(operation = nil)
aggregation(operation) do |pipeline|
pipeline.project(operation)
end
end
# Add an unwind ($unwind) to the aggregation pipeline.
#
# @example Add an unwind to the pipeline.
# aggregable.unwind(:field)
#
# @param [ String, Symbol ] field The name of the field to unwind.
#
# @return [ Aggregable ] The aggregable.
#
# @since 2.0.0
def unwind(field)
aggregation(field) do |pipeline|
pipeline.unwind(field)
end
end
private
# Add the aggregation operation.
#
# @api private
#
# @example Aggregate on the operation.
# aggregation(operation) do |pipeline|
# pipeline.push("$project" => operation)
# end
#
# @param [ Hash ] operation The operation for the pipeline.
#
# @return [ Aggregable ] The cloned aggregable.
#
# @since 2.0.0
def aggregation(operation)
return self unless operation
clone.tap do |query|
unless aggregating?
query.pipeline.concat(query.selector.to_pipeline)
query.pipeline.concat(query.options.to_pipeline)
query.aggregating = true
end
yield(query.pipeline)
end
end
end
end
|