Получите все документы, а в случае равенства (сходства) — более поздние

#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 документа из приведенного выше списка. В документе также есть поля: время совпадения и идентификатор объекта

https://mongoplayground.net/p/-9gz4zOnudW

Ответ №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