Reputation: 3459
If I have documents and I'm aggregating and I want to sub a field like:
{data: {'date_created': '2011-01-01', 'title': 'abc'}, 'owner': 'Jim'}
{data: {'date_created': '2011-05-01', 'title': 'def'}, 'owner': 'Bob'}
{data: {'date_created': '2011-03-01', 'title': 'ghi'}, 'owner': 'Jim'}
{data: {'date_created': '2011-03-01', 'title': ''}, 'owner': 'Sam'}
and I want to aggregate so that I only take titles created before a certain date or return an empty list, how would I structure the aggregate pipeline?
So desired output would be:
{owner: "Jim", titles: ["abc", "def"],
owner: "Bob", titles: [],
owner: "Sam", titles: []}
I have an aggregate pipeline that goes something like:
lookup => unwind =>
{'$match':
{'$or': [{'data.date_created': {'$lte': requested_date}}, {'data.title': {'$exists': False}}]}}}
but I can't figure out how to cast the data where the date created is after the required time to be blank so it'll be grouped with the blank titles.
Upvotes: 0
Views: 121
Reputation: 576
Hope this helps. You can do conditional push on the basis of date and then filter out the blank items from the titles array.
db.collection.aggregate([
{"$group":{
_id:"$owner",titles:{
$push: { $cond:[
{ $lte: [ "$data.date_created", "2011-03-01" ]},"$data.title",""
]
}
}
}
},
{
$project: {
_id:0,
owner : "$_id",
titles: {
$filter: {
input: "$titles",
as: "titles",
cond: { $ne: [ "$$titles", "" ] }
}
}
}
}
])
Result :
{ "titles" : [ ], "owner" : "Bob" }
{ "titles" : [ "abc", "ghi" ], "owner" : "Jim" }
{ "titles" : [ ], "owner" : "Sam" }
Upvotes: 2
Reputation: 9497
If you didn't strictly require the empty arrays when there are no items matching your criteria, that would make it much simpler. If that were the case you could simply do a $match
and a $group
with either $push
or $addToSet
.
For example:
db.foo.aggregate([
{ $match : {'data.date_created': {'$lte': "2011-03-01"}}},
{ $group: {
_id: "$owner",
titles : {$push : "$data.title"}
}}
])
Results:
{ "_id" : "Sam", "titles" : [ "" ] }
{ "_id" : "Jim", "titles" : [ "abc", "ghi" ] }
Another option would be to do your $group / $push
first, then filter the array elements using $filter (requires MongoDB 3.2+).
For example:
db.foo.aggregate([
{ $group: {
_id: "$owner",
data : {$push : "$data"}
}},
{
$project: {
titles: {
$filter: {
input: "$data",
as: "item",
cond: { $lte: [ "$$item.date_created", "2011-03-01" ] }
}
}
}}
])
Results:
{ "_id" : "Sam", "titles" : [ { "date_created" : "2011-03-01", "title" : "" } ] }
{ "_id" : "Bob", "titles" : [ ] }
{ "_id" : "Jim", "titles" : [ { "date_created" : "2011-01-01", "title" : "abc" }, { "date_created" : "2011-03-01", "title" : "ghi" } ] }
Upvotes: 1