gpt4 book ai didi

mongodb - MongoDB高级聚合

转载 作者:可可西里 更新时间:2023-11-01 09:29:31 24 4
gpt4 key购买 nike

我是MongoDB的新手。我为我的高尔夫俱乐部做了一个私人项目来分析这一轮。
我对应用程序使用meteorjs,并在命令行上尝试了一些聚合。但我不确定我是否对这项任务有正确的看法
示例文档:

{
"_id" : "2KasYR3ytsaX8YuoT",
"course" : {
"id" : "rHmYJBhRtSt38m68s",
"name" : "CourseXYZ"
},
"player" : {
"id" : "tdaYaSvXJueDq4oTN",
"firstname" : "Just",
"lastname" : "aPlayer"
},
"event" : "Training Day",
"tees" : [
{
"tee" : 1,
"par" : 4,
"fairway" : "straight",
"greenInRegulation" : true,
"putts" : 3,
"strokes" : 5
},
{
"tee" : 2,
"par" : 5,
"fairway" : "right",
"greenInRegulation" : true,
"putts" : 2,
"strokes" : 5
},
{
"tee" : 3,
"par" : 5,
"fairway" : "right",
"greenInRegulation" : false,
"shotType": "bunker",
"putts" : 2,
"strokes" : 5
}
]
}

到目前为止我的尝试是:
db.analysis.aggregate([
{$unwind: "$tees"},
{$group: {
_id:"$player.id",
strokes: {$sum: "$tees.strokes"},
par: {$sum: "$tees.par"},
putts: {$sum: "$tees.putts"},
teesPlayed: {$sum:1}
}}
])

我想要的结果
{ 
"_id" : "tdaYaSvXJueDq4oTN",
"strokes" : 15,
"par" : 14,
"putts" : 7,
"teesPlayed" : 3
// here comes what I want to add:
"fairway.straight": 1 // where tees.fairway equals "straight"
"fairway.right": 2 // where tees.fraiway equals "right" (etc.)
"shotType.bunker": 1 // where shotType equals "bunker" etc.
}

最佳答案

根据您的总体需求以及项目的目标MongoDB服务器版本,有几种方法可以实现这一点。
虽然“Meteor”安装和默认项目设置不会“捆绑”MongoDB 3.2实例,但您的项目不必将此类实例用作外部目标。如果这是一个新的项目起步,那么我强烈建议与最新版本的可用工作。甚至可能反对最新的开发版本,这取决于您自己的目标发布周期。用最新鲜的东西工作,你的申请也会很新鲜。
因此,我们从最新的开始。
MongoDB 3.2路-快速
MongoDB 3.2的一大特点是$sum操作方式的改变,这使得它在性能方面非常突出。以前,作为$group的累加器运算符,这将处理单个数值以生成总计。
大的改进隐藏在添加的$project阶段用法中,其中$sum可以直接应用于值数组。即{ "$sum": [1,2,3] }导致6。因此,现在可以使用从源代码生成值数组的任何内容来“嵌套”操作。最值得注意的是:

db.analysis.aggregate([
{ "$group": {
"_id": "$player.id",
"strokes": {
"$sum": {
"$sum": {
"$map": {
"input": "$tees",
"as": "tee",
"in": "$$tee.strokes"
}
}
}
},
"par": {
"$sum": {
"$sum": {
"$map": {
"input": "$tees",
"as": "tee",
"in": "$$tee.par"
}
}
}
},
"putts": {
"$sum": {
"$sum": {
"$map": {
"input": "$tees",
"as": "tee",
"in": "$$tee.putts"
}
}
}
},
"teesPlayed": { "$sum": { "$size": "$tees" } },
"shotsRight": {
"$sum": {
"$size": {
"$filter": {
"input": "$tees",
"as": "tee",
"cond": { "$eq": [ "$$tee.fairway", "right" ] }
}
}
}
},
"shotsStraight": {
"$sum": {
"$size": {
"$filter": {
"input": "$tees",
"as": "tee",
"cond": { "$eq": [ "$$tee.fairway", "straight" ] }
}
}
}
},
"bunkerShot": {
"$sum": {
"$size": {
"$filter": {
"input": "$tees",
"as": "tee",
"cond": { "$eq": [ "$$tee.shotType", "bunker" ] }
}
}
}
}
}}
])

因此,这里通过对数组项中的单个字段值执行double $map技巧来分割每个字段,或者相反,使用 $sum对数组进行处理,以仅限于匹配项,并使用 $filter对结果字段的匹配长度进行处理,而结果字段更希望“co”UNTS“。
尽管这在管道建设中看起来有些冗长,但它将产生快速的结果。尽管您需要指定所有键以使用关联的逻辑生成结果,但是没有什么可以阻止“生成”作为数据集上其他查询的结果的管道所必需的数据结构。
另一种聚合方式-稍微慢一点
当然,并不是每个项目都能实际使用最新版本的东西。因此,在MongoDB 3.2版本引入上述一些运算符之前,处理数组数据并有条件地处理不同元素和和和的唯一实际方法是先处理 $size
所以本质上我们从您开始构造的查询开始,然后添加对不同字段的处理:
db.analysis.aggregate([
{ "$unwind": "$tees" },
{ "$group": {
"_id": "$player.id",
"strokes": { "$sum": "$tees.strokes" },
"par": { "$sum": "$tees.par" },
"putts": { "$sum": "$tees.putts" },
"teedsPlayed": { "$sum": 1 },
"shotsRight": {
"$sum": {
"$cond": [
{ "$eq": [ "$tees.fairway", "right" ] },
1,
0
]
}
},
"shotsStraight": {
"$sum": {
"$cond": [
{ "$eq": [ "$tees.fairway", "straight" ] },
1,
0
]
}
},
"bunkerShot": {
"$sum": {
"$cond": [
{ "$eq": [ "$tees.shotType", "bunker" ] },
1,
0
]
}
}
}}
])

因此,您应该注意到,与第一个列表仍然有“一些”相似之处,在 $unwind语句中都有 $filter参数中的一些逻辑的地方,该逻辑在这里被转换为 "cond"运算符。
作为一个“三元”运算符(if/then/else),它的工作是计算一个逻辑条件(if),然后返回下一个条件为 $cond(then)的参数,或者返回最后一个条件为 true(else)的参数。在这种情况下,要么 false要么 1取决于测试条件是否匹配。这将根据需要为 0提供“计数”。
无论哪种说法,结果都是这样的:
{
"_id" : "tdaYaSvXJueDq4oTN",
"strokes" : 15,
"par" : 14,
"putts" : 7,
"teesPlayed" : 3,
"shotsRight" : 2,
"shotsStraight" : 1,
"bunkerShot" : 1
}

因为这是一个带 $sum的聚合语句,所以一个规则是“键”(除了需要在构造的语句中指定之外)必须在结构的“顶层”中。因此, $group中不允许有“嵌套”结构,因此每个键都有完整的名称。
如果确实必须转换,则可以在每个示例中的 $group后面添加 $project阶段:
{ "$project": {
"strokes": 1,
"par": 1,
"putts": 1,
"teesPlayed": 1,
"fairway": {
"straight": "$shotsStraight",
"right": "$shotsRight"
},
"shotType": {
"bunker": "$bunkerShot"
}
}}

所以可以做一些“重新塑造”,但是当然所有的名字和结构都必须被指定,尽管理论上你可以只在代码中生成这些。毕竟它只是一个数据结构。
这里的底线是 $group增加了成本,而且成本相当高。它基本上是要为管道中的每个文档添加一个副本,以便处理每个文档中包含的每个数组元素。因此,不仅有加工所有这些生产出来的东西的成本,而且还有一个首先“生产”它们的成本。
MapReduce-速度更慢,但按键更灵活
最后作为一种方法
db.analysis.mapReduce(
function() {

var data = { "strokes": 0 ,"par": 0, "putts": 0, "teesPlayed": 0, "fairway": {} };

this.tees.forEach(function(tee) {
// Increment common values
data.strokes += tee.strokes;
data.par += tee.par;
data.putts += tee.putts;
data.teesPlayed++;

// Do dynamic keys
if (!data.fairway.hasOwnProperty(tee.fairway))
data.fairway[tee.fairway] = 0;
data.fairway[tee.fairway]++;

if (tee.hasOwnProperty('shotType')) {
if (!data.hasOwnProperty('shotType'))
data.shotType = {};
if (!data.shotType.hasOwnProperty(tee.shotType))
data.shotType[tee.shotType] = 0;
data.shotType[tee.shotType]++
}

});

emit(this.player.id,data);
},
function(key,values) {
var data = { "strokes": 0 ,"par": 0, "putts": 0, "teesPlayed": 0, "fairway": {} };

values.forEach(function(value) {
// Common keys
data.strokes += value.strokes;
data.par += value.par;
data.putts += value.putts;
data.teesPlayed += value.teesPlayed;

Object.keys(value.fairway).forEach(function(fairway) {
if (!data.fairway.hasOwnProperty(fairway))
data.fairway[fairway] = 0;
data.fairway[fairway] += value.fairway[fairway];
});

if (value.hasOwnProperty('shotType')) {
if (!data.hasOwnProperty('shotType'))
data.shotType = {};
Object.keys(value.shotType).forEach(function(shotType) {
if (!data.shotType.hasOwnProperty(shotType))
data.shotType[shotType] = 0;
data.shotType[shotType] += value.shotType[shotType];
});
}
});

return data;

},
{ "out": { "inline": 1 } }
)

这个输出可以用嵌套结构立即完成,但当然是在“key/value”的mapreduce输出形式中,因为“key”是分组 $unwind,“value”包含所有输出:
            {
"_id" : "tdaYaSvXJueDq4oTN",
"value" : {
"strokes" : 15,
"par" : 14,
"putts" : 7,
"teesPlayed" : 3,
"fairway" : {
"straight" : 1,
"right" : 2
},
"shotType" : {
"bunker" : 1
}
}
}

mapreduce的 _id选项可以是 "out",如图所示,您可以将所有结果放入内存(并在16mb bson限制内),也可以是另一个集合,稍后可以从中读取。对于 "inline",有一个类似的 $out,但这通常是否定的,因为聚合输出可以作为“游标”使用,除非您确实希望它位于集合中。
结束语
所以这一切都取决于你真的想怎么做。如果速度是最重要的,那么 .aggregate()通常会产生最快的结果。另一方面,如果您想“动态地”使用生成的“键”,那么 .aggregate()允许逻辑通常是自包含的,而不需要另一个检查过程来生成所需的聚合管道语句。

关于mongodb - MongoDB高级聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35606546/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com