【问题标题】:How can I omit elements from arrays inside nested documents with mongo?如何使用 mongo 从嵌套文档中的数组中省略元素?
【发布时间】:2016-02-18 01:16:32
【问题描述】:

我在 MongoDB 中的集合具有以下结构

{
   '_id': 45
   'tags': [ 'tag 1', 'tag 3' ]
   'active': true
   'fields': [
      { 'name': 'common field 1', 'type': 'text', 'value': 'some text', ... },
      { 'name': 'common field 2', ... },
      { 'name': 'multivalued field 1', 
        'type': 'multifield',
        'valueCount': 5,
        'value': [
            { 'name': 'subfield1', ..., 'value': [1, 2, 3, 4, 5]},
            { 'name': 'subfield2', ..., 'value': ["one", "two", "three", "four", "five"]},
            { 'name': 'subfield3', ..., 'value': ["here", "there", "", "", ""]}
        ], ... }
   ]
}

我正在尝试在我的 API 中实现投影:例如,如果用户请求

api/collection/?fields=id,fields{common field 2, multifield{subfield1}} 

结果应该是

{
   '_id': 45
   'fields': [
      { 'name': 'common field 1', 'type': 'text', 'value': 'some text', ... },
      { 'name': 'multivalued field 1', 
        'type': 'multifield',
        'valueCount': 5,
        'value': [
            { 'name': 'subfield1', ..., 'value': [1, 2, 3, 4, 5]},
        ], ... }
   ]
}

由于“字段”名称不是实际的键,我不能使用 mongo 投影,比如说

db.collection.find({},{_id: 1, tags: 1, fields.'common field 1': 1})

所以我必须改为在数组中搜索“名称”属性与我的投影参数匹配的字段。正如这个答案https://stackoverflow.com/a/24032549/5418731

中所建议的,我通过聚合和 $redact 实现了第一级数组
db.points.aggregate([
      { $match: {}},
  {
        $project: {_id :1, fields: 1}
      },
      { $redact : {
       $cond: {
        if: { $or : [{ $not : "$name" }, { $eq: ["$name", "common field 1"] }]},
           then: "$$DESCEND",
           else: "$$PRUNE"
       }
      }}])

但是,我不能使用 $redact 从多值字段的内部数组中选择子字段。 $or 参数必须类似于

[{ $not : "$name" }, { $eq: ["$name", "common field 1"] }, { $eq: ["$name", "subfield1"] }]

这意味着与指定的子字段具有相同名称的第一级字段也将通过。

将 MongoDB 升级到 3.2 后,我尝试了这个答案 https://stackoverflow.com/a/12241930/5418731 中的 $filter 解决方案,它也适用于一级数组:

db.points.aggregate([
    {$project: {
        fields: {$filter: {
            input: '$fields',
            as: 'field',
            cond: {$eq: ['$$field.name', 'multivalued field 1']}
        }}
    }}
])

但我找不到“嵌套”使用它并过滤二级数组项的方法。添加{$eq: ['$$field.value.name', 'subfield1']} 不起作用。

最后,我尝试了这里介绍的 $map 解决方案https://stackoverflow.com/a/24156418/5418731

db.points.aggregate([
    { "$project": {
        "_id": 1,
        "fields": {
            "$map": {
                "input": "$fields",
                "as": "f",
                "in": {
                    "$ifNull": [
                        { 
                            "name": "$multivalued field 1",
                            "type": "$multifield", //attempt to restrict search to fields with arrays as values
                            "value": {
                                "$map": {
                                    "input": "$$f.value",
                                    "as": "v",
                                    "in": {
                                        "$ifNull": [
                                            { "name": "$subfield1"},
                                            false
                                        ]
                                    }
                                }
                            }
                        },
                        false
                    ]
                }
            }
        }
    }}
])

但是这个不起作用,因为每个“字段”项的“值”属性不一定是一个数组,如果不是,整个查询就会失败。

我即将放弃并掩盖 JS 中的结果。 Mongo 有什么好的解决方案吗?

【问题讨论】:

    标签: arrays mongodb subdocument


    【解决方案1】:

    您的示例请求中是否存在拼写错误。当您查询文档时 名称为“common field 2”的字段,但您期望在响应中出现“common field 1”。

    此外,您可以简单地使用聚合管道并按以下方式进行操作,而不是让这变得如此复杂:

    1. 字段数组上的第一个 $unwind。
    2. 然后 $match fields where fields.name = "common field 1" and type = "multifield"。
    3. 然后对值数组进行 $unwind。
    4. 最后 $match fields.value.name = "subfield1" 的字段

    类似这样的:

    db.points.aggregate([
        { $unwind: "$fields" },
        { $match: { "fields.name": "common field 1", "fields.type": "multifield" } },
        { $unwind: "$fields.value" },
        { $match: { "fields.value.name": "subfield1" } }
    ]);
    

    【讨论】:

      猜你喜欢
      • 2020-05-27
      • 1970-01-01
      • 2017-01-05
      • 2017-01-07
      • 1970-01-01
      • 1970-01-01
      • 2018-11-28
      • 1970-01-01
      • 2021-02-26
      相关资源
      最近更新 更多