Как объединить две коллекции, сохранив документ с наивысшей отметкой времени в MongoDB

# #json #database #mongodb #go #import

Вопрос:

Я создаю клиент MongoDB для приложения Go, используя драйвер MongoDB Go. В частности, у меня есть две базы данных с одной коллекцией в каждой. Эти коллекции могут быть асинхронно изменены разными клиентами, поэтому мне нужно периодически синхронизировать их, сохраняя последний отредактированный документ среди документов с одним и тем же id полем

Две базы данных хранятся на разных хостах, поэтому мне нужно экспортировать коллекцию с одного хоста с помощью mongoexport и импортировать на другой хост с помощью mongoimport .

Я уже пробовал использовать mongoimport --collection=myColl --mode=merge , но это не соответствует моей цели, потому что просто переопределяет конфликтующие документы из myColl импортированных.

Моя идея состоит в том, чтобы импортировать json во временную коллекцию, но я не знаю, как сравнивать временные метки во время процесса агрегации/слияния.

Мои коллекции структурированы так, есть идеи?

Коллекция 1

 {"_id":"K1","value":"VAL1","timest":{"$date":"2021-09-26T09:05:09.942Z"}}
{"_id":"K2","value":"VAL2","timest":{"$date":"2021-09-26T09:05:10.234Z"}}
 

Коллекция 2

 {"_id":"K2","value":"VAL3","timest":{"$date":"2021-09-26T09:15:09.942Z"}}
{"_id":"K3","value":"VAL4","timest":{"$date":"2021-09-26T09:15:10.234Z"}}
 

Желаемое Поведение

Конфликт

     {"_id":"K2","value":"VAL2","timest":{"$date":"2021-09-26T09:05:10.234Z"}}
{"_id":"K2","value":"VAL3","timest":{"$date":"2021-09-26T09:15:09.942Z"}}[LATEST]
 

Выход

 {"_id":"K1","value":"VAL1","timest":{"$date":"2021-09-26T09:05:09.942Z"}}
{"_id":"K2","value":"VAL3","timest":{"$date":"2021-09-26T09:15:09.942Z"}}
{"_id":"K3","value":"VAL4","timest":{"$date":"2021-09-26T09:15:10.234Z"}}
 

Комментарии:

1. Монгоимпорт на самом деле не создан для этого. Я бы выбрал маршрут отправки всех данных с одного хоста на другой по HTTP-почте. Затем получатель может объединить данные с любой логикой, которую вы хотите, обновить свою базу данных и отправить объединенные данные обратно первому хосту, который затем может продолжить и обновить свои собственные данные.

Ответ №1:

Вы можете использовать $merge

Приведенное ниже объединение testdb1.coll testdb2.coll выполняется на основе того же _id и сохраняет документ с последней датой. Если _id не найдено, то документ вставляется.

Данные в

testdb1.coll

 [{"_id" "K2","value" "VAL3","timest" (date "2021-09-26T09:15:09.942Z")}  
 {"_id" "K3","value" "VAL4","timest" (date "2021-09-26T09:15:10.234Z")}]
 

testdb2.coll

 [{"_id" "K1","value" "VAL1","timest" (date "2021-09-26T09:05:09.942Z")}
{"_id" "K2","value" "VAL2","timest" (date "2021-09-26T09:05:10.234Z")}]
 

Результаты

testdb2.coll (после слияния)

 {"_id": "K1", "value": "VAL1", "timest": {"$toDate": "2021-09-26T09:05:09.942Z"}}
{"_id": "K2", "value": "VAL3", "timest": {"$toDate": "2021-09-26T09:15:09.942Z"}}
{"_id": "K3", "value": "VAL4", "timest": {"$toDate": "2021-09-26T09:15:10.234Z"}}
 

Запрос
(вместо $let того, чтобы вы могли бы использовать $$new )

 client.db("testdb1").collection("coll").aggregate(
[
    {
      "$merge": {
        "into": {
          "db": "testdb2",
          "coll": "coll"
        },
        "on": [
          "_id"
        ],
        "let": {
          "p_ROOT": "$ROOT"
        },
        "whenMatched": [
          {
            "$replaceRoot": {
              "newRoot": {
                "$cond": [
                  {
                    "$gt": [
                      "$p_ROOT.timest",
                      "$timest"
                    ]
                  },
                  "$p_ROOT",
                  "$ROOT"
                ]
              }
            }
          }
        ],
        "whenNotMatched": "insert"
      }
    }
  ])
 

Комментарии:

1. Я понял логику, которой я должен следовать, но, используя golang, я должен использовать эту функцию. pkg.go.dev/go.mongodb.org/mongo-driver@v1.7.2/…. У меня возникли некоторые проблемы с преобразованием вашего запроса в запрос, используемый этой библиотекой.

2. вложенные bson.D{{...}} могут это сделать взгляните на этот поиск агрегации в примерах go, было бы очень легко преобразовать его, но он немного увеличится, также для $merge чтения документов о том, как выводить данные в коллекции. В Java, например, мы должны aggregate().toCollection(); , если у нас есть $merge или $out как последний этап

Ответ №2:

Вы можете выполнить следующие действия в конвейере агрегации:

  1. используйте $unionWith для объединения 2 коллекций
  2. $sort чтобы заказать их по timest
  3. используйте $first для получения последнего документа
  4. используйте $replaceRoot , чтобы получить окончательную форму, которую вы хотите

Вот игровая площадка Монго для вашей справки.

Комментарии:

1.Спасибо за ваш ответ. Я понял логику, которой я должен следовать, но, используя golang, я должен использовать эту функцию. pkg.go.dev/go.mongodb.org/mongo-driver@v1.7.2/.… У меня возникли некоторые проблемы с преобразованием вашего синтаксиса в синтаксис, используемый в этой библиотеке, для которого требуется bson.D для запроса.

2. решение хорошее и простое, но оно не работает, если коллекции находятся в разных базах данных, а OP имеет их в разных базах данных.