I'm working with the followings schemas:
Question Schema:
var Question = new Schema({
title: String,
content: String,
createdBy: {
type: Schema.ObjectId,
ref: 'User',
required: true
},
answers: {
type: [ { type: Schema.Types.ObjectId, ref: 'Answer' } ]
}
});
Answer Schema:
var Answer = new Schema({
content:String,
createdBy: {
type: Schema.Types.ObjectId,
ref: 'User',
},
isBest: {
type: Boolean,
default: false
},
createdAt: Date,
votes: Number
});
I'm trying to do an aggregation where I can have as result the list of all questions with a certain fields like title
, createdBy
(which is going to be populated after the aggregate) , an answers_count
and a has_best
field which is going to be true if a question
already have a best answer (an answer with the field isBest equals to true) .
I've try with the $project
pipeline:
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);
});
});
Also I've try to $unwind
the array and then $lookup
for answers but no results when doing an answers_count
. This is what I've tried with $unwind
and $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);
});
});
So, because the $unwind
in the array, cannot $size
in answers array to do an answers_count
field, also when I tried to do $group
to having uniques questions id
like this:
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);
});
});
I have this result:
[
{
"_id": "5825f2846c7ab9ec004f14ce"
},
{
"_id": "5823b9309de40494239c95cd"
},
{
"_id": "582538366062607c0f4bcdaa"
},
{
"_id": "5855d319b6a475100c7beba2"
},
{
"_id": "5878156328dba3d02052b321"
}
]
This is the output that I'm looking for:
[
{
_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
}
]
You can try something like below.
$lookup
- This stage joins all the answers
to a question documents.
$project
- This stage projects all the required fields. answers_count
- Counts the total the number of items in an answers
array. has_best_answer
- Iterates an answers
and compares if any of the isBest
field value is true.
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] }
}
}}
}
}
]);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.