Mongodb 按排序顺序更新数组子级

Mongodb 按排序顺序更新数组子级,mongodb,mongodb-query,Mongodb,Mongodb Query,我有一个包含具有以下结构的对象的集合 { “dep_id”:“some_id”, “部门”:“部门名称”, “雇员”:[{ “名称”:“emp1”, “年龄”:31 },{ “名称”:“emp2”, “年龄”:35 }] } 我想为id为“some_id”的对象按employees.age降序排序并保存雇员数组。最好的结果是使用mongodb的查询语言以原子方式实现这一点。这可能吗 如果没有,如何重新排列子文档而不影响父级的其他数据或子文档的数据?如果我必须从数据库下载数据并保存已排序的子数组

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

{
“dep_id”:“some_id”,
“部门”:“部门名称”,
“雇员”:[{
“名称”:“emp1”,
“年龄”:31
},{
“名称”:“emp2”,
“年龄”:35
}]
}
我想为id为“some_id”的对象按employees.age降序排序并保存雇员数组。最好的结果是使用mongodb的查询语言以原子方式实现这一点。这可能吗

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

最后,应将数据保存到数据库,如下所示:

db.collection.update(
  {  },
  { "$push": { "employees": { "$each": [], "$sort": { "rank": -1, "created_at": -1 } } } },
  { "multi": true }
)
{
“dep_id”:“some_id”,
“部门”:“部门名称”,
“雇员”:[{
“名称”:“emp2”,
“年龄”:35
},{
“名称”:“emp1”,
“年龄”:31
}]
}

要获取所需内容,可以使用以下查询:

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
})
在此步骤之后,您可能希望删除源表并重命名“output”集合以匹配源表名称

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


您也可以先查询所有数据,然后对客户端上的
employees
数组进行排序,然后使用单个更新查询或(更快但更复杂的)与所有单个更新调用一起使用a来更新现有文档。在这里,您可以使用最初读取的整个文档作为更新操作的过滤器。因此,如果一个单独的更新没有直接修改您知道的任何文档,那么一定是其他更改修改了您以前阅读的文档。对于这些情况,您需要稍后重试(或直接重试,直到更新确实修改了文档)。

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

因此,对于按多个属性进行“排序”,以下引用将进行如下调整:

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 }
)
哪一个奇怪地用于实际“修改”数组?是的,这是真的,因为修饰符说我们实际上没有添加任何新的内容,但是修饰符实际上将应用到数组中并“重新排序”

当然,这将解释如何编写对阵列的“新”更新,以应用该更新,并确保“最大年龄”始终是阵列中的“第一个”:

db.collection.update(
  { "dep_id": "some_id" },
  { "$push": { 
    "employees": { 
      "$each": [{ "name": "emp": 3, "age": 32 }],
      "$sort": { "age": -1 } 
    }
  }}
)
因此,这里发生的事情是,当您在更新时将新条目添加到数组中时,会应用修改器,并将新元素重新定位到两个现有元素之间,因为这是它将排序到的位置

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


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

为什么要这样做呢?@dnickless,我想这样做是为了优化我的一个查询模式。我的实际对象有“秩”和“created_at”,我希望第一个子对象始终是具有最高秩的对象,如果多个项目具有最高秩,那么数组中的第一个项目应该是具有最高秩的最新项目。我可以通过查询来做到这一点,但我必须$unwind然后排序,如果我这样做,我可能会达到mongodb的限制,当您尝试对大量数据进行排序时,这种限制就会生效,而排序并不是聚合的第一步。因此,这个过程完全没有必要,而且mongodb已经找到了“就地”更新数组内容的方法许多年来一直保持着一种新的排序顺序。因此,您不需要写入新集合,也不需要循环要转换的结果。