我在mongoDB中有以下示例文档。
{
"location" : {
"language" : null,
"country" : "null",
"city" : "null",
"state" : null,
"continent" : "null",
"latitude" : "null",
"longitude" : "null"
},
"request" : [
{
"referrer" : "direct",
"url" : "http://www.google.com/"
"title" : "index page"
"currentVisit" : "1401282897"
"visitedTime" : "1401282905"
},
{
"referrer" : "direct",
"url" : "http://www.stackoverflow.com/",
"title" : "index page"
"currentVisit" : "1401282900"
"visitedTime" : "1401282905"
},
......
]
"uuid" : "109eeee0-e66a-11e3"
}
10845
文档100
个请求(请求数组中有100个对象)。技术/语言 - node.js
我有setProfiling
来检查执行时间
First Query - 13899ms
Second Query - 9024ms
Third Query - 8310ms
Fourth Query - 6858ms
使用索引
我要执行以下aggregation queries
来获取数据。
var match = {"request.currentVisit":{$gte:core.getTime()[1].toString(),$lte:core.getTime()[0].toString()}};
For Example:
var match = {“request.currentVisit”:{$ gte:“1401282905”,$ lte:“1401282935”}};
对于第三和第四个查询request.visitedTime
而不是request.currentVisit
第一
[
{ "$project":{
"request.currentVisit":1,
"request.url":1
}},
{ "$match":{
"request.1": {$exists:true}
}},
{ "$unwind": "$request" },
{ "$match": match },
{ "$group": {
"_id": {
"url":"$request.url"
},
"count": { "$sum": 1 }
}},
{ "$sort":{ "count": -1 } }
]
第二
[
{ "$project": {
"request.currentVisit":1,
"request.url":1
}},
{ "$match": {
"request":{ "$size": 1 }
}},
{ "$unwind": "$request" },
{ "$match": match },
{ "$group": {
"_id":{
"url":"$request.url"
},
"count":{ "$sum": 1 }
}},
{ "$sort": { "count": -1} }
]
第三
[
{ "$project": {
"request.visitedTime":1,
"uuid":1
}},
{ "$match":{
"request.1": { "$exists": true }
}},
{ "$match": match },
{ "$group": {
"_id": "$uuid",
"count":{ "$sum": 1 }
}},
{ "$group": {
"_id": null,
"total": { "$sum":"$count" }}
}}
]
第四
[
{ "$project": {
"request.visitedTime":1,
"uuid":1
}},
{ "$match":{
"request":{ "$size": 1 }
}},
{ "$match": match },
{ "$group": {
"_id":"$uuid",
"count":{ "$sum": 1 }
}},
{ "$group": {
"_id":null,
"total": { "$sum": "$count" }
}}
]
获取数据的时间超过38091 ms
。
有没有办法优化查询?
任何建议都将不胜感激。
答案 0 :(得分:8)
那么有一些问题,你肯定需要索引,但你不能有复合索引。它是您要在索引的数组中查询的“时间戳”值。还建议您将这些转换为数值而不是当前字符串,或者实际转换为BSON日期类型。后一种形式实际上是内部存储为数字时间戳值,因此通常会减小存储大小,这也会降低索引大小,并且可以更有效地匹配数值。
每个查询的一个大问题是,您在处理$unwind
之后总是潜入“数组”内容,然后使用匹配“过滤”。虽然这是您想要对结果执行的操作,但由于您尚未在早期阶段应用相同的过滤器,因此当您$unwind
时,管道中有许多文档与这些条件不匹配。结果是在此阶段您不需要处理的“大量”文档。在这里你不能使用索引。
您需要此匹配位于管道阶段的开始。在过滤实际数组之前,这会将文档缩小为“可能的”匹配。
所以以第一个为例:
[
{ "$match":{
{ "request.currentVisit":{
"$gte":"1401282905", "$lte": "1401282935"
}
}},
{ "$unwind": "$request" },
{ "$match":{
{ "request.currentVisit":{
"$gte":"1401282905", "$lte": "1401282935"
}
}},
{ "$group": {
"_id": {
"url":"$request.url"
},
"count": { "$sum": 1 }
}},
{ "$sort":{ "count": -1 } }
]
所以有一些变化。管道头部有一个$match
。这会缩小文档范围并能够使用索引。这是最重要的性能考虑因素。黄金法则,始终首先“匹配”。
你在那里的$project
是多余的,因为你不能只“投射”尚未解开的数组的字段。还有一种误解,即人们认为他们{{}}首先要减少管道。如果事实上有一个后来的$project
或$project
语句实际上限制了字段,那么效果是非常小的,那么这将是“前向优化”所以事情确实会从管道处理中取出。上面的$group
声明仍然可以进行更优化。
不需要查看数组是否实际存在于其他$match
阶段,因为您现在在管道的开头“隐式地”执行此操作。如果更多条件让您更舒服,那么将它们添加到初始管道阶段。
其余部分保持不变,因为您然后$match
数组和$unwind
来过滤您实际需要的项目,然后再继续进行剩余处理。到目前为止,输入文档已经大大减少或减少了。
您可以使用MongoDB 2.6及更高版本执行的另一个替代方法是在您甚至$match
之前“过滤”数组内容。这将产生这样的列表:
[
{ "$match":{
{ "request.currentVisit":{
"$gte":"1401282905", "$lte": "1401282935"
}
}},
{ "$project": {
"request": {
"$setDifference": [
{
"$map": {
"input": "$request",
"as": "el",
"in": {
"$cond"": [
{
"$and":[
{ "$gte": [ "1401282905", "$$el.currentVisit" ] },
{ "$lt": [ "1401282935", "$$el.currentVisit" ] }
]
}
"$el",
false
]
}
}
}
[false]
]
}
}}
{ "$unwind": "$request" },
{ "$group": {
"_id": {
"url":"$request.url"
},
"count": { "$sum": 1 }
}},
{ "$sort":{ "count": -1 } }
]
这可以通过在**$unwind
之前“过滤”数组来节省一些,这可能比之后执行$unwind
更好。
但这是所有陈述的一般规则。您需要可用的索引,并且需要$match
首先。
您可能会在单个查询中获得您真正想要的实际结果,但就目前而言,您的问题并非如此。尝试按照概述更改处理,您应该会看到显着的改进。
如果您仍然试图接受这可能是单数的方式,那么您可以随时提出另一个问题。