2015-07-11 66 views
8

我在通过猫鼬填充人口后,通过文档内部的值匹配查询文档时遇到了一些麻烦。在填充猫鼬后查找

我的模式是这样的:

var EmailSchema = new mongoose.Schema({ 
    type: String 
}); 

var UserSchema = new mongoose.Schema({ 
    name: String, 
    email: [{type:Schema.Types.ObjectId, ref:'Email'}] 
}); 

我想有有与该类型=“Gmail的”,例如电子邮件的所有用户。

以下查询返回空的结果:

Users.find({'email.type':'Gmail').populate('email').exec(function(err, users) 
    { 
     res.json(users); 
    }); 

我不得不求助于这样的过滤结果JS:

users = users.filter(function(user) 
     { 
      for (var index = 0; index < user.email.length; index++) { 
      var email = user.email[index]; 
      if(email.type === "Gmail") 
      { 
       return true; 
      } 
      } 
      return false; 
     }); 

有什么办法来查询像这样直接从猫鼬?

回答

9

@Jason卡斯特解释还挺好已经 - 在这种情况下往往是最好的解决办法是修改模式以防止通过存储在单独集合中的文档的属性来查询Users

尽管(因为你在评论中说你不能这样做),但我可以想到的最好的解决方案并不会强制你这样做。

Users.find().populate({ 
    path: 'email', 
    match: { 
    type: 'Gmail' 
    } 
}).exec(function(err, users) { 
    users = users.filter(function(user) { 
    return user.email; // return only users with email matching 'type: "Gmail"' query 
    }); 
}); 

我们这里所做的是填充只email小号匹配其他查询(在.populate()调用match选项) - 否则emailUsers文件将被设置为null

剩下的只有.filter返回users数组,就像在你原来的问题中一样 - 只有更简单,非常通用的检查。正如你所看到的 - 无论是email还是不存在。

+2

我只是注意到一点担心,如果'用户'是这种方法的大集合。 –

+0

@JasonCust:的确如此!这可能是为什么仍然最好的选择是正确地改变模式(例如,如果你已经有工作应用程序和数据库中的一些数据,这可能不那么有趣)。我也考虑了另一种解决方案:1.获取正确过滤的电子邮件('Emails.find()')的'_id',2:仅加载与这些('Users.find()'匹配的电子邮件的用户'$ in'运算符)。如果您有兴趣,我也可以发布此解决方案的示例。一方面,不加载所有用户文件。另一方面,它似乎更加黑客 - 解决方法 - 是的。 – bardzusny

3

Mongoose的populate函数不会直接在Mongo中执行。取而代之的是,在最初的find查询返回一组文档后,populate将在引用的集合上创建一个单独的find查询数组,以执行并将结果合并回原始文档。因此,基本上,您的find查询正试图使用​​所引用文档的属性(尚未提取,因此为undefined)来过滤原始结果集。

在这个用例中,将电子邮件存储为子文档数组而不是单独的集合来实现您想要执行的操作似乎更合适。另外,作为一般的文档商店设计模式,这是将数组作为子文档存储的有用用例之一:有限的大小和很少的修改。

更新您的架构:

var EmailSchema = new mongoose.Schema({ 
    type: String 
}); 

var UserSchema = new mongoose.Schema({ 
    name: String, 
    email: [EmailSchema] 
}); 

则下面的查询应该工作:

Users.find({'email.type':'Gmail').exec(function(err, users) { 
    res.json(users); 
}); 
+1

我很欣赏关于如何填充作品的解释,这非常有见地。我给出的例子是一个简化,我的实际用例确实需要引用文档,所以我必须在不更改模型的情况下找到解决方案。 – Monokh

+3

@Rubelet目前在MongoDB中没有“join”的概念,所以MongooseJS在这种情况下将无法做到比当前设计的更多。我认为试图以这种方式询问收集品的问题在于,如果收集的是大量收集品,那么这样做的成本会很高。如果这是模式必须如何,我会考虑使用流和过滤,以避免在人口中造成瓶颈。 –