【问题标题】:How to sort a dictionary keys and pick the first in MongoDb?如何对字典键进行排序并在 MongoDb 中选择第一个?
【发布时间】:2021-09-03 11:06:19
【问题描述】:

我正在运行the docs 中所述的以下查询。

db.getCollection('things')
  .find(
    { _id: UUID("...") },
    { _id: 0, history: 1 }
  )

它产生一个单一的元素,当它在 GUI 中展开时,会显示字典历史。当我展开它时,我会看到内容:一堆键和相关值。

现在,我想按字母顺序对键进行排序,然后选择 n 个第一个键。请注意,它不是数组,而是存储的字典。另外,如果我可以展平结构并弹出我的 history 作为返回文档的头部(根?),那就太好了。

我知道这是关于投影和切片的。但是,尽管进行了很多尝试,但我还是没有成功。我收到语法错误或完整的元素列表。作为一个菜鸟,我担心我需要一些关于如何诊断我的问题的指示。

基于 cmets,我尝试使用 aggregate$sort。遗憾的是,我似乎只对当前输出进行排序(由于匹配条件而产生单个文档)。我想访问history中的元素。

db.getCollection('things')
  .aggregate([
    { $match: { _id: UUID("...") } },
    { $sort: { history: 1 } }
  ])

我感觉到我应该使用投影来提取位于history 下的元素列表,但使用以下内容没有成功。

db.getCollection('things')
  .aggregate([
    { $match: { _id: UUID("...") } },
    { $project: { history: 1, _id: 0 } }
  ])

【问题讨论】:

  • 您能否展示一个示例文档以及该文档的预期结果?在查找查询的对象中无法进行排序,您必须使用聚合查询。
  • @turivishal 不确定如何展示示例(很可能是由于我对 MongoDb 不熟悉)。对于那个很抱歉。我主要坚持的是如何访问存储在字段 history 中的元素。按照您的建议,当我切换到聚合时,请查看编辑。
  • 例如。假设历史字段在数据库中有history: { field1: "value", field2: "value" },如果您只想在历史中选择field1,他们在投影中使用history: { field1: 1 },如果您想在root 中选择field1,请使用field1: "$history.field1"
  • @turivishal 啊,现在我们到了某个地方!部分,但仍然。现在,如果我不知道字段的名称但想选择第一个(即按字母排序后)怎么办?我想进行匹配,然后对history 中的字段进行排序,然后选择最上面的 3。

标签: mongodb aggregation-framework projection


【解决方案1】:

仅按字母顺序对对象属性进行排序是一个漫长的过程,

  • $objectToArrayhistory 对象转换为键值格式的数组
  • $unwind 解构上面生成的数组
  • $sort by history 按升序排列(1 = 升序,-1 = 降序)
  • $group by _id 并重构 history 键值数组
  • $slice 从顶部的字典中获取您的属性数量,我输入了 1
  • $arrayToObject返回键值数组转为对象格式
db.getCollection('things').aggregate([
  { $match: { _id: UUID("...") } },
  { $project: { history: { $objectToArray: "$history" } } },
  { $unwind: "$history" },
  { $sort: { "history.k": 1 } },
  {
    $group: {
      _id: "$_id",
      history: { $push: "$history" }
    }
  },
  { 
    $project: { 
      history: { 
        $arrayToObject: { $slice: ["$history", 1] } 
      } 
    } 
  }
])

Playground


还有另一种选择,但根据 MongoDB,它不能保证这会重现确切的结果,

  • $objectToArrayhistory 对象转换为键值格式的数组
  • $setUnion 基本上这个操作符会从一个数组中获取唯一的元素,但是根据经验,它会按 key 升序对元素进行排序,所以对于 MongoDB 没有任何保证。
  • $slice 要从顶部的字典中获取您的属性数量,我输入了 1
  • $arrayToObject返回键值数组转对象格式
db.getCollection('things').aggregate([
  { $match: { _id: UUID("...") } },
  {
    $project: {
      history: {
        $arrayToObject: {
          $slice: [
            { $setUnion: { $objectToArray: "$history" } },
            1
          ]
        }
      }
    }
  }
])

Playground

【讨论】:

  • 里面的“.k”有什么意义?它只是一个临时变量名,还是我不熟悉的特定于 MongoDb 的名称?
  • $objectToArray 运算符将从{ field1: "value" } 字典中生成[{k: "field1", v: "value"}] 数组,因此只需按名称排序很容易,而$arrayToObject 则相反。你可以检查更新的答案。
  • 您的回答很好地解决了我的全部问题。我意识到我什至还没有接近,并且文档中有一些我以前甚至不知道的新花盆可供研究(例如$unwind 操作员)。真的很棒的信息!我是否总是让文档展开以查看我需要展开以查看切出的字段的历史记录?或者有没有办法将结果“展平”以仅列出跳过历史记录的字段作为持有者?我觉得这与作为附加阶段的投影有关,但我并不完全确定......
  • 是的,您必须执行此过程,该选项是准确的。我也添加了第二个选项,但 mongodb 不能保证重现预期结果。
猜你喜欢
  • 1970-01-01
  • 2020-12-04
  • 2015-11-11
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 1970-01-01
相关资源
最近更新 更多