@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 есть Хешированный индекс, но пока внятного описания не нашел.
  • Вопрос задан
  • 33 просмотра
Решения вопроса 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, что бы посмотреть статистику выполнения запроса.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы