【问题标题】:Find after populate mongoose填充猫鼬后查找
【发布时间】:2015-09-30 04:55:07
【问题描述】:

在猫鼬填充后,我在通过文档内部匹配的值查询文档时遇到了一些问题。

我的架构是这样的:

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;
        });

有没有办法直接从猫鼬那里查询类似的东西?

【问题讨论】:

    标签: javascript node.js mongodb mongoose


    【解决方案1】:

    @Jason Cust 已经很好地解释了它 - 在这种情况下,最好的解决方案通常是更改架构以防止通过存储在单独集合中的文档的属性来查询 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
      });
    });
    

    我们在这里所做的只是填充匹配附加查询的emails(.populate() 调用中的match 选项)-否则Users 文档中的email 字段将设置为null。

    剩下的就是返回的users 数组上的.filter,就像你原来的问题一样——只有更简单、非常通用的检查。如您所见 - email 存在或不存在。

    【讨论】:

    • 如果users 是采用这种方法的大型集合,我会注意到一些问题。
    • @JasonCust:确实!这可能就是为什么最好的选择仍然是正确更改架构的原因(例如,如果您已经在数据库中有工作的应用程序和一些数据,这可能不是那么有趣)。我还考虑了替代解决方案:1. 获取正确过滤的电子邮件的_ids (Emails.find()),2: 仅加载与电子邮件匹配的用户(Users.find() 和$in 运算符)。如果您有兴趣,我也可以发布此解决方案的示例。一方面,没有加载所有用户文档。另一方面,它似乎更像 hack-ish/workaround-ish。
    • 但这不适用于分页。我也陷入了这种情况,我也必须对数据进行分页。所以现在发生的事情是在第 1 页我得到空数组,但在第 2 页我得到过滤数据。有人可以帮忙吗??
    • 我浪费了太多时间来访问填充路径的字段。虽然我可以正确记录填充字段“用户”,但如果我尝试直接访问其中的字段“user.firstName”,则会引发错误。 “字符串”类型上不存在属性“名字”。因为它是 TS 接口和模型上的引用类型。 //正确记录{ user:{ firstName:"Hasan", lastName:"Mehmet" } }我只是累了,要像你一样过滤结果数组。
    【解决方案2】:

    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);
    });
    

    【讨论】:

    • 我很欣赏关于填充如何工作的解释,这非常有见地。我给出的例子是一个简化,我的实际用例确实需要参考文档,所以我真的必须在不改变模型的情况下找到解决方案。
    • @Rubelet 目前在 MongoDB 中没有 join 的概念,因此 MongooseJS 在这种情况下将无法做更多的事情。我认为尝试以这种方式查询集合的一个问题是,如果它是一个大型集合,那么人口将是一项昂贵的操作。如果这是架构必须的方式,我会考虑使用流和过滤以避免在填充期间造成瓶颈。
    猜你喜欢
    • 2015-09-08
    • 2012-11-11
    • 2015-07-13
    • 2016-10-10
    • 2019-09-16
    • 2021-04-19
    • 2023-03-21
    • 2015-07-13
    相关资源
    最近更新 更多