【问题标题】:Update Array Children Sorted Order更新数组子排序顺序
【发布时间】:2018-04-22 12:51:23
【问题描述】:

我有一个包含具有以下结构的对象的集合

{
"dep_id": "some_id",
"departament": "dep name",
"employees": [{
   "name": "emp1",
   "age": 31
  },{
   "name": "emp2",
   "age": 35
  }]
}

我想按employees.age 降序对ID 为“some_id”的对象的员工数组进行排序和保存。最好的结果是使用 mongodb 的查询语言以原子方式执行此操作。这可能吗?

如果没有,如何在不影响父级其他数据或子文档数据的情况下重新排列子文档?如果我必须从数据库下载数据并保存回排序的子数组,如果其他东西对其中一个子执行更新或同时添加或删除子会发生什么?

最后,数据应该这样持久化到数据库中:

{
"dep_id": "some_id",
"departament": "dep name",
"employees": [{
   "name": "emp2",
   "age": 35
  },{
   "name": "emp1",
   "age": 31
  }]
}

【问题讨论】:

  • 一开始为什么要这样做?
  • @dnickless,我想这样做是为了优化我的一种查询模式。我的实际对象有一个“等级”和“created_at”,我希望第一个孩子总是具有最高等级的那个,如果多个项目具有最高等级,那么数组中的第一个项目应该是最新鲜的最高等级。我可以通过查询来做到这一点,但我必须先 $unwind 然后排序,如果这样做,我可能会遇到 mongodb 的限制,当您尝试对大量数据进行排序并且排序不是聚合的第一步时会出现这种限制。

标签: mongodb mongodb-query


【解决方案1】:

为了得到你想要的,你可以使用以下查询:

db.collection.aggregate({
    $unwind: "$employees" // flatten employees array
}, {
    $sort: {
        "employees.name": -1 // sort all documents by employee name (descending)
    }
}, {
    $group: { // restore the previous structure
        _id: "$_id",
        "dep_id": {
            $first: "$dep_id"
        },
        "departament": {
            $first: "$departament"
        },
        "employees": {
            $push: "$employees"
        },
    }
}, {
    $out: "output" // write everything out to a separate collection
})

在此步骤之后,您可能希望删除源表并重命名“输出”集合以匹配源表名称。

但是,此解决方案不会处理并发问题。因此,您应该首先从集合中删除写访问权限,这样在此过程中没有人修改它,然后在您完成迁移后恢复它。

您也可以先查询所有数据,然后在客户端对 employees 数组进行排序,然后使用单个更新查询或 - 更快但更复杂 - 带有所有单独更新调用的 bulk write operation 以进行更新现有的文件。在这里,您可以将最初阅读的整个文档用作更新操作的过滤器。因此,如果单个更新没有修改您立即知道的任何文档,那么其他一些更改一定修改了您之前阅读的文档。这些情况您需要稍后重试(或在更新确实修改文档之前立即重试)。

【讨论】:

  • 所以这个过程是完全没有必要的,而且 MongoDB 多年来一直有办法将数组内容“就地”更新为新的排序顺序。所以你不需要写入一个新的集合,或者循环转换结果。
【解决方案2】:

最好的方法是在向数组添加项目时实际应用$sort 修饰符。正如您在评论中所说“我的实际对象有一个“等级”和“created_at””,这意味着您真的应该在您的问题中提出这个问题,而不是写一个“人为的”案例(不要'不知道人们为什么这样做)。

因此,对于按多个属性“排序”,以下参考将调整如下:

db.collection.update(
  {  },
  { "$push": { "employees": { "$each": [], "$sort": { "rank": -1, "created_at": -1 } } } },
  { "multi": true }
)

但是要更新您目前拥有的所有数据“如问题所示”,那么您将使用以下命令对"age" 进行排序:

db.collection.update(
  {  },
  { "$push": { "employees": { "$each": [], "$sort": { "age": -1 } } } },
  { "multi": true }
)

哪个奇怪地使用$push 来实际“修改”一个数组?是的,这是真的,因为 $each 修饰符表示我们实际上并没有添加任何新内容,但 $sort 修饰符实际上将应用于数组并“重新排序”它。

当然,这将解释如何将“新”更新写入数组以应用 $sort 并确保“最大年龄”始终是数组中的“第一个”:

db.collection.update(
  { "dep_id": "some_id" },
  { "$push": { 
    "employees": { 
      "$each": [{ "name": "emp": 3, "age": 32 }],
      "$sort": { "age": -1 } 
    }
  }}
)

因此,当您在更新时将新条目添加到数组时,会应用 $sort 修饰符并在两个现有元素之间重新定位新元素,因为这是排序的位置。

p>

这是 MongoDB 的常见模式,通常与 $slice 修饰符结合使用,以便在添加新项目时保持数组的“最大”长度,同时保留“有序”的结果。很多时候,“排名”就是确切的用法。

总的来说,您实际上可以“更新”现有数据并使用“一个简单的原子语句”对其进行重新排序。无需循环或集合重命名。此外,您现在有一个简单的原子方法来“更新”数据并在添加或删除新数组项时保持该顺序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-29
    • 2018-02-18
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    • 2012-02-14
    • 2017-03-22
    • 2014-09-09
    相关资源
    最近更新 更多