我有以下收藏:
**S_SERVER** – **S_PORT** – **D_PORT** – **D_SERVER** – **MBYTES**
L0T410R84LDYL – 2481 – 139 – MRMCRUNCHAPP – 10
MRMCASTLE – 1904 – 445 – MRMCRUNCHAPP – 25
MRMXPSCRUNCH01 – 54769 – 445 – MRMCRUNCHAPP - 2
MRMCASTLE – 2254 – 139 – MRMCRUNCHAPP - 4
MRMCASTLE – 2253 – 445 – MRMCRUNCHAPP -35
MRMCASTLE – 987 – 445 – MRMCRUNCHAPP – 100
MRMCASTLE – 2447 – 445 – MRMCRUNCHAPP – 12
L0T410R84LDYL – 2481 – 139 – MRMCRUNCHAPP - 90
MRMCRUNCHAPP – 61191 – 1640 – OEMGCPDB – 10
首先,根据从每个S_SERVER传输的总MBYTES,我需要前30个S_SERVER。这是我能够得到以下查询:
$sourcePipeline = array(
array(
'$group' => array(
'_id' => array('sourceServer' => '$S_SERVER'),
'MBYTES' => array('$sum' => '$MBYTES')
),
),
array(
'$sort' => array("MBYTES" => -1),
),
array(
'$limit' => 30
)
);
$sourceServers = $collection->aggregate($sourcePipeline);
根据从每个D_PORT传输的个人S_SERVER的总MBYTES,我还需要前30个D_PORT。我这样做是通过从上面的服务器结果运行循环并逐个为每个S_SERVER获取它们。
$targetPortPipeline = array(
array(
'$project' => array('S_SERVER' => '$S_SERVER', 'D_PORT' => '$D_PORT', 'MBYTES' => '$MBYTES')
),
array(
'$match' => array('S_SERVER' => S_SERVER(find from above query, passed one by one in for loop)),
),
array(
'$group' => array(
'_id' => array('D_PORT' => '$D_PORT'),
'MBYTES' => array('$sum' => '$MBYTES')
),
),
array(
'$sort' => array("MBYTES" => -1),
),
array(
'$limit' => $limit
)
);
$targetPorts = $collection->aggregate($targetPortPipeline);
但这个过程花费了太多时间。我需要一种有效的方法来获得相同查询中的所需结果。我知道我使用Mongodb php适配器来实现这一目标。你也可以让我知道javascript格式的聚合函数。我会把它转换成php。
答案 0 :(得分:1)
这里的问题基本上似乎是您要为最初的30个结果发出30多个查询。没有简单的解决方案,目前似乎没有一个查询,但您可以考虑一些事项。
作为补充说明,你并不孤单,因为这是我之前看到过的一个问题,我们可以将其称为"前N个结果问题"。基本上你真正想要的是组合两个结果集的某种方式,这样每个分组边界(源服务器)本身只有最大N
个结果,而在那个顶级你也将这些结果再次限制在最顶层N
个结果值。
您的第一个聚合查询前30个"源服务器的结果"你想要的,那很好。但是,您可以尝试使用"源服务器"创建一个数组,而不是从中循环其他查询。此结果中的值,并使用 $in
运算符将其传递给您的第二个查询:
db.collection.aggregate([
// Match should be first
{ "$match": { "S_SERVER": { "$in": sourceServers } } },
// Group both keys
{ "$group": {
"_id": {
"S_SERVER": "$S_SERVER",
"D_SERVER": "$D_SERVER"
},
"value": { "$sum": "$MBYTES" }
}},
// Sort in order of key and largest "MBYTES"
{ "$sort": { "S_SERVER": 1, "value": -1 } }
])
注意到你不能限制"这里包含每个"源服务器"从最初的比赛。你也不能限制"在分组边界上,这实际上是聚合框架中缺少的,否则会使其成为两个查询结果。
因为它包含每个" dest服务器"结果,并可能超过"前30"您将在代码中处理结果并在"前30"之后跳过返回的结果。在每个分组(源服务器)级别检索。根据您拥有的结果数量,这可能是也可能不是最实用的解决方案。
继续前进不太实际的地方,你很可能会把这个输出作为临时步骤转移到另一个集合中。如果你有MongoDB 2.6或更高版本,这可以像在语句末尾添加 $out
管道阶段一样简单。对于早期版本,您可以使用mapReduce执行等效语句:
db.collection.mapReduce(
function() {
emit(
{
"S_SERVER": this["S_SERVER"],
"D_SERVER": this["D_SERVER"]
},
this.MBYTES
);
},
function(key,values) {
return Array.sum( values );
},
{
"query": { "S_SERVER": { "$in": sourceServers } },
"out": { "replace": "output" }
}
)
这与前一个聚合语句基本上是相同的过程,同时还注意到 mapReduce
不对输出进行排序。这是对结果集合的其他 mapReduce
操作所涵盖的内容:
db.output.mapReduce(
function() {
if ( cServer != this._id["S_SERVER"] ) {
cServer = this._id["S_SERVER"];
counter = 0;
}
if ( counter < 30 )
emit( this._id, this.value );
counter++;
},
function(){}, // reducer is not actually called
{
"sort": { "_id.S_SERVER": 1, "value": -1 },
"scope": { "cServer": "", "counter": 0 }
}
)
这里的实施是&#34;服务器端&#34; &#34;光标跳过的版本&#34;那是前面提到的。因此,您仍在处理每个结果,但通过网络返回的结果仅限于每个&#34;源服务器&#34;下的前30个结果。
如上所述,仍然非常可怕,因为它必须以编程方式扫描&#34;通过结果丢弃您不想要的结果,并再次依赖于您的音量,您可能更好的只是为每个&#34;源服务器&#34;发出 .find()
。排序和限制结果时这些结果中的值
sourceServers.forEach(function(source) {
var cur = db.output.find({ "_id.S_SERVER": source })
.sort({ "value": -1 }).limit(30);
// do something with those results
);
这仍然是30个额外的查询,但至少你不是&#34;聚合&#34;每次这项工作已经完成。
作为一个实际上太详细的最后一点,您可以使用显示的初始聚合查询的修改形式来处理此问题。这更像是一个脚注,如果你已经读过这篇文章而没有其他看似合理的方法,那么由于可能会遇到的内存限制,这可能是最糟糕的。
引入这个的最好方法是使用&#34;理想&#34;结果&#34;前N个结果&#34;聚合,当然实际上并不存在,但理想情况下,管道的末端看起来像这样:
{ "$group": {
"_id": "$S_SERVER",
"results": {
"$push": {
"D_SERVER": "$_id.D_SERVER",
"MBYTES": "$value"
},
"$limit": 30
}
}}
所以&#34;不存在&#34;这里的因素是能够限制&#34;推送的结果数量&#34;进入每个&#34;源服务器的结果数组&#34;值。如果实现这个或类似的功能,世界肯定会是一个更好的地方,因为它可以很容易地解决这个问题。
由于它不存在,你可以使用其他方法来获得相同的结果并最终得到像this example这样的列表,除非在这种情况下实际上要复杂得多。
考虑到代码,您将按照以下方式执行某些操作:
您永远不会直接对此进行编码,并且必须编写代码来生成管道阶段。它很可能会破坏16MB的限制,可能不会在管道文档本身上,但很可能在实际的结果集上这样做,因为你将所有内容都推到了数组中。
您可能还会注意到,如果您的实际结果不包含每个服务器至少30个最高值,那么完全爆炸是多么容易。
整个方案归结为权衡哪种方法最适合您的数据和性能考虑因素:
在任何情况下,由于一般聚合的复杂性,我会定期生成您的结果集并将其存储在自己的集合中,而不是试图实时执行此操作。
数据不会是&#34;最新的&#34;结果,只有你更新它的频率一样新鲜。但实际上检索那些显示的结果变成了一个简单的查询,最多返回900个相当紧凑的结果,而没有为每个请求聚合的开销。