问题
In my MongoDB people
collection I need to filter people with the same 'alias' property value, keeping the first one of them, and also keeping all people with a null 'alias'.
Some sample people data:
{ "_id" : "1", "flag" : true, "name" : "Alice", "alias" : null },
{ "_id" : "2", "flag" : true, "name" : "Bob", "alias" : "afa776bea788cf4c" },
{ "_id" : "3", "flag" : true, "name" : "Bobby", "alias" : "afa776bea788cf4c" },
{ "_id" : "4", "flag" : true, "name" : "Cristina", "alias" : null },
{ "_id" : "5", "flag" : false, "name" : "Diego", "alias" : null },
{ "_id" : "6", "flag" : true, "name" : "Zoe", "alias" : "2211293acc82329a" },
This is the result I expect:
{ "_id" : "1", "name" : "Alice", "alias" : null },
{ "_id" : "2", "name" : "Bob", "alias" : "afa776bea788cf4c" },
{ "_id" : "4", "name" : "Cristina", "alias" : null },
{ "_id" : "6", "name" : "Zoe", "alias" : "2211293acc82329a" },
I've come with this initial query:
db.people.aggregate({ $group: { _id: '$alias', alias: { $first: '$alias' } } })
The first problem I face is that this returns only _id
and alias
fields, but I need all of them...
UPDATE: I have changed a bit sample data to better reflect my use case, since @user3100115 answer solves the issue for old sample data, but not for real data.
What I did change:
add one more document ("Cristina") with a null alias (my documents all have "alias" field), since I need all documents with a null alias value to be returned, and not just the first one.
add one more boolean property ("flag"), which I need to be able to match, too... I.e.: using
find()
I'd do:db-people.find({flag:true})
, but I don't understand how to filter with more fields withaggregate()
...
Please tell me if you think I should better place a new question...
回答1:
You can use $first to return the the _id
value in the $group stage.
db.people.aggregate([
{ '$match': { 'flag': true } },
{ '$project': {
'name': 1,
'alias': {
'$cond': [
{ '$eq': [ '$alias', null ] },
'$_id',
'$alias'
]
}
}},
{ '$group': {
'_id': '$alias',
'name': { '$first': '$name' },
'id': { '$first': '$_id' }
}},
{ '$project': {
'alias': {
'$cond': [
{ '$eq': [ '$id', '$_id' ] },
null,
'$_id'
]
},
'name': 1,
'_id': '$id'
}}
])
Which returns:
{ "_id" : "6", "name" : "Zoe", "alias" : "2211293acc82329a" }
{ "_id" : "4", "name" : "Cristina", "alias" : null }
{ "_id" : "2", "name" : "Bob", "alias" : "afa776bea788cf4c" }
{ "_id" : "1", "name" : "Alice", "alias" : null }
回答2:
If you need all the fields including the original _id
you need to specify them all in the grouping and then additionally do a projection:
db.entries.aggregate([{ $group: { _id: '$alias', alias: { $first: '$alias' }, name: {$first: '$name'}, id: {$first: '$_id'} } }, {$project: {_id: '$id', name: '$name', alias: '$alias'}}])
回答3:
If you need all your fields you can use $$ROOT to references the root document. It will keep all fields of the document in a one field.
Person.aggregate([
{
$project:
{
alias: {$ifNull: ['$alias', "$_id"] },
name: 1,
document: "$$ROOT"
}
},
{ $group: { _id: "$alias", name: {$first: "$name"}, document: {$first: "$document"}}},
{
$project: { _id:0, document: 1}
}
], function (err, documents) {
var result = documents.map(function(doc){
return doc.document;
});
});
来源:https://stackoverflow.com/questions/34375163/how-to-use-mongodb-aggregate-to-get-the-first-of-each-group-including-nulls