更新数组子项排序顺序

时间:2017-11-09 13:39:20

标签: mongodb mongodb-query

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

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

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

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

最后,数据应该像以下一样持久保存到数据库中:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

2 个答案:

答案 0 :(得分:1)

执行此操作的最佳方法是在向项目添加项目时实际应用$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修饰符并将新元素重新定位在两个现有元素之间,因为这是它要排序的位置。

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

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

答案 1 :(得分:0)

为了获得您想要的内容,您可以使用以下查询:

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包含所有单独的更新调用为了更新现有文件。在这里,您可以使用最初读取的整个文档作为更新操作的过滤器。因此,如果单个更新未修改您立即知道的任何文档,则其他一些更改必须修改您之前阅读的文档。那些您需要稍后重试的情况(或者直到更新确实修改文档为止)。