从内部数组中选择第一个元素

时间:2017-06-29 06:58:24

标签: javascript mongodb mongoose mongodb-query aggregation-framework

我想从嵌套数组中选择第一项,而不是获取整个文档。

架构/型号

假设我有一个类似的架构:

const parentSchema = mongoose.Schema({
  name: String,
  children: []
});

const grandparentSchema = mongoose.Schema({
  name: String,
  children: [parentSchema]
})

将转换为此示例实例:

{
  name: 'Grandparent Foo',
  children: [
    {
      name: 'Parent Foo',
      children: ['Child Foo', 'Child Bar', 'Child Baz']
    }
  ]
}

问题

我想得到'Parent Foo'的第一个孩子,所以要把它煮熟,我应该回来'Child Foo'

注释

  • 正如您所看到的,孙子是普通的String,而不是Document s(与父母相比),所以我不能用点符号选择它们。

  • 我不想返回整个文档并在代码中过滤掉它。由于孙子数组(children 'Parent Foo'数组$pop)可能包含数百万条目,所以我只想接通第一个孙子。

  • 我需要这个,因为我想$pop第一个孙子并将其归还。为此,我计划首先获取该项目,然后{{1}}关闭它,因此我问这个问题

1 个答案:

答案 0 :(得分:1)

如果不在数据库中投入额外的工作,你就不能真的。

作为一般性解释:

Grandparent.find(
  { "children.name": "Parent Foo" },
  { "children.$": 1 }
)

将仅返回"children"中匹配的条目,而不存在其他条目。

如果您明确需要"首先"数组元素,然后使用.aggregate()

Granparent.aggregate([
  { "$match": { "children.name": "Parent Foo" } },
  { "$addFields": {
    "children": {
      "$map": {
        "input": {
          "$filter": {
            "input": "$children",
            "as": "child",
            "cond": { "$eq": [ "$$child.name", "Parent Foo" ] }
          }
        },
        "as": "child",
        "in": {
          "name": "$$child.name",
          "children": { "$arrayElemAt": [ "$$child.children", 0 ] }
        }
      }
    }
  }}
])

所以你基本上使用$filter来复制标准的位置匹配,然后使用$map重塑$arrayElemAt$slice以实际获得内部的第一个元素阵列。

相比之下,如果你生活在返回"少量的额外数据",那么你只需切断位置匹配:

Grandparent.find(
  { "children.name": "Parent Foo" },
  { "children.$": 1 }
).lean().exec((err,docs) => {
  docs = docs.map( doc => {
    doc.children = doc.children.map( c => c.children = c.children.slice(0,1) );
    return doc;
  });
  // do something with docs

所以我们在光标中返回了一点,只需要很少的努力就可以摆脱那些非常小的数据。

由于实际数据的实际大小,里程可能因此而异,但如果差异为“小”,则通常最好“修剪”#34;在客户端而不是服务器。