(1)我在集合中添加了以下内容:
{ "_id" : 1, "hitsPerOneSecond" : [ 2, 3, 5, 4, 1, 2, 3, 4, 1, 2 ], "startTime" : ISODate("2012-04-07T10:41:33.380Z"), "returnCodeHits" : { "300" : 5, "200" : 12 }, "xxxServer" : "xxx:8100", "statsSummarizedToSeconds" : 10, "pathStats_xxx_api_get_version" : [ 0.2280779683225852, 0.030849283020361273, 0.9947690473370484 ], "pathStats_xxx_api_get_response" : [ 1.2163705612407407, 1.0602539963494662, 1.4853219936411421 ], "type" : "xxxType", "startTimeStr" : "07-04-2012:10AM" }
{ "_id" : 2, "hitsPerOneSecond" : [ 2, 3, 5, 4, 1, 2, 3, 4, 1, 2 ], "startTime" : ISODate("2012-04-07T10:41:43.380Z"), "returnCodeHits" : { "300" : 5, "200" : 12 }, "xxxServer" : "xxx:8100", "statsSummarizedToSeconds" : 10, "pathStats_xxx_api_get_version" : [ 0.2280779683225852, 0.030849283020361273, 0.9947690473370484 ], "pathStats_xxx_api_get_response" : [ 1.2163705612407407, 1.0602539963494662, 1.4853219936411421 ], "type" : "xxxType", "startTimeStr" : "07-04-2012:10AM" }
Run Code Online (Sandbox Code Playgroud)
(2)执行以下聚合时:
db.newStats.aggregate({$unwind: "$hitsPerOneSecond"},{$group:{_id:"$startTimeStr", totalHits: {$sum: "$hitsPerOneSecond"}, totalHitsCount: {$sum: 1}, avgHit: {$avg: "$hitsPerOneSecond"}, minHit: {$min:"$hitsPerOneSecond"}, maxHit:{$max: "$hitsPerOneSecond"}}});
Run Code Online (Sandbox Code Playgroud)
(3)结果正确:
{
"result" : [
{
"_id" : "07-04-2012:10AM",
"totalHits" : 54,
"totalHitsCount" : 20,
"avgHit" : 2.7,
"minHit" : 1,
"maxHit" : 5
}
],
"ok" : 1
Run Code Online (Sandbox Code Playgroud)
}
(4)但是,我需要在上面相同的聚合中对'pathStats_xxx_api_get_response'(来自集合)执行展开,这样我就可以在上面的相同结果中得到totalResponses,totalResponsesCount,avgResponse,minResponse和maxResponse输出.因此,我的结果应该是这样的:
{
"result" : [
{
"_id" : "07-04-2012:10AM",
"totalHits" : 54,
"totalHitsCount" : 20,
"avgHit" : 2.7,
"minHit" : 1,
"maxHit" : 5,
"totalResponses" : ??
"totalResponsesCount": ??
"avgResponse" : 2.7,
"minResponse" : 1,
"maxResponse" : 5
}
],
"ok" : 1
Run Code Online (Sandbox Code Playgroud)
}
不确定如何在与我差不多相同的聚合中添加更多$ unwind!
Mar*_*ace 13
如何使用$unwind
多个阵列?你有$unwinding
多次尝试过吗?:)
db.newStats.aggregate([
{$unwind: "$hitsPerOneSecond"},
{$unwind: "$pathStats_xxx_api_get_response"},
{$group:{
_id:"$startTimeStr",
totalHits: {$sum: "$hitsPerOneSecond"},
totalHitsCount: {$sum: 1},
avgHit: {$avg: "$hitsPerOneSecond"},
minHit: {$min:"$hitsPerOneSecond"},
maxHit:{$max: "$hitsPerOneSecond"},
totalResponses: {$sum: "$pathStats_xxx_api_get_response"},
. . .
}}
]);
Run Code Online (Sandbox Code Playgroud)
请记住,骨料框架带有一个数组作为输入(请注意,我加[
,]
).在数组中,您可以根据需要添加到管道中尽可能多的聚合函数(需要引用),任何步骤的输出都将是下一个步骤的输入!
注意:
不要忘记,如果您尝试$unwind
使用不存在的键或空数组,最终根本就没有文档!这就像0
我想的那样倍增......所以有多个(可能很多)$unwind
,处理鬼的机会增加:如果任何涉及的数组都是空的,整个文档都会丢失,你的任何$group
聚合都没有得到任何结果. .
最简单的解决方案可能是通过两个单独的聚合操作来完成此操作,并将结果合并到应用程序中。
或者,您可以使用 MapReduce 操作来完成此操作:
以下映射和归约函数应该提供您正在寻找的结果:
var map = function() {
var totalHits = this.hitsPerOneSecond.map(function(a,b){return a+b;});
var totalHitsCount = this.hitsPerOneSecond.length;
var avgHit = totalHits / totalHitsCount;
var minHit = Math.min.apply(Math, this.hitsPerOneSecond);
var maxHit = Math.max.apply(Math, this.hitsPerOneSecond);
var totalResponses = pathStats_xxx_api_get_response.map(function(a,b){return a+b;});
var totalResponsesCount = this.pathStats_xxx_api_get_response.length;
var avgResponse = totalResponses / totalResponsesCount;
var minResponse = Math.min.apply(Math, this.pathStats_xxx_api_get_response);
var maxResponse = Math.max.apply(Math, this.pathStats_xxx_api_get_response);
emit(this.startTimeStr, {
"totalHits": totalHits,
"totalHitsCount": totalHitsCount,
"avgHit": avgHit,
"minHit": minHit,
"maxHit": maxHit,
"totalResponses": totalResponses,
"totalResponsesCount": totalResponsesCount,
"avgResponse": avgResponse,
"maxResponse": maxResponse,
"minResponse": minResponse
})
}
var reduce = function(key, values) {
var output = {
"totalHits": 0,
"totalHitsCount": 0,
"avgHit": 0,
"minHit": null,
"maxHit": null,
"totalResponses": 0,
"totalResponsesCount": 0,
"avgResponse": 0,
"maxResponse": null,
"minResponse": null
};
values.forEach(function(v) {
output.totalHits += v.totalHits;
output.totalHitsCount += v.totalHitsCount;
output.avgHit = output.totalHits / output.totalHitsCount;
if (output.minHit == null) {
output.minHit = v.minHit;
} else {
if (v.minHit < output.minHit) {
output.minHit = v.minHit
}
}
if (output.maxHit == null) {
output.maxHit = v.maxHit;
} else {
if (v.maxHit > output.maxHit) {
output.maxHit = v.maxHit
}
}
output.totalResponses += v.totalResponses;
output.totalResponsesCount += v.totalResponsesCount;
output.avgResponse = output.totalResponses / output.totalResponsesCount;
if (output.minResponse == null) {
output.minResponse = v.minResponse;
} else {
if (v.minResponse < output.minResponse) {
output.minResponse = v.minResponse
}
}
if (output.maxResponse == null) {
output.maxResponse = v.maxResponse;
} else {
if (v.maxResponse > output.maxResponse) {
output.maxResponse = v.maxResponse
}
}
});
return output;
}
> db.newStats.mapReduce(map, reduce, {out:{inline:1}})
{
"results" : [
{
"_id" : "07-04-2012:10AM",
"value" : {
"totalHits" : 54,
"totalHitsCount" : 20,
"avgHit" : 2.7,
"minHit" : 1,
"maxHit" : 5,
"totalResponses" : 7.523893102462698,
"totalResponsesCount" : 6,
"avgResponse" : 1.253982183743783,
"maxResponse" : 1.4853219936411421,
"minResponse" : 1.0602539963494662
}
}
],
"timeMillis" : 0,
"counts" : {
"input" : 2,
"emit" : 2,
"reduce" : 1,
"output" : 1
},
"ok" : 1,
}
>
Run Code Online (Sandbox Code Playgroud)
如果您不熟悉 MapReduce,可以在这里找到文档: http: //www.mongodb.org/display/DOCS/MapReduce
此外,MongoDB Cookbook 中还有一些很好的 MapReduce 示例: http: //cookbook.mongodb.org/
烹饪书文章“Finding Max And Min Values with Versioned Documents”的“Extras”部分http://cookbook.mongodb.org/patterns/finding_max_and_min/包含了 MapReduce 操作的分步演练,解释了如何进行函数被执行。
希望这将帮助您实现您想要的结果。如果您能够找到一种通过单个聚合操作来完成此操作的方法,请分享您的解决方案,以便社区可以从您的经验中受益。谢谢。
以下是关于 MapReduce 的一些注释,以回应您的评论:
MapReduce 在服务器上执行 JavaScript。因此,您可能会发现其他操作的性能受到影响。MapReduce 非常适合偶尔执行的操作,这些操作可能在服务器流量未达到峰值时执行。您可能会发现,使用 MapReduce 来获取大型集合中的动态统计信息并不是最佳选择。
另一方面,聚合框架依赖于本机代码并且不执行服务器端 JavaScript,使其比 MapReduce 更快。
如果可能,最好的选择是为每个可查询的文档添加字段。这会给每次插入或更新增加一点额外的开销,但如果可以避免 MapReduce 操作,结果将更快地返回。不幸的是,这对于最大值、最小值和平均值来说很困难。
如果 MapReduce 操作是唯一的选择,则可以采取一些措施来减轻其对服务器的影响。首先,可以使用 SlaveOk 在辅助节点上运行 Map Reduce。然而,由于数据无法写入辅助设备,因此输出必须内联返回,因此限制为 16MB。一些用户将从副本集中取出辅助副本,将其作为独立的 mongod 进程重新启动,对其运行 map-reduce 操作,将输出集合复制到需要的位置,然后将辅助副本重新加入副本集。
最后要考虑的一件事是增量 MapReduce: http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-IncrementalMapreduce 您可以将查询传递给 MapReduce 命令,该命令将仅匹配自最后一个映射归约,并使用归约输出选项运行映射归约操作。
希望以上内容能为您提供一些关于计算统计数据的最佳方法的思考。最好在文档中包含所需的信息,但如果不可能,则使用聚合框架将比 MapReduce 更有效。
以下是针对第二条评论的关于聚合框架和 pymongo 的注释:
聚合框架可以在pymongo中与数据库对象的命令方法一起使用。
有关命令方法的文档可以在这里找到:
http://api.mongodb.org/python/current/api/pymongo/database.html#pymongo.database.Database.command
要执行聚合操作,请使用两个键将文档传递给命令方法;“聚合”和“管道”。“aggregate”的值是要执行操作的集合的名称,“pipeline”的值是要执行聚合操作的数组。管道在“聚合框架”文档中进行了解释: http://www.mongodb.org/display/DOCS/Aggregation+Framework#AggregationFramework-Pipelines
以下是如何在 pymongo 中执行 $unwind 操作的示例:
In [1]: import pymongo
In [2]: conn = pymongo.Connection()
In [3]: db = conn.test
In [4]: result = db.command({"aggregate":"newStats", "pipeline":
[{"$unwind": "$hitsPerOneSecond"},
{"$group": {"_id":"$startTimeStr",
"totalHits": {"$sum":
"$hitsPerOneSecond"},
"totalHitsCount": {"$sum": 1},
"avgHit": {"$avg": "$hitsPerOneSecond"},
"minHit": {"$min":"$hitsPerOneSecond"},
"maxHit":{"$max": "$hitsPerOneSecond"}}}]})
In [5]: result
Out[5]:
{u'ok': 1.0,
u'result': [{u'_id': u'07-04-2012:10AM',
u'avgHit': 2.7,
u'maxHit': 5.0,
u'minHit': 1.0,
u'totalHits': 54.0,
u'totalHitsCount': 20}]}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
12764 次 |
最近记录: |