@SmAl27

Как указать несколько пар условий в запросе по списку?

Есть документы, такого типа
{"username" : "user1", "tags" : ["tag1", "tag2", "tag3", "tag4"]}
{"username" : "user2", "tags" : ["tag3", "tag2", "tag5", "tag6"]}
{"username" : "user1", "tags" : ["tag4", "tag5", "tag2", "tag1"]}
{"username" : "user2", "tags" : ["tag1", "tag2", "tag3", "tag4"]}
{"username" : "user3", "tags" : ["tag3", "tag7", "tag8", "tag1"]}
{"username" : "user4", "tags" : ["tag8", "tag5", "tag3", "tag2"]}
{"username" : "user1", "tags" : ["tag9", "tag6", "tag4", "tag5"]}
{"username" : "user2", "tags" : ["tag1", "tag2", "tag7", "tag8"]}

Мне нужно в один запрос засунуть несколько пар условий:
username с именем user1 и с тегом tag4
И
username с именем user2 и с тегом tag1
И
username с именем user2 и с тегом tag3

Сейчас делаю через $or
$or: [
["username" : "user1", tags: "tag4"],
["username" : "user2", tags: "tag1"],
["username" : "user2", tags: "tag3"],
]

Все работает
Но притормаживает)
Бывает так, что бывает много пар условий.
Самих документов 5 000 000

Какой индекс нужно делать? Отдельно по каждому полю? Или составной "username + tags"

Или как сделать более правильно?
Завести отдельную таблицу с "хешем" типа username.tags и ссылочным полем на этот документ?
В самих документах сделать новый поле массив с этим "хешем"?
Слышал что в mongo есть Хешированный индекс, но пока внятного описания не нашел.
  • Вопрос задан
  • 44 просмотра
Решения вопроса 1
@Asapin
В свободное время ковыряюсь с Rust и Wasm
Про хешированные индексы
Судя по документации, MongoDB не поддерживает хешированные индексы для массивов.
Хешированные индексы нужны для того, что бы более равномерно распределить данные, которые расположены близко к друг другу. Их часто применяют при шардировании коллекций (когда одна коллекция разбита на несколько частей, которые хранятся на разных серверах). В таком случае, запросы на выбор этих данных не будут идти всегда на один и тот же сервер. Но если у вас запросы вида поле1 > значение1 && поле1 < значение2, то индекс использоваться не будет (потому что в таком случае монге надо будет сгенерировать все возможные промежуточные значения и посчитать для них хеши, что невозможно).

Но судя по документации, хешированные индексы также можно применять и для embedded-документов (сам так не делал), но подозреваю, что в таком случае в запросе надо передавать весь embedded-документ, что бы для него правильно посчитался хеш.

Про индексы в целом
В целом, задача индекса в том, что бы после первичного поиска по нему осталось как можно меньше документов для последующего перебора, поэтому рекомендуется строить индекс по полям, которые у вас гарантированно участвуют в каждом запросе.
Скажем, у вас в коллекции проиндексированно только поле username и вы выполняете свой запрос вида
$or: [
["username" : "user1", tags: "tag4"],
["username" : "user2", tags: "tag1"],
["username" : "user2", tags: "tag3"],
]

в таком случае, MongoDB с помощью индекса очень быстро выберет все документы, у которых username равен либо user1, либо user2, а затем уже начнёт перебирать найденные документы и проверять их на совпадение остальным параметрам запроса.

Получается, лучше делать составной индекс username + tags?
Не всегда. При использовании в индексе поля типа Array, на каждый элемент массива будет создана отдельная запись в индексе.
Если у вас мало документов, у которых поле username имеет одинаковое значение, но при этом в массиве tags может быть много значений, и значения внутри одного массива могут повторяться, то использование составного индекса приведёт к большему количеству одинаковых записей в индексе, чем если бы вы использовали индексацию только по полю username.

Отсюда же вытекает другое ограничение: в составном индексе только одно из полей может быть массивом, иначе в индексе на каждый документ пришлось бы создавать N ・ M записей, где N и M - размеры массивов, которые участвуют в построении индекса.

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

Отдельная коллекция с хешами
Транзакционность в MongoDB есть только на уровне отдельного документа. А это значит, что вам придётся самому следить за консистентностью данных в обеих коллекциях, что может обернуться той ещё головной болью: что вы будете делать, если документ был отредактирован, но информация в связанной коллекции по каким-то причинам не была обновлена (например, прервалась связь с БД или приложение упало из-за нехватки памяти)? Поэтому лично я в общем случае такой подход не рекомендовал бы.

Так что же делать?
Самое простое и эффективное, что вы можете сделать - поднять монгу локально, сгенерировать в ней реалистичные данные, поиграться с разными настройками индексов и замерить скорость выполнения запросов.
Дополнительно можете воспользоваться методом explain, что бы посмотреть статистику выполнения запроса.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы