Проблема в $ в сочетании с сортировкой.
Если вы сможете удалить одно или другое, это значительно ускорит ваш запрос.
Поскольку у вас не может быть нескольких индексов, которые имеют ключи значений массива (multikeys, как они их называют), вы хотите выбрать наиболее гранулированный массив из вашего запроса для индексации. В вашем примере запроса это могут быть ключевые слова.
Итак, чтобы сделать ваш запрос немного быстрее, вы должны поместить индекс в {ключевые слова: 1, счет: -1}. Это отсканирует индекс ключевых слов, отфильтровывает другие требования запроса по тегам и reachable_via, а затем сортирует их по убыванию оценки. Я проверил это, собрав 5 миллионов документов, похожих на ваш, и использовал индекс по значениям, который действительно хорошо отфильтровал работу.
Вот пример запроса из оболочки mongo (извините, я не эксперт по mongoid):
> db.test.find({keywords:{$in:["keyword15", "keyword18"]}, tags:{$in:["shopping","web20"]}, reachable_via:{$in:["email"]}}).sort({score:-1}).limit(10).explain();
{
"cursor" : "BtreeCursor keywords_1_score_-1 multi",
"nscanned" : 1750873,
"nscannedObjects" : 1750872,
"n" : 10,
"scanAndOrder" : true,
"millis" : 11999,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"keywords" : [
[
"keyword15",
"keyword15"
],
[
"keyword18",
"keyword18"
]
],
"score" : [
[
{
"$maxElement" : 1
},
{
"$minElement" : 1
}
]
]
}
}
Если вы можете изменить свой запрос на запрос только по одному ключевому слову, это заставит его использовать индекс намного эффективнее, получая 10 лучших баллов по определенному ключевому слову за 0 мс.
> db.test.find({keywords:"keyword15", tags:{$in:["shopping","web20"]}, reachable_via:{$in:["email"]}}).sort({score:-1}).limit(10).explain();
{
"cursor" : "BtreeCursor keywords_1_score_-1",
"nscanned" : 14,
"nscannedObjects" : 14,
"n" : 10,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"keywords" : [
[
"keyword15",
"keyword15"
]
],
"score" : [
[
{
"$maxElement" : 1
},
{
"$minElement" : 1
}
]
]
}
}
Вот еще один пример. Я переместил счет из сортировки в запрос (запрос точного результата без ограничения). Это помогает ускорить запрос, если вы ищете только лучший результат или что-то в этом роде.
> db.test.find({keywords:{$in:["keyword15", "keyword18"]}, tags:{$in:["shopping","web20"]}, reachable_via:{$in:["email"]}, score:9}).explain();
{
"cursor" : "BtreeCursor keywords_1_score_-1 multi",
"nscanned" : 175583,
"nscannedObjects" : 175581,
"n" : 82345,
"millis" : 999,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : true,
"indexOnly" : false,
"indexBounds" : {
"keywords" : [
[
"keyword15",
"keyword15"
],
[
"keyword18",
"keyword18"
]
],
"score" : [
[
9,
9
]
]
}
}
Промыть, повторить для других комбинаций запросов. Выберите в запросе самое большое поле массива гранулярности, индексируйте его вместе с полем сортировки. Если вы можете ограничить запрос, чтобы он не использовал $ in в индексированном массиве, это идеально.
Мой тестовый скрипт находится здесь:
https://gist.github.com/2091880
У тестового скрипта есть несколько слабых мест, например, тот факт, что почти в каждом документе есть ключевое слово1, поэтому выясняется, что при запросе по ключевому слову1, хотя у него есть индекс, сканирование коллекции выполняется быстрее. В любом случае, мне было немного лень случайным образом выбирать ключевые слова, но в реальной жизни это не составило бы проблем.