MongoDB - Aggregate: как группировать в зависимости от запроса - PullRequest
0 голосов
/ 31 января 2020

Я хочу агрегировать по коллекции, в которой указан тип. Типы поступают из строки запроса и могут быть днем, месяцем или годом. В зависимости от того, какой тип выбирают пользователи, я хочу сгруппировать. Например: если пользователь выбирает «месяц», я хочу сгруппировать по месяцам.

Event.aggregate([
    {
      $lookup: { from: Product.collection.name, localField: 'product', foreignField: '_id', as: 'product' }
    },
    {
      $group: {
        _id: { $month: { date: "$date" } },
        price: { $sum: "$price" },
        result: { $mergeObjects: { name: "$product.name", _id: "$product._id" } },
        count: { $sum: 1 }
      },
    },
  ]).then(response => {
    console.log(response)
    res.send(response)
  })

Я не могу понять, как найти чистое решение. До сих пор единственный способ, который я нашел, заключался в использовании условного выражения перед Model.aggregate ([]) ...

  if (req.query.dateAvailability && req.query.dateAvailability === 'month') {
    Event.aggregate([
      {
        $lookup: { from: Product.collection.name, localField: 'product', foreignField: '_id', as: 'product' }
      },
      {
        $group: {
          _id: { $month: { date: "$date" } },
          price: { $sum: "$price" },
          result: { $mergeObjects: { name: "$product.name", _id: "$product._id" } },
          count: { $sum: 1 }
        },
      },
    ]).then(response => {
      console.log(response)
      res.send(response)
    })
  } else if (req.query.dateAvailability && req.query.dateAvailability === 'day') {
    Event.aggregate([
      {
        $lookup: { from: Product.collection.name, localField: 'product', foreignField: '_id', as: 'product' }
      },
      {
        $group: {
          _id: { $dateToString: { format: "%d-%m-%Y", date: "$date" } },
          price: { $sum: "$price" },
          result: { $mergeObjects: { name: "$product.name", _id: "$product._id" } },
          count: { $sum: 1 }
        },
      },
    ]).then(response => {
      console.log(response)
      res.send(response)
    })
  } else if (req.query.dateAvailability && req.query.dateAvailability === 'year') {
    Event.aggregate([
      {
        $lookup: { from: Product.collection.name, localField: 'product', foreignField: '_id', as: 'product' }
      },
      {
        $group: {
          _id: { $year: { date: "$date" } },
          price: { $sum: "$price" },
          result: { $mergeObjects: { name: "$product.name", _id: "$product._id" } },
          count: { $sum: 1 }
        },
      },
    ]).then(response => {
      console.log(response)
      res.send(response)
    })
  }

Событие модели:

const EventSchema = new Schema({

  client: {
    type: [{
      type: Schema.Types.ObjectId,
      ref: 'Client'
    }]
  },

  product: {
    type: [{
      type: Schema.Types.ObjectId,
      ref: 'Product'
    }]
  },

  date: {
    type: Date,
    maxlength: 64,
    lowercase: true,
    trim: true
  },

  place: {
    type: String,
    maxlength: 1200,
    minlength: 1,
  },

  price: {
    type: Number
  },

  comment: {
    type: String,
    maxlength: 12000,
    minlength: 1,
  },

  status: {
    type: Number,
    min: 0,
    max: 1,
    default: 0,
    validate: {
      validator: Number.isInteger,
      message: '{VALUE} is not an integer value'
    }
  },
},
  {
    toObject: { virtuals: true },
    toJSON: { virtuals: true }
  },
  {
    timestamps: true
  },
);

Ответы [ 2 ]

2 голосов
/ 31 января 2020

Нет решения для волхвов c, чтобы исключить использование логики c. В таких случаях это всегда будет необходимо.

Однако мы можем сделать код немного более сексуальным:

let groupCond;
if (req.query.dateAvailability && req.query.dateAvailability === 'month') {
    groupCond = { $month: { date: "$date" } };
} else if (req.query.dateAvailability && req.query.dateAvailability === 'day') {
    groupCond = { $dateToString: { format: "%d-%m-%Y", date: "$date" } };
} else if (req.query.dateAvailability && req.query.dateAvailability === 'year') {
    groupCond =  { $year: { date: "$date" } };
}

Event.aggregate([
    {
        $lookup: { from: Product.collection.name, localField: 'product', foreignField: '_id', as: 'product' }
    },
    {
        $group: {
            _id: groupCond,
            price: { $sum: "$price" },
            result: { $mergeObjects: { name: "$product.name", _id: "$product._id" } },
            count: { $sum: 1 }
        },
    },
]).then(response => {
    console.log(response)
    res.send(response)
})
1 голос
/ 31 января 2020

В вашей проблеме нет волхвов c, логика c должна где-то произойти. Либо с оператором if вне запроса, либо с оператором $ switch внутри запроса, если вы используете версию mongodb 3.4 или выше.

{"$group": {
  "_id":{ 
    "$switch": { 
      "branches": [
        { "case":{ "$eq": [ { "$literal": "day" }, { "$literal": req.query.dateAvailability } ] },
         "then": { $dateToString: { format: "%d-%m-%Y", date: "$date" } } },
        { "case":{ "$eq": [ { "$literal": "month" }, { "$literal": req.query.dateAvailability } ] },
         "then": { $month: { date: "$date" } } },
        { "case":{ "$eq": [ { "$literal": "year" }, { "$literal": req.query.dateAvailability } ] },
         "then": { $year: { date: "$date" } } }
      ], 
      "default": { ... default logic for when dateAvailability isn't set ... } 
    } 
  }
  ... rest of the group operation
} }
...