如何按mongoose中的schema属性进行过滤

时间:2017-01-02 12:48:28

标签: node.js mongodb mongoose mongodb-query aggregation-framework

我有一个group对象,其中包含members属性。 members属性是一个对象数组。该对象具有以下属性。

{"id" : "1", "status" : 1}

我的要求是获取给定1个对象(按给定ID)的状态为group的用户列表。

这可以通过简单的ID by get查询和foreach来实现。但是想知道,是否有任何提前查询要做到这一点。

我的整体Group对象如下。

var UserSchema = new Schema({
  user_id:{
    type : Schema.ObjectId,
    ref : 'User',
    default : null
  },
  status:{
    type : Number,
    default : null /* 1 - pending | 2 - rejected | 3 - accepted*/
  },
});

var GroupSchema = new Schema({
  type:{
    type : Number,
    default : 1 /* 1 - group | 2 - community*/
  },
  name:{
    type:String,
    default:null
  },
  members:[UserSchema]

},{collection:"groups"});

提前谢谢你。

2 个答案:

答案 0 :(得分:2)

您可以使用聚合框架按给定的组ID和成员数组状态字段筛选groups集合中的文档。这将是您的初始管道阶段, $match 由运营商驱动。

下一个管道步骤应该是 $filter 运算符,它根据给定条件选择成员数组的子集。这是必要的,因为前一个管道只在文档级别过滤,而不是在数组/字段级别过滤。

获得已过滤的数组后,您可以应用 $lookup 功能作为“填充”您的成员列表的方法。但是,由于localField是一个数组,并且您希望将其中的元素与foreignField(单个元素)进行匹配,因此您需要 $unwind 在应用 $lookup 运算符之前,数组作为聚合管道的一个阶段。

以下示例演示了如何在您的案例中应用上述所有步骤:

Group.aggregate([
    { 
        "$match": { 
            "_id": groupId,
            "members.status": 1         
        } 
    },
    {
        "$filter": {
            "input": "$members",
            "as": "member",
            "cond": { "$eq": ["$$member.status", 1] }
        }
    }
    { "$unwind": "$members" },
    {
        "$lookup": {
            "from": "users"
            "localField": "members.user_id",
            "foreignField": "_id",
            "as": "member"
        }
    }
]).exec(function(err, results) { 
    if (err) throw err;
    console.log(results);
});

结果将包含具有组和用户属性的文档列表。

如果您的MongoDB版本不支持版本3.2中引入的 $filter $lookup 运算符。 X和更新版本,然后考虑使用 $setDifference $map 运算符组合来过滤 $project中的数组元素管道。

$map 运算符本质上创建了一个新的数组字段,该字段在子表达式中对数组的每个元素进行评估逻辑时保存值。 $setDifference 运算符然后返回一个集合,其中的元素出现在第一个集合中但不出现在第二个集合中;即执行第二组相对于第一组的相对补码。在这种情况下,它将通过status属性返回包含与父文档无关的元素的最终成员数组。

$project 管道步骤之后执行聚合操作,因为返回的文档是普通的javascript对象,而不是Mongoose Documents(可以返回任何形状的文档),你需要将结果转换为Mongoose Documents,以便您可以在字段上使用结果的填充函数。

以下示例演示了上述解决方法:

Group.aggregate([
    { 
        "$match": { 
            "_id": groupId,
            "members.status": 1         
        } 
    },
    {
        "$project": {
            "type": 1, "name": 1,
            "members": {
                "$setDifference": [
                    {
                        "$map": {
                            "input": "$members",
                            "as": "member",
                            "in": {
                                "$cond": [
                                    { "$eq": [ "$$member.status", 1 ] },
                                    "$$member",
                                    false
                                ]
                            }
                        }
                    },
                    [false]
                ]
            }
        }
    }
]).exec(function(err, result) { 
    if (err) throw err;
    var docs = result.map(function(doc) { return new Group(doc) });
    Group.populate(docs, { "path": "members" }, function(err, results) {
        if (err) throw err;
        console.log(JSON.stringify(results, undefined, 4 ));
        res.json(results);
    });
});

答案 1 :(得分:0)

假设您的收藏名称为groups,您可以这样查询:

db.groups.find({"members.status":1}, {"members":1});

这将获取具有状态1的所有用户。如果要基于特定的user_id进行查询(假设用户ID为&#34; 1A&#34;此处),则可以添加如下对象:< / p>

db.groups.find({"members.status":1, "members.user_id":"1A"}, {"members":1});