mongo / mongo调查数据的聚合管道查询

mongo / mongoose aggregation pipeline query for survey data

我正在尝试编写查询以获取存储在 mongo 中的某些调查数据的所有结果。棘手的部分是有些问题是单一答案的单选题,有些问题是多select类型的问题,有些是需要取平均值的值,所以我想根据问题的类型进行不同的聚合.

结果存储在这样的模式中,数组中的每一项都是调查响应。

[
  {
    metaData: {
      survey: new ObjectId("62206ea0b31be3535abac547")
    },
    answers: {
      'question1': 'a',
      'question2': 'a',
      'question3': ['a','c'],
      'question4': 3
    },
    createdAt: 2022-03-03T07:30:40.517Z,
  },
  {
    metaData: {
      survey: new ObjectId("62206ea0b31be3535abac547"),
    },
    answers: {
      'question1': 'a',
      'question2': 'b',
      'question3': ['a','c'],
      'question4': 2
    },
    createdAt: 2022-03-03T07:30:40.518Z,
  },
  {
    metaData: {
      survey: new ObjectId("62206ea0b31be3535abac547"),
    },
    answers: {
      'question1': 'b',
      'question2': 'c',
      'question3': ['b']
      'question4': 1
    },
    createdAt: 2022-03-03T07:30:40.518Z,
  }
]

问题1和问题2是单选题,所以只能有一个答案,而问题3是多选select,所以用户可以有多个答案。问题4是一个需要取平均值的值。

我认为有一些方法可以在单个聚合管道中通过方面、分组、过滤器、投影等的某种组合来实现这一点,但我被卡住了。

我想得到这样的最终结果

{ 
  'question1' : {
    'a' : 2,
    'b' : 1 
  },
  'question2' : {
    'a' : 1,
    'b' : 1,
    'c' : 1,
  },
  'question3' : {
    'a' : 2,
    'b' : 1,
    'c' : 2,
  },
  'question4' : 2 //avg (3+2+1)/3
}

或者更好:

{ 
  'radio': {
    'question1' : {
      'a' : 2,
      'b' : 1 
    },
    'question2' : {
      'a' : 1,
      'b' : 1,
      'c' : 1,
     },
  },
  'multi': {
    'question3' : {
      'a' : 2,
      'b' : 1,
      'c' : 2,
    }
  },
  'avg' : {
    'question4' : 2
  }
}

我的管道看起来像这样:

Response.aggregate([
{ $match: { 'metaData.survey': surveyId} }, // filter only for the specific survey
{ $project: { // I assume I have to turn the answers into an array
  "answers": { $objectToArray: "$answers" },
  "createdAt": "$createdAt"
  }
},
// maybe facet here?
// conceptually, In the next stage I'd want to bucket the questions
// by type with something like below, then perform the right type of
// aggregation depending on the question type
// if $in [$$answers.k  ['question1, 'question2']] group by k, v and count
// if $in [$$answers.k  ['question3']] unwind and count each unique value?
// { $facet : { radio: [], multi:[]}}
])

基本上,我知道哪个问题 ID 是一个单选题或一个多选题 select,我只是想弄清楚如何格式化管道以根据 questionId 在一个已知数组。

如果我能弄清楚如何根据 createdAt 时间 day/month 对

进行分组,则加分
db.collection.aggregate([
  {
    $match: {}
  },
  {
    $project: { answers: { $objectToArray: "$answers" } }
  },
  {
    $unwind: "$answers"
  },
  {
    $unwind: "$answers.v"
  },
  {
    $group: {
      _id: "$answers",
      c: { "$sum": 1 }
    }
  },
  {
    $group: {
      _id: "$_id.k",
      v: { "$push": { k: "$_id.v", v: "$c" } }
    }
  },
  {
    $group: {
      _id: null,
      v: { "$push": { k: "$_id", v: { "$arrayToObject": "$v" } } }
    }
  },
  {
    $set: { v: { $arrayToObject: "$v" } }
  },
  {
    $replaceWith: "$v"
  }
])

mongoplayground


db.collection.aggregate([
  {
    $match: {}
  },
  {
    $project: { answers: { $objectToArray: "$answers" } }
  },
  {
    $unwind: "$answers"
  },
  {
    $set: {
      "answers.type": {
        $switch: {
          branches: [
            {
              case: { $isArray: "$answers.v" },
              then: "multi"
            },
            {
              case: { $eq: [ { $type: "$answers.v" }, "string" ] },
              then: "radio"
            },
            {
              case: { $isNumber: "$answers.v" },
              then: "avg"
            }
          ],
          default: "other"
        }
      }
    }
  },
  {
    $unwind: "$answers.v"
  },
  {
    $group: {
      _id: "$answers",
      c: { $sum: 1 }
    }
  },
  {
    $group: {
      _id: "$_id.k",
      type: { $first: "$_id.type" },
      v: {
        $push: {
          k: { $toString: "$_id.v" },
          v: "$c"
        }
      }
    }
  },
  {
    $group: {
      _id: "$type",
      v: {
        $push: {
          k: "$_id",
          v: { $arrayToObject: "$v" }
        }
      }
    }
  },
  {
    $group: {
      _id: null,
      v: {
        $push: {
          k: "$_id",
          v: { $arrayToObject: "$v" }
        }
      }
    }
  },
  {
    $set: { v: { $arrayToObject: "$v" } }
  },
  {
    $replaceWith: "$v"
  },
  {
    $set: {
      avg: {
        $arrayToObject: {
          $map: {
            input: { $objectToArray: "$avg" },
            as: "s",
            in: {
              k: "$$s.k",
              v: {
                $avg: {
                  $map: {
                    input: { $objectToArray: "$$s.v" },
                    as: "x",
                    in: { $multiply: [ { $toInt: "$$x.k" }, "$$x.v" ] }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
])

mongoplayground