![](/img/trans.png)
[英]Aggregate data from Mongo collection based on dates and other fields, and count
[英]Mongo Aggregate: how to compare with a field from another collection?
我正在嘗試實現一個從文章集合中收集未讀消息的函數。 集合中的每篇文章都有一個帶有討論評論子文檔的“討論”條目。 此類子文檔的示例是:
{
"id": NumberLong(7534),
"user": DBRef("users", ObjectId("...")),
"dt_create": ISODate("2015-01-26T00:10:44Z"),
"content": "The discussion comment content"
}
父文檔具有以下(部分)結構:
{
model: {
id: 17676,
title: "Article title",
author: DBRef("users", ObjectId(...)),
// a bunch of other fields here
},
statistics: {
// Statistics will be stored here (pageviews, etc)
},
discussions: [
// Array of discussion subdocuments, like the one above
]
}
每個用戶還具有一個last_viewed
條目,該條目是一個文檔,示例如下:
{
"17676" : "2015-01-10T00:00:00.000Z",
"18038" : "2015-01-10T00:00:00.000Z",
"18242" : "2015-01-20T00:00:00.000Z",
"18325" : "2015-01-20T00:00:00.000Z"
}
這意味着,對於ID為17676和18038的文章,以及2015年1月20日的ID為18242和18325的文章,用戶已查看2015年1月10日最后一次的討論評論。
所以我想收集文章文檔中的討論條目,對於ID為17676的文章,我想收集2015-01-10之后創建的討論條目,對於ID為18242的文章,我想顯示討論條目2015-01-20之后創建。
更新
基於Neil Lunn的回復 ,我到目前為止創建的函數是:
function getUnreadDiscussions(userid) {
user = db.users.findOne({ 'model.id': userid });
last_viewed = [];
for(var i in user.last_viewed) {
last_viewed.push({
'id': parseInt(i),
'dt': user.last_viewed[i]
});
}
result = db.articles.aggregate([
// For now, collect just articles the user has written
{ $match: { 'model.author': DBRef('users', user._id) } },
{ $unwind: '$discussions' },
{ $project: {
'model': '$model',
'discussions': '$discussions',
'last_viewed': {
'$let': {
'vars': { 'last_viewed': last_viewed },
'in': {
'$setDifference': [
{ '$map': {
'input': '$$last_viewed',
'as': 'last_viewed',
'in': {
'$cond': [
{ '$eq': [ '$$last_viewed.id', '$model.id' ] },
'$$last_viewed.dt',
false
]
}
} },
[ false ]
]
}
}
}
}
},
// To get a scalar instead of a 1-element array:
{ $unwind: '$last_viewed' },
// Match only those that were created after last_viewed
{ $match: { 'discussions.dt_create': { $gt: '$last_viewed' } } },
{ $project: {
'model.id': 1,
'model.title': 1,
'discussions': 1,
'last_viewed': 1
} }
]);
return result.toArray();
}
整個$let
事物,然后是$unwind
,將數據轉換為以下部分投影(最后一個$match
注釋掉):
{
"_id" : ObjectId("54d9af1dca71d8054c8d0ee3"),
"model" : {
"id" : NumberLong(18325),
"title" : "Article title"
},
"discussions" : {
"id" : NumberLong(7543),
"user" : DBRef("users", ObjectId("54d9ae24ca71d8054c8b4567")),
"dt_create" : ISODate("2015-01-26T00:10:44Z"),
"content" : "Some comment here"
},
"last_viewed" : ISODate("2015-01-20T00:00:00Z")
},
{
"_id" : ObjectId("54d9af1dca71d8054c8d0ee3"),
"model" : {
"id" : NumberLong(18325),
"title" : "Article title"
},
"discussions" : {
"id" : NumberLong(7554),
"user" : DBRef("users", ObjectId("54d9ae24ca71d8054c8b4567")),
"dt_create" : ISODate("2015-01-26T02:03:22Z"),
"content" : "Another comment here"
},
"last_viewed" : ISODate("2015-01-20T00:00:00Z")
}
到目前為止這里很好。 但現在的問題是,僅$match
last_viewed
日期之后創建的討論的$match
不起作用。 我收到一個空數組響應。 但是,如果我硬編碼日期並輸入$match: { 'discussions.dt_create': { $gt: ISODate("2015-01-20 00:00:00") } }
,它就可以了。 但我希望它能從last_viewed
獲取它。
我找到了另一個SO線程, 使用$cmp
運算符可以解決此問題。
聚合的最后部分是:
[
{ /* $match, $unwind, $project, $unwind as before */ },
{ $project: {
'model': 1,
'discussions': 1,
'last_viewed': 1,
'compare': {
$cmp: [ '$discussions.dt_create', '$last_viewed' ]
}
} },
{ $match: { 'compare': { $gt: 0 } } }
]
聚合框架很棒,但是在解決問題時采用了完全不同的方法。 希望這有助於任何人!
如果其他人有更好的答案/方法,我會保留這個問題沒有答案。 如果此答案已被投票足夠次數,我將接受這一答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.