我正在尝试聚合具有数组的集合。在这个数组中,有一个reminders
数组。该文件可能如下所示:
{
_id: "1234",
dates: {
start: ISODate(),
end: ISODate()
},
reminders: [{
sendAt: ISODate(),
status: 'closed'
}, {
sendAt: ISODate(),
status: 'open'
}]
}
说第一个是今天之前,下一个是今天之后。我想要做的是获取今天之前的所有数组,或者,如果今天之前没有数组,则为空数组。我尝试了以下聚合
db.reminders.aggregate([
{ $match: { 'dates.end': { $gt: new Date } } },
{ $unwind: '$reminders' },
{
$match: {
reminders: {
$elemMatch: {
sendAt: { $lt: new Date() },
status: { $ne: 'open' }
}
}
}
}
])
但是,如果在今天之前没有提醒,它将会失败并且不予以回复。
有没有办法用mongodb聚合构建这个结构?
注意:我无法使用$filter
,因为这是在3.2
答案 0 :(得分:1)
您可以使用运算符过滤掉版本>=2.6
的子文档。
它还避免了不必要的$unwind
阶段。
示例代码:
var endDateToMatch = ISODate("2014-01-01T00:00:00Z");
var currentDate = ISODate();
db.t.aggregate([
{
$match:{"dates.end":{$gt:endDateToMatch}}
},
{
$redact:{$cond:[
{$and:[
{$ne:[{$ifNull:["$status",""]},
"open"]},
{$lt:[{$ifNull:["$sendAt",currentDate-1]},
currentDate]}
]
},
"$$DESCEND","$$PRUNE"]}
}
])
这将为每个与$match
阶段匹配的文档提供一个文档。如果您需要累积所有子文档,则需要$unwind
“提醒”和$group
_id
为null
。
答案 1 :(得分:1)
所以你基本上想要$filter
行为但是需要在早期版本中执行它,主要情况是返回文档,即使数组内容最终为空。
对于MongoDB 2.6,您可以使用$map
和$setDifference
“几乎”做同样的事情:
db.reminders.aggregate([
{ "$match": { "dates.end": { "$gt": new Date() } } },
{ "$project": {
"dates": 1,
"reminders": {
"$setDifference": [
{ "$map": {
"input": "$reminders",
"as": "reminder",
"in": {
"$cond": [
{ "$and": [
{ "$lt": [ "$$reminder.sendAt", new Date() ] },
{ "$ne": [ "$$reminder.status", "open" ] }
]},
"$$reminder",
false
]
}
}},
[false]
]
}
}}
])
只要$setDifference
生成的“设置”都是未经识别的项目,那就没问题。因此,$map
方法应用测试,要么返回内容,要么false
如果条件不匹配。 $setDifferene
基本上会删除结果中的所有false
元素,但当然“set”会将任何项目与一个项目完全相同。
如果您的MongoDB小于2.6(或“sets”的情况使上述情况无法使用),那么在查看要过滤的内容时只需要更加小心:
db.reminders.aggregate([
{ "$match": { "dates.end": { "$gt": new Date() } } },
{ "$unwind": "$reminders" },
// Count where condition matched
{ "$group": {
"_id": "$_id",
"dates": { "$first": "$dates" },
"reminders": { "$push": "$reminders" },
"matched": { "$sum": {
"$cond": [
{ "$and": [
{ "$lt": [ "$reminders.sendAt", new Date() ] },
{ "$ne": [ "$reminders.status", "open" ] }
]},
1,
0
]
}}
}},
// Substitute array where no count just for brevity
{ "$project": {
"dates": 1,
"reminders": { "$cond": [
{ "$eq": [ "$matched", 0 ] },
{ "$const": [false] },
"$reminders"
]},
"matched": 1
}},
// Unwind again
{ "$unwind": "$reminders" },
// Filter for matches "or" where there were no matches to keep
{ "$match": {
"$or": [
{
"reminder.sendAt": { "$lt": new Date() },
"reminder.status": { "$ne": "open" }
},
{ "matched": 0 }
]
}},
// Group again
{ "$group": {
"_id": "$_id",
"dates": { "$first": "$dates" },
"reminders": { "$push": "$reminders" }
}},
// Replace the [false] array with an empty one
{ "$project": {
"dates": 1,
"reminders": { "$cond": [
{ "$eq": [ "$reminders", [false] ] },
{ "$const": [] },
"$reminders"
]}
}}
])
这有点长啰嗦,但它基本上做同样的事情。
另请注意,$elemMatch
在处理$unwind
后不适用,因为内容实际上不再是数组。简单的点表示法适用于现在在单个文档中的元素。