【问题标题】:Remove Duplicates from Array | Mongo DB $setUnion follows order which is wrong as per documentation从数组中删除重复项 | Mongo DB $setUnion 遵循文档中错误的顺序
【发布时间】:2020-04-22 17:48:58
【问题描述】:

在下面的查询中,您可以看到状态数组中元素的顺序,这是我在文档中的实际顺序。

查询:

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 是按元素中的第一个属性“order”排序,然后按第二个属性“created_on”排序,然后它可能会按最后一个属性“name”排序状态数组中的每个元素。

此行为与文档 https://docs.mongodb.com/manual/reference/operator/aggregation/setUnion/ 中提到的内容相悖

这个命令对我很有用,我应该相信它吗?

我正在处理的场景:

"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")
        }
    ],

我的集合中有超过 50k 的文档,具有上述给定的数组类型属性。问题是,我如何按名称重复状态,如您所见,Pending 出现了两次,Sent Initial Viewed 和 Viewed 相同。

要求的结果:

我必须更新 statuses 数组中所有具有重复状态名称的文档,以删除所有重复条目。首先出现的任何状态都应该保留,所有其他重复的状态都应该被删除。

有没有简单的方法通过 Mongo 本机更新查询(不是 javascript)来做到这一点?我的首要任务是如何匹配这些名称重复条目的记录?

【问题讨论】:

  • should I trust it? 不,这就是为什么文档说它是unspecified

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

解决方案 (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

解决方法 (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?

没有

MongoDB 对 BSON 对象的比较使用以下顺序:
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 对顺序。
  • 不要使用 pymongo,因为 Python dict(在 Python 3.7 之前)和 JSON 对象是 unordered collections

【讨论】:

  • 是的,在我的情况下,键值对顺序始终相同。我不能使用 unwind,因为我必须在更新查询中使用管道。更新不允许放松。
  • @AbdulMoiz 你需要更新整个收藏吗?
  • 我只是更新了我正在处理的问题中的场景。请检查一下。
  • @AbdulMoiz :-) 很高兴为您提供帮助
猜你喜欢
  • 2021-11-15
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-06
  • 1970-01-01
  • 2019-06-06
  • 2016-10-01
相关资源
最近更新 更多