从与数组上的查询条件匹配的第一个元素投影特定字段

问题描述

我有一个 families 集合。其中的文档有一个 users 数组。鉴于此家庭文件

{
    "id" : "1","users" : [ 
        {
            "id" : "2","identities" : [ 
                {
                    "type" : "email","email" : "foo"
                },{
                    "type" : "email","email" : "bar"
                }
            ]
        },{
            "id" : "3","email" : "baz"
                },"email" : "qux"
                }
            ]
        }
    ]
}

我想投影 type 数组元素的 identities 字段,但只能来自我查询特定用户。位置运算符获得正确的用户

db.families.findOne(
    { 'users.id': '2' },{ 'users.$': 1 } 
  )

我可以指定我感兴趣的字段:

db.families.findOne(
    { 'users.id': '2' },{ 'users.identities.type': 1 } 
  )

但是当两者结合时,只考虑位置运算符:

db.families.findOne(
    { 'users.id': '2' },{ 'users.$': 1,'users.identities.type': 1 } 
  )

(返回 id 为 2 的用户,但返回包含所有字段的 identities 数组元素)。 想试试这个:

db.families.findOne(
    { 'users.id': '2' },{ 'users.$.identities.type': 1 } 
  )

但它不好,因为per the docs

MongoDB 忽略 $

后面的路径部分

有办法吗?

解决方法

您只需使用 $ 运算符来投影字段。

查询:

db.collection.find({
  "users.id": "2"
},{
  "users.identities.type.$": 1
});

结果:

[
  {
    "_id": "1","users": [
      {
        "identities": [
          {
            "type": "email"
          },{
            "type": "email"
          }
        ]
      }
    ]
  }
]
,

这将仅投影所选 users.id 的类型和 users.id:

 db.families.findOne({"users.id":"2"},{"users":{id:1,"identities.type.$":1}})

 {
"_id" : ObjectId("601f1974cf95940e8e0c814e"),"users" : [
    {
        "id" : "2","identities" : [
            {
                "type" : "email"
            },{
                "type" : "email"
            }
        ]
    }
]
}

(在 mongod 4.4.3 中测试) 也可以这样重写(也是 4.4.3 ):

 db.families.findOne({"users.id":"2"},"identities":{"type.$":1}}})

在 mongod 4.2.12 中可以通过以下聚合实现相同的结果:

 db.families.aggregate([  
{$project:{users:{$filter:{input:"$users",as:"us",cond:{$eq:["$$us.id","2"]}}}}},{$project:{"users.identities.type":1,"users.id":1  }}     
 ])

聚合解释:

  • 在第一个 $project 阶段,您过滤 users.id:2 的数组元素
  • 在第二个 $project 阶段,您只投影您需要的字段