【问题标题】:get a sub document field in $project stage of aggregation在聚合的 $project 阶段获取子文档字段
【发布时间】:2021-04-15 12:43:05
【问题描述】:

我正在使用以下架构:

问题架构:

var Question = new Schema({
      title: String,
      content: String,
      createdBy: {
          type: Schema.ObjectId,
          ref: 'User',
          required: true
        },
      answers: {
         type: [ { type: Schema.Types.ObjectId, ref: 'Answer' } ]
       }
    });

答案架构:

var Answer = new Schema({
     content:String,
     createdBy: {
         type: Schema.Types.ObjectId,
         ref: 'User',
      },
     isBest: {
         type: Boolean,
         default: false
   },
    createdAt: Date,
    votes: Number    
});

我正在尝试进行聚合,结果可以得到具有特定字段的所有问题列表,例如 titlecreatedBy (将在聚合后填充) em>,一个answers_count 和一个has_best 字段,如果question 已经有一个最佳答案(带有字段isBest 的答案等于真),则该字段将为真。。 p>

我已经尝试使用$project 管道:

  Question.aggregate([{
  $project: {
    answers_count: { $size: '$answers' },
    title: true,
    createdAt: true,
    createdBy: true,
    has_best_answer: '$answers.isBest'
  }
}, {
  $sort: {
    createdAt: -1
  }
}], function(err, questions){
  if(err){
    return res.status(400).send({ message: err });
  }
  User.populate(questions, { path: 'createdBy', model: 'User', select: 'firstname lastname image' }, function(err, questions){
    return res.json(questions);
  });
});

我也尝试$unwind 数组,然后$lookup 寻求答案,但在执行answers_count 时没有结果。这是我用$unwind$lookup 尝试过的。

Question.aggregate([
  {
    $unwind: '$answers'
  },
  {
    $lookup: {
      from: 'answers',
      localField: 'answers',
      foreignField: '_id',
      as: 'answer'
    }
  },{
    $project: {
      title: true,
      createdBy: true,
      createdAt: true,
      has_best_answer: '$answer.isBest'
    }
  }
], function(err, questions){
  if(err){
    return res.status(400).send({ message: err });
  }
  User.populate(questions, { path: 'createdBy', model: 'User', select:
      'firstname lastname image' }, function(err, questions){
    return res.json(questions);
  });
});

所以,因为数组中的$unwind,不能在答案数组中使用$size 来执行answers_count 字段,当我尝试使用$group 来拥有唯一性questions id 时也是这样:

Question.aggregate([
  {
    $unwind: '$answers'
  },
  {
    $lookup: {
      from: 'answers',
      localField: 'answers',
      foreignField: '_id',
      as: 'answer'
    }
  },{
    $project: {
      title: true,
      createdBy: true,
      createdAt: true,
      has_best_answer: '$answer.isBest'
    }
  },
  {
    $group: { _id: '$_id' }
  }
], function(err, questions){
  if(err){
    return res.status(400).send({ message: err });
  }
  User.populate(questions, { path: 'createdBy', model: 'User', select: 'firstname lastname image' }, function(err, questions){
    return res.json(questions);
  });
});

我有这个结果:

[
   {
      "_id": "5825f2846c7ab9ec004f14ce"
   },
   {
      "_id": "5823b9309de40494239c95cd"
   },
   {
      "_id": "582538366062607c0f4bcdaa"
   },
   {
      "_id": "5855d319b6a475100c7beba2"
   },
   {
    "_id": "5878156328dba3d02052b321"
   }
 ]

这是我正在寻找的输出:

[
   {
      _id: '5825f2846c7ab9ec004f14ce',
      title: 'Some question title',
      createdBy: '5855d319b6a475100c7beba2', // this is going to be       populated,
      createdAt: '2016-11-10T00:02:56.702Z',
      answers_count: 5,
      has_best_answer: true
   },
  {
     _id: '5825f2846c7ab9ec004f14ce',
     title: 'Some other question title',
     createdBy: '5855d319b6a475100c7beba2', // this is going to be populated,
     createdAt: '2016-11-10T00:02:56.702Z',
     answers_count: 2,
     has_best_answer: false
  }
]

【问题讨论】:

  • 你能在你用过$unwind/$lookup的地方添加代码吗?添加一些您正在测试查询的示例文档。
  • 我刚刚编辑了问题:)

标签: node.js mongodb mongoose aggregation-framework


【解决方案1】:

你可以试试下面的方法。

$lookup - 此阶段将所有answers 加入到问题文档中。

$project - 此阶段投影所有必填字段。 answers_count - 计算 answers 数组中的项目总数。 has_best_answer - 迭代 answers 并比较 isBest 字段值是否为真。

Question.aggregate([
  {
    $lookup: {
      from: 'answers',
      localField: 'answers',
      foreignField: '_id',
      as: 'answers'
    }
  },{
    $project: {
      title: true,
      createdBy: true,
      createdAt: true,
      answers_count: { $size: '$answers' },
      has_best_answer: 
        { $anyElementTrue: {
            $map: {
                input: "$answers",
                as: "answer",
                in: { $eq: [ "$$answer.isBest", true] }
            }
        }}
    }
  }
]);

【讨论】:

  • 效果很好,但has_best_answer 总是false
  • 我刚刚更改了in: { $eq: [ "$$answer.isBest", true] }。你有最新的更新吗?
  • 是的,我有,因为某种原因总是错误的。
  • 您有isBest 的财产booleanstring 吗?
  • 是布尔值,isBest: { type: Boolean, default: false }
猜你喜欢
  • 2017-01-25
  • 1970-01-01
  • 2020-11-14
  • 2019-01-17
  • 2019-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多