После применения $setUnion в MongoDB не стоит рассчитывать на исходный порядок элементов. $setUnion нужен, чтобы объединить массивы и убрать повторяющиеся значения. Если после этого нужен стабильный порядок, добавьте отдельную сортировку. В современных версиях MongoDB для массива удобно использовать $sortArray. В старых версиях можно развернуть массив через $unwind, отсортировать документы и собрать массив обратно
Пример документа:
{
tagsA: ["css", "mongodb", "nodejs"],
tagsB: ["python", "mongodb", "api"]
}
Объединение:
db.posts.aggregate([
{
$project: {
tags: { $setUnion: ["$tagsA", "$tagsB"] }
}
}
])
Результат содержит уникальные значения, но порядок лучше явно не считать гарантированным
- Сортировка через $sortArray
- Если массив состоит из объектов
- Вариант через $unwind
- Когда сортировать до $setUnion не поможет
- Как проверить
- Частые ошибки
- Ждут порядок от $setUnion
- Сортируют документы, а не массив
- Забывают про версию MongoDB
- Сортируют строки как числа
- Что почитать дальше по MongoDB
- Как проверить результат на практике
Сортировка через $sortArray
Если ваша версия MongoDB поддерживает $sortArray, используйте такой pipeline:
db.posts.aggregate([
{
$project: {
tags: {
$sortArray: {
input: { $setUnion: ["$tagsA", "$tagsB"] },
sortBy: 1
}
}
}
}
])
sortBy: 1 сортирует по возрастанию, sortBy: -1 — по убыванию
Если массив состоит из объектов
Допустим, массив содержит объекты:
items: [
{ code: "b", title: "Beta" },
{ code: "a", title: "Alpha" }
]
Тогда сортировка по полю:
{
$sortArray: {
input: "$items",
sortBy: { code: 1 }
}
}
Так вы сортируете элементы массива по code
Вариант через $unwind
Если $sortArray недоступен, можно сделать так:
db.posts.aggregate([
{
$project: {
tags: { $setUnion: ["$tagsA", "$tagsB"] }
}
},
{ $unwind: "$tags" },
{ $sort: { tags: 1 } },
{
$group: {
_id: "$_id",
tags: { $push: "$tags" }
}
}
])
Этот способ длиннее, но понятен: сначала объединяем, потом превращаем элементы массива в отдельные документы, сортируем и собираем обратно
Когда сортировать до $setUnion не поможет
Иногда пытаются отсортировать исходные массивы до объединения. Это не решает задачу надежно, потому что $setUnion заново формирует массив уникальных значений. Поэтому логика должна быть такой: сначала получить уникальное объединение, потом применить сортировку к уже готовому результату
Если результат нужен только для вывода на странице, сортировку можно сделать и в приложении. Но если дальше в pipeline есть стадии, которые зависят от порядка, лучше сортировать прямо в aggregation
Как проверить
Создайте тестовый документ с перемешанными значениями и выполните pipeline. Результат должен быть предсказуемым: например ["api", "css", "mongodb", "nodejs", "python"]
Частые ошибки
Ждут порядок от $setUnion
$setUnion решает задачу уникального объединения, а не сортировки. Сортировку добавляйте отдельно
Сортируют документы, а не массив
$sort сортирует документы pipeline. Для сортировки элементов массива используйте $sortArray или схему с $unwind
Забывают про версию MongoDB
Если $sortArray не работает, проверьте версию сервера и используйте вариант через $unwind
Сортируют строки как числа
Если в массиве лежат строки "10" и "2", строковая сортировка может дать неожиданный порядок. Для числовой сортировки храните числа числами
Что почитать дальше по MongoDB
Если нужен общий маршрут по теме, откройте рубрику MongoDB. Для соседних задач пригодятся эти разборы:
- Как сделать уникальный номер документа в MongoDB
- Как сделать, чтобы ActiveRecord MongoDB отдавал модели, а не массив
- Ошибка создания документа в MongoDB после генерации _id: как исправить
- Discord bot на Python и MongoDB: как задать проверку
Как проверить результат на практике
Для MongoDB-материала полезно делать три проверки: сначала выполнить команду на маленьком наборе тестовых документов, затем посмотреть результат через find() или MongoDB Compass, а после этого повторить действие на копии реальной структуры данных. Если запрос меняет документы, сначала запускайте его с фильтром на один документ и только потом расширяйте условие
Еще одна хорошая привычка — записывать исходное состояние и ожидаемый результат. Например: было три документа, после обновления изменился только один; был массив из пяти элементов, после операции остался нужный элемент; индекс появился в getIndexes(). Такая проверка быстро показывает, что вы исправили именно задачу, а не просто получили команду без ошибки



