#arrays #mongodb #sorting #custom-sort
#массивы #mongodb #сортировка #пользовательская сортировка
Вопрос:
Я пытаюсь получить каждую последнюю запись «группы» из Mongo.
Вы можете отображать данные как набор записей вики:
- Каждую запись можно редактировать (и старые версии сохраняются).
- Нас интересует последняя версия записей.
- Записи (и их предки) сортируются по определенной логике.
Чтобы получить более подробную информацию:
- Каждая запись идентифицируется с помощью
itemId
- Каждый
itemId
из них может встречаться в наборе данных от 1 до n раз - Версии записей сортируются по
orderHash
Здесь вы можете увидеть небольшой пакет данных:
{
"_id": ObjectId("5f69b963b8705200282d6174"),
"itemId": "xszy",
"orderHash": "383u",
"title": "A",
"content": "ABC"
},
{
"_id": ObjectId("5f69b963b8705200282d6175"),
"itemId": "e92q",
"orderHash": "5j12",
"title": "K",
"content": "KLMN"
},
{
"_id": ObjectId("5f69b963b8705200282d6178"),
"itemId": "xszy",
"orderHash": "p578",
"title": "A",
"content": "ABD"
},
{
"_id": ObjectId("5f69b963b8705200282d6180"),
"itemId": "mtewy",
"orderHash": "383u",
"title": "L",
"content": "CASE"
},
{
"_id": ObjectId("5f69b963b8705200282d6189"),
"itemId": "mtewy",
"orderHash": "5j12",
"title": "L1",
"content": "CASE"
}
Я использую следующий агрегированный конвейер для получения желаемого результата:
db.getCollection('wiki').aggregate([
{
// create a sortable field for mongo
$set: {
"sortField": {
"$indexOfArray": [
[ "p578", "5j12", "383u" ], "$orderHash"
]
}
}
},
{
// sort by created sort field
"$sort": {
"sortField": 1
}
},
{
// group items by itemId and "save" them sorted in an array
$group: {
_id: "$itemId",
data: {
$push: "$$ROOT"
}
}
},
{
// get first entry of each "group array" to obtain
// the latest version of each entry
$project: {
resp: { $arrayElemAt: ['$data', 0] }
}
}
])
Пока это работает. Но кажется неправильным хранить данные в дополнительном массиве, и я не уверен в производительности (особенно для больших наборов данных с большим количеством изменений). Еще один недостаток заключается в том, что я не просто получаю массив документов обратно, но данные вложены в свойство documents resp
(которое не подходит для использования, например, для mongoose).
Есть ли более простое решение для моей проблемы?
РЕШЕНИЕ:
Спасибо Джо (см. Комментарии). Он указал мне на решение.
db.getCollection('wiki').aggregate([
{
$set: {
"sortField": {
"$indexOfArray": [
[ "p578", "5j12", "383u" ], "$orderHash"
]
}
}
},
{
"$sort": {
"sortField": 1
}
},
{
$group: {
_id: "$itemId",
resp: {
$first: "$$ROOT"
}
}
},
{
$replaceRoot: {
newRoot: "$resp"
}
}
])
Комментарии:
1. Будет ли использовать
$first
вместо$push
того, чтобы получить то, что вы хотите?2. Да! Это так. Спасибо. Это также экономит один шаг конвейера. Тогда остается только одна проблема.
3. возможно
$replaceRoot
, этап?4. Опять же: да! Спасибо!