Ну, как автор ответа, который вы нашли, я думаю, что на самом деле мы можем добиться большего успеха с современными версиями MongoDB.
Одно совпадение на документ
Короче говоря, мы можем применить $max
к вашему конкретному случаю, используемому с $indexOfArray
и $arrayElemAt
для извлечения соответствующего значения:
db.collection.aggregate([
{ "$addFields": {
"history": {
"$arrayElemAt": [
"$history",
{ "$indexOfArray": [ "$history.timestamp", { "$max": "$history.timestamp" } ] }
]
}
}}
])
Который вернет вам:
{
"_id" : ObjectId("5ae9175564de8a00a66b3974"),
"location" : "new york",
"history" : {
"timestamp" : 1524560400,
"temp" : 78,
"wind_speed" : 4,
"wind_direction" : "S"
}
}
{
"_id" : ObjectId("5ae9175564de8a00a66b3975"),
"location" : "san francisco",
"history" : {
"timestamp" : 1524560400,
"temp" : 73,
"wind_speed" : 8,
"wind_direction" : "S"
}
}
{
"_id" : ObjectId("5ae9175564de8a00a66b3976"),
"location" : "miami",
"history" : {
"timestamp" : 1524560400,
"temp" : 87,
"wind_speed" : 12,
"wind_direction" : "S"
}
}
Это, разумеется, без необходимости «группировать» что-либо и просто найти значение $max
в каждом документе, как вы, похоже, пытаетесь это сделать. Это избавляет от необходимости «манипулировать» любым другим документом, выдавая его через $group
или действительно $unwind
.
По сути, использование заключается в том, что $max
возвращает «максимальное» значение из указанного свойства массива, поскольку $history.timestamp
- это короткий способ извлечения «только этих значений» из объектов массива.
Используется по сравнению с тем же «списком значений», чтобы определить соответствующий «индекс» с помощью $indexOfArray
, который принимает массив в качестве первого аргумента и значение для сопоставления в качестве второго .
Оператор $arrayElemAt
также принимает массив в качестве первого аргумента, здесь мы используем полный массив "$history"
, так как мы хотим извлечь «полный объект». Что мы делаем с помощью значения «возвращаемого индекса» оператора $indexOfArray
.
«Несколько» совпадений на документ
Конечно, это хорошо для «одиночных» совпадений, но если вы хотите расширить это значение до «множественных» совпадений с одинаковым значением $max
, то вы должны использовать $filter
вместо:
db.collection.aggregate([
{ "$addFields": {
"history": {
"$filter": {
"input": "$history",
"cond": { "$eq": [ "$$this.timestamp", { "$max": "$history.timestamp" } ] }
}
}
}}
])
Что бы вывести:
{
"_id" : ObjectId("5ae9175564de8a00a66b3974"),
"location" : "new york",
"history" : [
{
"timestamp" : 1524560400,
"temp" : 78,
"wind_speed" : 4,
"wind_direction" : "S"
}
]
}
{
"_id" : ObjectId("5ae9175564de8a00a66b3975"),
"location" : "san francisco",
"history" : [
{
"timestamp" : 1524560400,
"temp" : 73,
"wind_speed" : 8,
"wind_direction" : "S"
}
]
}
{
"_id" : ObjectId("5ae9175564de8a00a66b3976"),
"location" : "miami",
"history" : [
{
"timestamp" : 1524560400,
"temp" : 87,
"wind_speed" : 12,
"wind_direction" : "S"
}
]
}
Основное различие заключается в том, что свойство "history"
по-прежнему является "массивом", поскольку именно это будет генерировать $filter
. Также, конечно, отметив, что если бы на самом деле существовали «множественные» записи с одинаковым значением метки времени, то это, конечно, вернуло бы их всех, а не только «первый индекс».
Сравнение в основном выполняется вместо каждого элемента массива, чтобы определить, имеет ли объект current ("$$this"
) указанное свойство, соответствующее результату $max
, и в конечном итоге возвращает только те элементы массива, которые соответствуют заданному условию.
Это, по сути, ваши "современные" подходы, позволяющие избежать издержек $unwind
и даже $sort
и $group
там, где они могут не нужно Конечно, они не нужны только для работы с отдельными документами.
Если, однако, вам действительно нужно $group
по «нескольким документам» по определенному ключу группировки и учету значений «внутри» массива, то первоначальный подход, обрисованный в общих чертах, как вы обнаружили, на самом деле подходит для этого сценария, как и в конечном итоге, вы «должны» $unwind
, чтобы обрабатывать элементы «внутри» массива таким образом. А также с учетом «по всем документам».
Так что будьте внимательны, используя такие этапы, как $group
и $unwind
только там, где вам действительно нужно и где «группировка» - это ваше реальное намерение , Если вы просто ищете что-то «в документе», то есть гораздо более эффективные способы сделать это без дополнительных затрат, которые эти этапы приносят с собой на обработку.