【问题标题】:Mongoose subquery and append results to mainquery猫鼬子查询并将结果附加到主查询
【发布时间】:2018-02-16 17:06:53
【问题描述】:

我已经为这些问题苦苦挣扎了几个月,现在仍然没有解决方案。

基本上我有 2 个 mongodb 数据库结构。

一个叫做用户,另一个叫做项目。

一个用户可以拥有多个项目。

用户结构简单=

Users = [{
    _id: 1,
    name: "Sam",
    email: "sam@gmail.com",
    group: "Rangers"
  },

  {
    _id: 2,
    name: "Michael",
    email: "michael@gmail.com"
    group: "Muse"
  },

  {
    _id: 3,
    name: "John",
    email: "john@gmail.com"
    group: "Merchant"
  },
  .....
]

Items 结构如下,每个项目都分配给一个用户。

Items = [
  {
    _id: 1,
    user_id: 1,
    item_name: "Flying Sword",
    timestamp: ...
  },
  {
    _id: 3,
    user_id: 1,
    item_name: "Invisible Cloak",
    timestamp: ...
  },

  {
    _id: 4,
    user_id: 2,
    item_name: "Iron Shield"
  },

  {
    _id: 5,
    user_id: 7,
    item_name: "Splashing Gun",
    timestamp: ...
  },
  ...
]

我想运行一个猫鼬查询,将用户作为主要对象进行查询。

在返回用户对象的结果后,我想用过滤后的用户查询所有 Items 对象,并将它们作为子文档附加到先前查询的每个用户对象。

比如我要查询

Users.find({group: "Muse"}, function(err, users){
    I DON"T KNOW WHAT TO WRITE INSIDE
})

基本上结果应该是:

[
  {
    _id: 4,
   name: "Jack",
   email: "jack@gmail.com",
   group: "Muse",
   items: [
     {
       _id: 8
       name: "Magic Wand",
       user_id: 4,
       timestamp: ...
     }
     {
       _id: 12
       name: "Blue Potion",
       user_id: 4,
       timestamp: ...
     },

     {
       _id: 18
       name: "Teleportation Scroll",
       user_id: 4,
       timestamp: ...
     }
   ]
  }
  .....
  More USERS of similar structure



]

每个用户最多返回三个按时间戳排序的项目。

先谢谢了,我试了很多次都失败了。

【问题讨论】:

    标签: node.js mongodb mongoose


    【解决方案1】:

    这是一个多步骤的问题。所以让我们列出步骤:

    1. 获取与特定组匹配的用户文档列表。
    2. 获取从步骤 1 分配给每个匹配用户的项目文档列表。
    3. 将适当的项目文档分配给相应用户文档上的新属性。

    这可以通过几种方式解决。第一步可能是检索所有用户文档,然后在内存中遍历它们,检索每个用户的项目文档列表并将该列表附加到用户文档。如果您的列表很小,这应该不是什么大问题,但随着规模的增加,这变成一个更大的列表,它可能会占用内存。

    注意:以下所有代码均未经测试,因此可能存在拼写错误等。

    Users.find({group: "Muse"}, function(err, users){
      var userIDs;
    
      if (err) {
        // do error handling
        return;
      }
    
      userIDs = users.map(function (user) { return user._id; });
    
      Items.find({user_id: {$in: userIDs}}, function (err, items) {
        if (err) {
          // do error handling
          return;
        }
    
        users.forEach(function (user) {
          user.items = items.filter(function (item) {
            return item.user_id === user._id;
          });
        });
    
        // do something with modified users object
      });
    });
    

    虽然这将解决问题,但可以进行大量改进以使其性能更高,并且更“干净”。

    例如,让我们使用 Promise,因为这无论如何都涉及异步操作。 假设 Mongoose 是 configured to use the native Promise object 或 then/catch 兼容库

    Users.find({group: "Muse"}).exec().then(function(users) {
      var userIDs = users.map(function(user) {
        return user._id;
      });
    
      // returns a promise
      return Promise.all([
        // include users for the next `then`
        // avoids having to store it outside the scope of the handlers
        users,
        Items.find({
          user_id: {
            $in: userIDs
          }
        }).exec()
      ]);
    }).then(function(results) {
      var users = results[0];
      var items = results[1];
    
      users.forEach(function(user) {
        user.items = items.filter(function(item) {
          return item.user_id === user._id;
        });
      });
    
      return users;
    }).catch(function (err) {
      // do something with errors from either find
    });
    

    这使得它在主观上更具可读性,但并没有真正帮助,因为我们在内存中进行了大量操作。同样,如果文档集合很小,这可能不是问题。但是,如果是这样,则可以通过将项目请求分解为每个用户一个来进行权衡。因此一次只能处理项目列表的块。

    我们还将使用Bluebird's map 来限制项目的并发请求数。

    Users.find({group: "Muse"}).exec().then(function(users) {
      return bluebird.map(users, function(user) {
        return Items.find({user_id: user._id}).exec().then(function (items) {
          user.items = items;
          return user;
        });
      }, {concurrency: 5});
    }).then(function(users) {
      // do something with users 
    }).catch(function(err) {
      // do something with errors from either find
    });
    

    这限制了项目在内存中的操作量,但仍然让我们在内存中迭代用户。这也可以通过使用 mongoose 流来解决,但我会留给你自己去探索(how to use streams 上的 SO 上还有其他问题)。

    【讨论】:

      【解决方案2】:

      这使得它在主观上更具可读性,但并没有真正帮助,因为我们在内存中进行了大量操作。同样,如果文档集合很小,这可能不是问题。但是,如果是这样,则可以通过将项目请求分解为每个用户一个来进行权衡。因此一次只能处理项目列表的块。

      【讨论】:

      • 这应该是评论而不是答案。当您获得足够的声望时,您将能够发布 cmets。
      猜你喜欢
      • 2017-05-08
      • 1970-01-01
      • 2017-09-21
      • 2014-07-25
      • 2012-03-23
      • 1970-01-01
      • 1970-01-01
      • 2019-01-26
      • 2017-12-03
      相关资源
      最近更新 更多