Как реструктурировать монго-документ, чтобы он действительно попадал в индексы? - PullRequest
0 голосов
/ 19 марта 2012

В идеале у меня есть монго-документ, который выглядит ниже.Мне нужна возможность запрашивать любые два атрибута, а затем упорядочивать по третьему.

Документ:

{

 "tags" => ["ads", "shopping", "web20", "newspaper", "others..."],
 "reachable_via" => ["email", "twitter", "facebook", "contact_form", "phone"],
 "keywords" => ["keyword1", "keyword2", "keyword3"], 
 "score" => 4 #scalar of 0 - 10,
 "read_in_project_ids => [124, 433,556]

}

Пример запроса с использованием синтаксиса Mongoid:

Document.any_in(:keywords => ["keyword1", "keyword2"]).where(:tags.in => ["ads", "shopping"], :reachable_via.in => ["email"]).order_by([:presence_score, :desc]).limit(10)

Этот запрос работает, но они не используют индексы.Кроме того, я попытался реструктурировать эту штуку, чтобы она работала тремя разными способами, без всякой удачи.

Сейчас у меня 3,8 миллиона документов, и этот запрос может занять 45-60 секунд.

Итак, как мне реструктурировать, чтобы поддерживать гибкость набора полей массива, одновременно получая преимущества при индексации ?.

К вашему сведению, ключевые слова могут быть длиной в сотни (и добавляются пользователями), но теги и элементы reachable_via являются фиксированными (7 вариантов, которые будут расти), а теги - это около 20 вариантов, которые будут расти, и управляютсякод приложения.

Спасибо!

Ответы [ 2 ]

1 голос
/ 19 марта 2012

Проблема в $ в сочетании с сортировкой.

Если вы сможете удалить одно или другое, это значительно ускорит ваш запрос.

Поскольку у вас не может быть нескольких индексов, которые имеют ключи значений массива (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, хотя у него есть индекс, сканирование коллекции выполняется быстрее. В любом случае, мне было немного лень случайным образом выбирать ключевые слова, но в реальной жизни это не составило бы проблем.

0 голосов
/ 19 марта 2012

Вам нужно установить индексы, которые вы хотели бы использовать.

http://www.mongodb.org/display/DOCS/Indexes

Раздел «Составные ключи» - это то, что вам нужно.

Если вы считаете, что ваши индексы установлены правильно, вы можете предоставить подсказки для вашего запроса.

http://www.mongodb.org/display/DOCS/Optimization#Optimization-Hint

...