1

我有一个group对象,它包含一个members属性。 members属性是一个对象数组。该对象具有以下属性。如何通过猫鼬中的模式属性进行过滤

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

我的要求是(由给定ID)来获得用户,谁是有状态1,给定group对象的名单。

这是可能做到的,通过一个简单的ID查询和一个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"}); 

非常感谢您。

+0

您的意思是对于给定的,而不是_group_对象_post_对象? – chridam

+0

除了给出Schema定义之外,分享少量示例文档和预期输出将会很好,这样很容易理解您要完成的任务。 – superUser

+0

@chridam'组'对象。编辑了这个问题。 –

回答

2

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

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

一旦你得到了过滤的数组,你可以应用功能作为“填充”你的成员名单的手段。然而,由于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操作在本质上创建保持值作为一个子表达式到数组的每个元素的逻辑评价的结果的新的数组字段。然后,运算符返回一个集合,其中的元素出现在第一个集合中,但不出现在第二个集合中;即执行第二组相对于第一组的相对补偿。在这种情况下,它将通过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); 
    }); 
}); 
+0

lookup是如何工作的 –

+0

@sacDahal它通过对同一个数据库中的未加注集合执行“左外部联接”来过滤来自“已加入”集合的文档进行处理。 '$ lookup'阶段在输入文档的字段与“已加入”集合的文档中的字段之间进行平等匹配。有时'$ lookup'可能会被误用来将MongoDB当作关系数据库来对待,但是开发人员应该知道它的使用何时适用,何时它是反模式。 – chridam

+0

看起来像我的mongoDB版本不兼容'$ filter'管道阶段。是否有其他可能的阶段,我们可以用来代替'$ filter'。 –

0

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

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

这将获取所有如果想根据特定USER_ID查询(假定用户ID为具有状态1.用户“1A”在这里),您可以添加在这样的对象:

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