mongodb基于数组中的值聚合条件添加字段

Ste*_*ber 9 mongodb mongodb-query aggregation-framework

请原谅标题。可以找到更好的描述我正在尝试做什么。

我有一组消息,其中存储了以下信息

  • 代码:消息的唯一识别码
  • from:发送消息的电话号码
  • to:消息发送到的电话号码
  • 消息:消息文本
  • 读数:一组 ObjectIds。ids 引用另一个集合名称“users”中的文档。如果 ObjectId 在这里,则表示该消息已被该特定用户读取。

示例数据

{
    "_id" : ObjectId("59ba30c95869d32a803e4c4d"),
    "code" : "SM54c9366e9b8544e89bdcf2ee841adea7",
    "from" : "+49157xxxxxxx",
    "to" : "+49160xxxxxxxx",
    "message" : "xxxxxxxx",
    "createdAt" : ISODate("2017-09-14T07:33:39.000Z"),
    "lastModifiedAt" : ISODate("2017-09-14T07:33:32.324Z"),
    "status" : "delivered",
    "room" : ObjectId("59bfa293bd7717251cecfae7"),
    "readings" : [ 
        ObjectId("59c25751dcfdaf2944ee2fae"), 
        ObjectId("59c25751dcfdaf2944e32fae")
    ],
}

/* 2 */
{
    "_id" : ObjectId("59ba3270f53b7f2fb4fa807f"),
    "code" : "SM04585672d02644018e3ff466d73c571d",
    "from" : "+49xxxxxxx",
    "to" : "+49xxxxxxxx",
    "message" : "xxxxxxx",
    "createdAt" : ISODate("2017-09-14T07:40:42.000Z"),
    "lastModifiedAt" : ISODate("2017-09-14T07:40:34.338Z"),
    "status" : "delivered",
    "room" : ObjectId("59bfa293bd7717251cecfae7"),
    "readings" : [ 
        ObjectId("59c25751dcfdaf2944ee2fae")
    ],
}
Run Code Online (Sandbox Code Playgroud)

我想要实现的是,如果特定用户已阅读消息,则消息会获得一个附加字段“hasRead”。

这是我想要达到的结果

{
    "_id" : ObjectId("59ba30c95869d32a803e4c4d"),
    "code" : "SM54c9366e9b8544e89bdcf2ee841adea7",
    "to" : "+491606983534",
    "message" : "Schau mer mal",
    "createdAt" : ISODate("2017-09-14T07:33:39.000Z"),
    "lastModifiedAt" : ISODate("2017-09-14T07:33:32.324Z"),
    "status" : "delivered",
    "room" : ObjectId("59bfa293bd7717251cecfae7"),
    "hasRead" : true
}

/* 2 */
{
    "_id" : ObjectId("59ba3270f53b7f2fb4fa807f"),
    "code" : "SM04585672d02644018e3ff466d73c571d",
    "to" : "+491606983534",
    "message" : "Schau mer mal",
    "createdAt" : ISODate("2017-09-14T07:40:42.000Z"),
    "lastModifiedAt" : ISODate("2017-09-14T07:40:34.338Z"),
    "status" : "delivered",
    "room" : ObjectId("59bfa293bd7717251cecfae7"),
    "hasRead" : true
}
Run Code Online (Sandbox Code Playgroud)

我用以下阶段构建了一个聚合,但对于这样一个简单的任务来说它看起来太大了,我想知道是否有更优雅、更轻松的方法来做到这一点?

这些阶段是:

  • $addFields:检查读数数组是否为 0。如果为 0,则添加一个虚拟的 ObjectId,否则设置读数数组
  • $unwind:展开读数数组
  • $addFields:在检查特定 ObjectId 是否与“读数”字段匹配时添加字段“hasRead”。如果相等则为真否则为假
  • $group:除“hasRead”字段之外的所有字段分组,“hasRead”基于$max hasRead
  • $project:构造结果使其成为平面对象。

这是我的代码:

db.getCollection('sms').aggregate([
{ $addFields: {
    "readings": {
        "$cond": {
            if: { $or: [ { "$gt": [ {"$size": "$readings"},0] } ]} ,
            then: "$readings",
            else: [ObjectId("000000000000000000000000")]
        }
    }
}},
{ $unwind: "$readings" },
{ $addFields: { 
    "hasRead": { 
        $cond: { 
            if: { 
                $eq: ["$readings", ObjectId("59c25751dcfdaf2944ee2fae")] 
            }, 
            then: true, 
            else: false 
        }
    } 
  } 
},
{ $group: {
    _id: { 
        _id: "$_id",
        code: "$code",
        from: "$from",
        to: "$to",
        message: "$message",
        createdAt: "$createdAt",
        lastModifiedAt: "$lastModifiedAt",
        room: "$room"
    },
    hasRead: { $max: "$hasRead" }
}},

{ $project: {
    "_id": "$_id._id",
    "code": "$_id.code",
    "from": "$_id.from",
    "to": "$_id.to",
    "message": "$_id.message",
    "createdAt": "$_id.createdAt",
    "lastModifiedAt": "$_id.lastModifiedAt",
    "room": "$_id.room",
    "hasRead": "$hasRead"
}}
])
Run Code Online (Sandbox Code Playgroud)

浏览完尼尔(见评论)给另一个问题的答案后,我可以将查询简化为:

db.getCollection('sms').aggregate([
    { "$addFields": {
        "hasRead" : {
            "$filter": {
                "input": { "$setUnion": [ "$readings", []] },
                "as": "o",
                "cond" : {
                    "$eq": [ "$$o",ObjectId("59c25751dcfdaf2944ee2fae")]
                    }
                }
            }
        }
    },
    { "$project": {
        "_id": 1,
        "code": 1,
        "to": 1,
        "message": 1,
        "createdAt": 1,
        "lastModifiedAt" : 1,
        "status": 1,
        "room": 1,
        "hasRead": { 
            "$cond": {
            if: { $or: [ { "$gt": [ {"$size": "$readings"},0] } ]} ,
            then: true,
            else: false
            }
        }
    }
}
])
Run Code Online (Sandbox Code Playgroud)

And*_*rea 0

太晚了,但你可以简单地写:

db.getCollection("sms").aggregate([
  {
    $project: {
      _id: 1,
      code: 1,
      to: 1,
      message: 1,
      createdAt: 1,
      lastModifiedAt: 1,
      status: 1,
      room: 1,
      hasRead: {
        $in: [ObjectId("59c25751dcfdaf2944ee2fae"), "$readings"],
      },
    },
  },
]);

Run Code Online (Sandbox Code Playgroud)

通常最简单的解决方案就是正确的解决方案:)