我是使用MongoDB的新手,我有这种类型的文档的集合:
{
"_id" : {
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
},
"prices" : [
{
"type" : "GAS_95",
"price" : 1370,
"date" : ISODate("2014-05-03T18:39:13.635Z")
},
{
"type" : "DIESEL_A",
"price" : 1299,
"date" : ISODate("2014-05-03T18:39:13.635Z")
},
{
"type" : "DIESEL_A_NEW",
"price" : 1350,
"date" : ISODate("2014-05-03T18:39:13.635Z")
},
{
"type" : "GAS_98",
"price" : 1470,
"date" : ISODate("2014-05-03T18:39:13.635Z")
}
]
}
我需要检索特定日期的价格,所以我运行这个查询:
db.gasStation.aggregate(
{ "$unwind" : "$prices"},
{ "$match" : {
"_id" : {
"coordinate" : {
"latitude" : 532144 ,
"longitude" : -33333} ,
"margin" : "N"
} ,
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00.000Z") ,
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}
});
一切都很好,我检索文档,但我认为我可以改进,我试图为_id和prices.date创建索引:
db.gasStation.ensureIndex( {
"_id" : 1,
"prices.date" : 1
} )
之后,我试着看看索引是否在我的查询中使用了解释选项,但没有使用任何索引:
{
"stages" : [
{
"$cursor" : {
"query" : {
},
"plan" : {
"cursor" : "BasicCursor",
"isMultiKey" : false,
"scanAndOrder" : false,
"allPlans" : [
{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"scanAndOrder" : false
}
]
}
}
},
{
"$unwind" : "$prices"
},
{
"$match" : {
"_id" : {
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
},
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00Z"),
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}
}
],
"ok" : 1
}
是否有任何原因导致我的查询不适合使用索引?我在MongoDB文档中读到,唯一不使用索引的管道是$group,但我不使用该功能。
尝试重新安排您的聚合管道操作符。例如,这个查询:
db.gasStation.aggregate([
{ "$match" : {
"_id" : {
"coordinate" : {
"latitude" : 532144 ,
"longitude" : -33333} ,
"margin" : "N"
}
}},
{ "$unwind" : "$prices"},
{ "$match" : {
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00.000Z") ,
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}}
], {explain:true});
产生以下输出,它现在确实显示了一些索引的使用情况:
{
"stages" : [
{
"$cursor" : {
"query" : {
"_id" : {
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
}
},
"plan" : {
"cursor" : "IDCursor",
"indexBounds" : {
"_id" : [
[
{
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
},
{
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
}
]
]
}
}
}
},
{
"$unwind" : "$prices"
},
{
"$match" : {
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00Z"),
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}
}
],
"ok" : 1
关键是要尝试在管道开始时让管道操作符(如$match和$sort)使用索引来限制访问和传递到聚合其余部分的数据量。在上面的例子中,你可以做更多的事情来提高性能,但这应该给你一个很好的想法,如何接近它。
我将引用文档:
$match和$sort管道操作符可以利用索引当它们出现在管道的开头时。
来源:http://docs.mongodb.org/manual/core/aggregation-pipeline/pipeline-operators-and-indexes
你没有$match或$sort在管道的开始,你有$unwind操作。因此,索引在这里是无用的。
编辑 -详细说明:
仍然可以将匹配条件的一部分移动到管道的开头,以便使用索引。
db.gasStation.aggregate([
{ "$match" : {
"_id" : {
"coordinate" : {
"latitude" : 532144 ,
"longitude" : -33333} ,
"margin" : "N"
}
}},
{ "$project": { "prices" : 1, "_id" : 0 } },
{ "$unwind" : "$prices"},
{ "$match" : {
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00.000Z") ,
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}}
],{explain:true});
但是,这里这个索引是不必要的:
{"_id":1, "prices.date":1}
为什么?因为管道开头的$match只过滤_id。在mongodb中,文档的_id是自动索引的,这是将在这种情况下使用的索引。
此外,您可以通过使用$project操作符删除不必要的字段来进一步优化查询。如果您不需要某个字段,请尽快删除它。