我在MongoDB中有两个集合,一个带有用户,一个带有操作。用户大致如下:
{_id: ObjectId("xxxxx"), country: "UK",...}
和类似
的操作{_id: ObjectId("yyyyy"), createdAt: ISODate(), user: ObjectId("xxxxx"),...}
我正在尝试按国家划分事件和不同用户。前半部分工作得很好,但是当我尝试添加子查询来提取国家时,我只得到国家
的空值。db.events.aggregate({
$match: {
createdAt: { $gte: ISODate("2013-01-01T00:00:00Z") },
user: { $exists: true }
}
},
{
$group: {
_id: {
year: { $year: "$createdAt" },
user_obj: "$user"
},
count: { $sum: 1 }
}
},
{
$group: {
_id: {
year: "$_id.year",
country: db.users.findOne({
_id: { $eq: "$_id.user_obj" },
country: { $exists: true }
}).country
},
total: { $sum: "$count" },
distinct: { $sum: 1 }
}
})
这里没有join,只有us bears
所以MongoDB"不做连接"。您可能在shell中尝试过这样的操作,例如:
db.events.find().forEach(function(event) {
event.user = db.user.findOne({ "_id": eventUser });
printjson(event)
})
但这并不像你想象的那样。它实际上完全像它看起来的那样,对从"事件"集合返回的每个项目在"用户"集合上运行查询,"到"one_answers"从"客户端",而不是在服务器上运行。
出于同样的原因,聚合管道中的"嵌入"语句不会像那样工作。与上述不同的是,"整个管道"逻辑在执行之前被发送到服务器。如果你像这样选择"UK"用户:
db.events.aggregate([
{ "$match": {
"user": {
"$in": db.users.distinct("_id",{ "country": "UK" })
}
}}
])
那么.distinct()
查询实际上是在"客户端"而不是服务器上求值的,因此聚合管道中的任何文档值都不可用。因此,.distinct()
首先运行,将其数组作为参数返回,然后将整个管道发送到服务器。这就是执行的顺序。
纠正
对于想要运行的查询类型,您至少需要某种程度的反规范化。所以你通常有两个选择:
将整个用户对象数据嵌入到事件数据中
至少在事件数据中嵌入"一些"用户对象数据。在本例中是"country",因为您将使用它
那么,如果你遵循这里的"second"情况,至少"扩展"一下现有的数据,包括"country",像这样:
{
"_id": ObjectId("yyyyy"),
"createdAt": ISODate(),
"user": {
"_id": ObjectId("xxxxx"),
"country": "UK"
}
}
那么"聚合"过程就变得简单了:
db.events.aggregate([
{ "$match": {
"createdAt": { "$gte": ISODate("2013-01-01T00:00:00Z") },
"user": { "$exists": true }
}},
{ "$group": {
"_id": {
"year": { "$year": "$createdAt" },
"user_id": "$user._id"
"country": "$user.country"
},
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.country",
"total": { "$sum": "$count" },
"distinct": { "$sum": 1 }
}}
])
我们不正常
修复你的数据,包括它需要的信息在一个单一的集合,我们"不做连接"是一个相对简单的过程。只是上面原始查询示例的一个变体:
var bulk = db.events.intitializeUnorderedBulkOp(),
count = 0;
db.users.find().forEach(function(user) {
// update multiple events for user
bulk.find({ "user": user._id }).update({
"$set": { "user": { "_id": user._id, "country": user.country } }
});
count++;
// Send batch every 1000
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.events.intitializeUnorderedBulkOp();
}
});
// Clear any queued
if ( count % 1000 != 0 )
bulk.execute();
这就是它的全部。单个查询到MongoDB服务器得到"一个集合"one_answers"一个集合只有"的工作。即使是上面所示的神奇的"批量操作"仍然只能在单个集合上"批处理"。
如果你想做像"聚合相关属性"这样的事情,那么你"必须"在你要聚合数据的集合中包含这些属性。将数据放在单独的集合中是完全可以的,例如"用户"通常会附加更多的信息,而不仅仅是一个"_id"和一个"country"。
但这里的要点是,如果您需要"国家"来分析"用户"的"事件"数据,那么也要将其包含在数据中。最有效的服务器连接是"预连接",这是理论在实践中的一般做法。