我在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
获取数据.
有没有办法优化查询?
任何建议都将不胜感激.
那么有一些问题,你肯定需要索引,但你不能有复合的.它是您要在索引的数组中查询的"时间戳"值.还建议您将这些转换为数值而不是当前字符串,或者实际转换为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
的数组一样,$match
在继续进行剩余处理之前过滤你真正想要的项目.到目前为止,输入文档已经大大减少或减少了.
您可以使用MongoDB 2.6及更高版本执行的另一个选择是在您甚至**之前"过滤"数组内容$unwind
它.这会产生这样的列表:
[
{ "$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
可能比$match
之后更好.
但这是所有陈述的一般规则.你需要使用的索引,你需要先.$match
您可能会在单个查询中获得您真正想要的实际结果,但就目前而言,您的问题并非如此.尝试按照概述更改处理,您应该看到显着的改进.
如果你仍然试图接受这可能是单数的方式,那么你总是可以提出另一个问题.