Reputation: 1071
I have a user document like:
{
_id: "s0m3Id",
_skills: ["skill1", "skill2"],
}
Now I want to unwind this document by the _skills
field and add a score for each skill. So my aggregate looks like:
{
'$unwind': {'path': '$_skills', 'preserveNullAndEmptyArrays': true},
},
{
'$project': {
'_skills':
'label': '$_skills',
'skill_score': 1
},
}
},
Sometimes the _skills
field can be empty, however in this case I still want the user document to flow through the aggregation - hence the preserveNullAndEmptyArrays
parameter. However, the problem I'm having is that it will project a skill_score
(though with no label
) onto documents which had empty _skills
array fields. Thus, when I go to $group
the documents later on, those documents now have a non-empty _skills
array, containing a single object, namely {skill_score: 1}
. This is not what I want - I want documents which had empty (or non-existent) _skills
fields to not have any skill_score projected onto them.
So how can I conditionally project a field based on the existence of another field? Using $exists
does not help, because that is intended for querying, not for boolean expressions.
Upvotes: 12
Views: 20967
Reputation: 7748
Updated
This aggregation will set the value of skill_score
to 0
if _skills
does not exist, then use $redact
to remove the subdocument having skill_score
equals to 0
:
db.project_if.aggregate([
{
$unwind: {
path: '$_skills',
preserveNullAndEmptyArrays: true,
}
},
{
$project: {
_skills: {
label: '$_skills',
skill_score: {
$cond: {
if: {
$eq: ['$_skills', undefined]
},
then: 0,
else: 1,
}
}
}
}
},
{
$redact: {
$cond: {
if: { $eq: [ "$skill_score", 0 ] },
then: '$$PRUNE',
else: '$$DESCEND'
}
}
}
]);
Result would be like:
[
{ "_id" : '', "_skills" : { "label" : "skill1", "skill_score" : 1 } },
{ "_id" : '', "_skills" : { "label" : "skill2", "skill_score" : 1 } },
{ "_id" : '' },
]
Upvotes: 13