#mongodb
Вопрос:
Есть парсер для занятий спортом. Это работает по кругу. По какой-то логике спортивные события добавляются в базу данных. В каждом цикле анализа спортивное событие может быть добавлено в базу данных, но может и не быть добавлено)
Мне нужно получить все спортивные события за последние два цикла анализа. Но, если и есть спортивное событие в обоих циклах, то только с последнего. В этом-то и проблема. Образцы документов:
{
"command1": "A",
"command2": "B",
"parseCount": 0
},
{
"command1": "A",
"command2": "B",
"parseCount": 1
},
{
"command1": "A",
"command2": "B",
"parseCount": 2
},
{
"command1": "C",
"command2": "D",
"parseCount": 1
},
{
"command1": "E",
"command2": "F",
"parseCount": 2
},
В результате я должен получить последние 3 документа из приведенного выше списка. В документе также есть поля: время совпадения и идентификатор объекта
Ответ №1:
Если я правильно понял, вы можете сначала $sort
, а затем $first
ввести объект $group
вот так:
Я использовал $first: $$ROOT
, но вы можете использовать $first: value
для каждого значения объекта, если хотите.
Этот запрос:
- Сначала отсортируйте по
parseCount
, чтобы получить более высокое значение на первой позиции. - Затем
$group
по двум условиям получаем первый объект (как заказано, тот, что выше) - И используйте
$project
для получения выходных значений, которые вы хотите.
db.collection.aggregate([
{
"$sort": {
"parseCount": -1
}
},
{
"$group": {
"_id": {
"command1": "$command1",
"command2": "$command2"
},
"object": {
"$first": "$ROOT"
}
}
},
{
"$project": {
"_id": "$object._id",
"command1": "$object.command1",
"command2": "$object.command2",
"parseCount": "$object.parseCount"
}
}
])
Пример здесь
Ответ №2:
Запрос
- это похоже на 2 запроса, но может стать 1 с помощью поиска
- фасет можно использовать и создавать 2 группы, но он будет ограничен максимальным объемом данных 16 МБ при сборе, приведенное ниже решение не имеет этого ограничения
- находит максимальный синтаксический анализ для коллекции с помощью поиска (mongodb автоматически оптимизирует его, поэтому конвейер в поиске будет выполняться только 1 раз не для каждого документа коллекции, по крайней мере, это произошло, когда я тестировал его в прошлом)
- мы сохраняем только последние 2 анализа, например , если
max=3
мы сохраняемparseCount=3
иparseCount=2
, мы также сохраняем только parseCount>0, у вас был этот фильтр на игровой площадке, если он вам не нужен, удалите его. - группируйтесь
command1,command2
и сохраняйте только тот максимумparseCount
, который вы сказали, что мы сохраняем только последнюю версию, если у нас больше 1 - проект по восстановлению структуры документа,
matchTime
и_id
сохраняются также потому, что вы сказали, что они у вас тоже есть
db.collection.aggregate([
{
"$lookup": {
"from": "collection",
"pipeline": [
{
"$group": {
"_id": null,
"maxParse": {
"$max": "$parseCount"
}
}
}
],
"as": "result"
}
},
{
"$set": {
"maxParses": {
"$let": {
"vars": {
"v0": {
"$arrayElemAt": [
"$result",
0
]
}
},
"in": "$v0.maxParse"
}
}
}
},
{
"$unset": [
"result"
]
},
{
"$match": {
"$expr": {
"$and": [
{
"$gt": [
"$parseCount",
0
]
},
{
"$gte": [
"$parseCount",
{
"$subtract": [
"$maxParses",
1
]
}
]
}
]
}
}
},
{
"$group": {
"_id": {
"command1": "$command1",
"command2": "$command2"
},
"maxParseCount": {
"$max": {
"parseCount": "$parseCount",
"matchTime": "$matchTime",
"id": "$_id"
}
}
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$_id",
"$maxParseCount",
"$ROOT"
]
}
}
},
{
"$project": {
"command1": 1,
"command2": 1,
"parseCount": 1,
"matchTime": 1,
"_id": "$id"
}
}
])
Чтобы объяснить, что делает запрос на основе ваших данных
- найдет maxParse=2 (поиск делает это)
- первый документ будет отфильтрован, потому что сохраняется только 2,1 анализа, а в нем 0
- остальные будут сгруппированы по командам1,команда2 «A»,»B» имеет 2 документа (
_id=2,_id=3
), но_id=3
пройдет только потому, что у него максимальное количество парсеков
[
{
"_id": 1,
"command1": "A",
"command2": "B",
"parseCount": 0,
"matchTime": 1
},
{
"_id": 2,
"command1": "A",
"command2": "B",
"parseCount": 1,
"matchTime": 2
},
{
"_id": 3,
"command1": "A",
"command2": "B",
"parseCount": 2,
"matchTime": 3
},
{
"_id": 4,
"command1": "C",
"command2": "D",
"parseCount": 1,
"matchTime": 4
},
{
"_id": 5,
"command1": "E",
"command2": "F",
"parseCount": 2,
"matchTime": 5
}
]
Результаты
[
{
"_id": 3,
"command1": "A",
"command2": "B",
"matchTime": 3,
"parseCount": 2
},
{
"_id": 4,
"command1": "C",
"command2": "D",
"matchTime": 4,
"parseCount": 1
},
{
"_id": 5,
"command1": "E",
"command2": "F",
"matchTime": 5,
"parseCount": 2
}
]
Комментарии:
1. Спасибо. Запрос работает. Но это выглядит очень сложно по сравнению с другим ответом
2. вы сказали, что вам нужны только последние 2 цикла , см. Это latest_parse=10 и parseCount=5 проходов, когда должно пройти только 10,9. этот запрос сохраняет только 9,10 [см. Это](mongoplayground.net/p/gGi9ZugsU3i )Это делается первой частью запроса, группа аналогична ответу JF, но с максимумом (сортировка необходима, если нам нужно больше 1), если нам нужно только 1, макс быстрее, но сортировка также подходит. Если вам не нужна последняя версия 2 , используйте часть из группы и ниже или решение для сортировки
3. Да, вы правы. Но это исправимо, если на первом этапе нужно добавить такой запрос, как
{ $match: { parseCount: { $gte: 9 } } }
4. это не может исправить ситуацию, потому что мы не знаем максимальное количество парсеков перед запуском запроса. Если вам нужно, по крайней мере, решение с 1 запросом и решением с 2 запросами, вы можете сделать это с помощью этого, но , насколько я знаю, автоматически оптимизированное решение с 1 запросом работает нормально, НО если вы знаете max-parseCount перед запуском запроса, вы можете использовать после group max или sort group в качестве решения JF, оба варианта хороши
5. Да, я знаю Макса
parseCount
…. но для этого я делаю дополнительный запрос ) . У меня есть один документ, напримерstat
, который включает в себя текущиеparseCount