group.js 3.43 KB
Newer Older
jutatip's avatar
jutatip committed
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
import {
  assert,
  clone,
  each,
  err,
  getType,
  getHash,
  has,
  inArray,
  intersection,
  isArray,
  isBoolean,
  isDate,
  isEmpty,
  isEqual,
  isFunction,
  isNil,
  isNull,
  isNumber,
  isObject,
  isObjectLike,
  isRegExp,
  isString,
  isUndefined,
  map,
  notInArray,
  reduce,
  unique
} from '../util'
import { computeValue, stddev } from '../internal.js'

/**
 * Group stage Accumulator Operators. https://docs.mongodb.com/manual/reference/operator/aggregation-group/
 */

export const groupOperators = {

  /**
   * Returns an array of all the unique values for the selected field among for each document in that group.
   *
   * @param collection
   * @param expr
   * @returns {*}
   */
  $addToSet (collection, expr) {
    return unique(this.$push(collection, expr))
  },

  /**
   * Returns the sum of all the values in a group.
   *
   * @param collection
   * @param expr
   * @returns {*}
   */
  $sum (collection, expr) {
    if (!isArray(collection)) return 0

    if (isNumber(expr)) {
      // take a short cut if expr is number literal
      return collection.length * expr
    }
    return reduce(this.$push(collection, expr).filter(isNumber), (acc, n) => acc + n, 0)
  },

  /**
   * Returns the highest value in a group.
   *
   * @param collection
   * @param expr
   * @returns {*}
   */
  $max (collection, expr) {
    let mapped = this.$push(collection, expr)
    return reduce(mapped, (acc, n) => (isNil(acc) || n > acc) ? n : acc, undefined)
  },

  /**
   * Returns the lowest value in a group.
   *
   * @param collection
   * @param expr
   * @returns {*}
   */
  $min (collection, expr) {
    let mapped = this.$push(collection, expr)
    return reduce(mapped, (acc, n) => (isNil(acc) || n < acc) ? n : acc, undefined)
  },

  /**
   * Returns an average of all the values in a group.
   *
   * @param collection
   * @param expr
   * @returns {number}
   */
  $avg (collection, expr) {
    let data = this.$push(collection, expr).filter(isNumber)
    let sum = reduce(data, (acc, n) => acc + n, 0)
    return sum / (data.length || 1)
  },

  /**
   * Returns an array of all values for the selected field among for each document in that group.
   *
   * @param collection
   * @param expr
   * @returns {Array|*}
   */
  $push (collection, expr) {
    if (isNil(expr)) return collection
    return map(collection, (obj) => computeValue(obj, expr))
  },

  /**
   * Returns the first value in a group.
   *
   * @param collection
   * @param expr
   * @returns {*}
   */
  $first (collection, expr) {
    return (collection.length > 0) ? computeValue(collection[0], expr) : undefined
  },

  /**
   * Returns the last value in a group.
   *
   * @param collection
   * @param expr
   * @returns {*}
   */
  $last (collection, expr) {
    return (collection.length > 0) ? computeValue(collection[collection.length - 1], expr) : undefined
  },

  /**
   * Returns the population standard deviation of the input values.
   * @param  {Array} collection
   * @param  {Object} expr
   * @return {Number}
   */
  $stdDevPop (collection, expr) {
    let data = this.$push(collection, expr).filter(isNumber)
    return stddev({ data: data, sampled: false })
  },

  /**
   * Returns the sample standard deviation of the input values.
   * @param  {Array} collection
   * @param  {Object} expr
   * @return {Number|null}
   */
  $stdDevSamp (collection, expr) {
    let data = this.$push(collection, expr).filter(isNumber)
    return stddev({ data: data, sampled: true })
  }
}