Удалить дубликаты из массива | Mon go DB $ setUnion следует за порядком, который является неправильным согласно документации - PullRequest
1 голос
/ 22 апреля 2020

В приведенном ниже запросе вы можете видеть порядок элементов в массиве состояний, это фактический порядок, который у меня есть в моем документе.

Запрос:

db.order_test.aggregate([]);

Результат:

{
    "_id" : ObjectId("5ea0805cb0b44d2784a70f90"),
    "statuses" : [
        {
            "order" : 3,
            "created_on" : ISODate("2019-11-25T18:44:48.930Z"),
            "name" : "In Progress"
        },
        {
            "order" : 2,
            "created_on" : ISODate("2019-11-25T18:44:55.104Z"),
            "name" : "Pending"
        },
        {
            "order" : 2,
            "created_on" : ISODate("2019-11-25T18:45:09.022Z"),
            "name" : "Sent"
        },
        {
            "order" : 1,
            "created_on" : ISODate("2019-11-25T20:04:49.347Z"),
            "name" : "Initial Viewed"
        },
        {
            "order" : 6,
            "created_on" : ISODate("2019-11-25T20:04:49.347Z"),
            "name" : "Viewed"
        },
        {
            "order" : 4,
            "created_on" : ISODate("2019-11-25T20:04:49.347Z"),
            "name" : "Opened"
        },
        {
            "order" : 2,
            "created_on" : ISODate("2019-12-15T05:59:04.719Z"),
            "name" : "Abandoned"
        }
    ]
}

Теперь после применения $ setUnion

Запрос:

db.order_test.aggregate([
    {
        $addFields: {
            statuses: {$setUnion: ['$statuses']}
        }
    }    
]);

Результат:

{
    "_id" : ObjectId("5ea0805cb0b44d2784a70f90"),
    "statuses" : [
        {
            "order" : 1,
            "created_on" : ISODate("2019-11-25T20:04:49.347Z"),
            "name" : "Initial Viewed"
        },
        {
            "order" : 2,
            "created_on" : ISODate("2019-11-25T18:44:55.104Z"),
            "name" : "Pending"
        },
        {
            "order" : 2,
            "created_on" : ISODate("2019-11-25T18:45:09.022Z"),
            "name" : "Sent"
        },
        {
            "order" : 2,
            "created_on" : ISODate("2019-12-15T05:59:04.719Z"),
            "name" : "Abandoned"
        },
        {
            "order" : 3,
            "created_on" : ISODate("2019-11-25T18:44:48.930Z"),
            "name" : "In Progress"
        },
        {
            "order" : 4,
            "created_on" : ISODate("2019-11-25T20:04:49.347Z"),
            "name" : "Opened"
        },
        {
            "order" : 6,
            "created_on" : ISODate("2019-11-25T20:04:49.347Z"),
            "name" : "Viewed"
        }
    ]
}

Как ясно видно, что $ setUnion упорядочивается по первому свойству в элементах, которое является «порядком», а затем по второму свойству, которое «создано_он», а затем оно вероятно, будет использовать имя, которое является последним свойством каждого элемента в массиве состояний.

Это поведение противоречит тому, что упомянуто в документации https://docs.mongodb.com/manual/reference/operator/aggregation/setUnion/

enter image description here

Этот заказ очень полезен для меня, могу ли я доверять ему?

Сценарий, над которым я работаю:

"statuses" : [
        {
            "name" : "In Progress",
            "created_on" : ISODate("2019-11-25T18:44:50.302Z")
        },
        {
            "name" : "Pending",
            "created_on" : ISODate("2019-11-25T18:44:55.104Z")
        },
        {
            "name" : "Sent",
            "created_on" : ISODate("2019-11-25T18:45:19.871Z")
        },
        {
            "name" : "Initial Viewed",
            "created_on" : ISODate("2019-11-25T20:08:42.299Z")
        },
        {
            "name" : "Viewed",
            "created_on" : ISODate("2019-11-25T20:10:04.016Z")
        },
        {
            "name" : "Pending",
            "created_on" : ISODate("2019-11-25T20:49:56.008Z")
        },
        {
            "name" : "Sent",
            "created_on" : ISODate("2019-11-26T02:30:17.701Z")
        },
        {
            "name" : "Initial Viewed",
            "created_on" : ISODate("2019-11-26T02:30:17.701Z")
        },
        {
            "name" : "Viewed",
            "created_on" : ISODate("2019-11-26T02:30:17.701Z")
        },
        {
            "name" : "Opened",
            "created_on" : ISODate("2019-11-26T02:30:17.701Z")
        },
        {
            "name" : "Completed",
            "created_on" : ISODate("2019-11-26T02:33:56.484Z")
        }
    ],

У меня есть более 50 тыс. документов в моей коллекции с указанным выше свойством типа array. Проблема в том, что у меня есть дублирование статуса по имени, как вы можете видеть, «Ожидание появляется дважды», то же самое относится и к «Отправлено при первоначальном просмотре и просмотре».

Требуемый результат:

Мне нужно обновить все документы, имеющие дублированные имена статусов в массиве статусов, для удаления всех дубликатов записей. Любое состояние, которое появилось первым, должно остаться, а все остальные дубликаты должны быть удалены.

Есть ли какой-нибудь простой способ сделать это через Mon go собственный запрос на обновление (не javascript)? Мой первый приоритет - как мне сопоставить эти записи, которые имеют повторяющиеся записи по имени?

1 Ответ

1 голос
/ 22 апреля 2020

Soution (v4.2) :

db.order_test.update({},
  [
    {$set:{
      statuses: {
        $reduce: {
          input: "$statuses",
          initialValue: [],
          in: {
            $concatArrays: [
              "$$value",
              {
                $cond: [
                  {
                    $eq: [
                      {
                        $size: {
                          $filter: {
                            input: "$$value",
                            as: "uniq",
                            cond: {
                              $eq: [
                                "$$uniq.name",
                                "$$this.name"
                              ]
                            }
                          }
                        }
                      },
                      0
                    ]
                  },
                  [
                    "$$this"
                  ],
                  []
                ]
              }
            ]
          }
        }
      }
    }}
  ]
  {multi:true}
)

MongoPlayground

Обходной путь (<<code>v4.2):

db.order_test.aggregate([
  {
    $addFields: {
      statuses: {
        $reduce: {
          input: "$statuses",
          initialValue: [],
          in: {
            $concatArrays: [
              "$$value",
              {
                $cond: [
                  {
                    $eq: [
                      {
                        $size: {
                          $filter: {
                            input: "$$value",
                            as: "uniq",
                            cond: {
                              $eq: [
                                "$$uniq.name",
                                "$$this.name"
                              ]
                            }
                          }
                        }
                      },
                      0
                    ]
                  },
                  [
                    "$$this"
                  ],
                  []
                ]
              }
            ]
          }
        }
      }
    }
  }
  //,{$out:"order_test"}
])

MongoPlayground

Примечание: Раскомментировать $out оператор для переопределения order_test коллекции с результатом агрегации.


This ordering is very useful for me, should I trust it?

Нет

Для сравнения объектов BSON в MongoDB используется следующий порядок: 1. Рекурсивно сравнивать пары ключ-значение в порядке их появления в объекте BSON. ... https://docs.mongodb.com/manual/reference/bson-type-comparison-order/#objects

Это означает:

{                                          {
  "created_on" : ISODate("2019-11-25"),  ≠   "name" : "In Progress",
  "name" : "In Progress"                 ≠   "created_on" : ISODate("2019-11-25"),
}                                          }

"Да"

  • , если вы убедитесь, что все statuses имеют одинаковый key-value порядок пар.
  • Не используйте pymon go, поскольку Python dict (до Python 3.7) и JSON объект неупорядоченные коллекции
...