Mongoose子查询并将结果附加到mainquery

时间:2015-10-13 16:57:51

标签: node.js mongodb mongoose

我一直在努力解决这些问题,现在仍然没有解决方案。

基本上我有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 = [
  {
    _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: ...
  },
  ...
]

我想运行一个mongoose查询,将查询用户作为​​主要对象。

在返回用户对象的结果时,我想用过滤后的用户查询所有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



]

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

在此先感谢,我尝试了很多次并且失败了。

2 个答案:

答案 0 :(得分:3)

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

  1. 获取与特定组匹配的用户文档列表。
  2. 获取从步骤1分配给每个匹配用户的项目文档列表。
  3. 将相应的项目文档分配给相应用户文档上的新属性。
  4. 这可以通过几种方式解决。第一遍可能是检索所有用户文档,然后在内存中迭代它们,检索每个用户的项目文档列表并将该列表附加到用户文档。如果你的名单很小,这不应该是一个太大的问题,但随着规模发挥作用,这将成为一个更大的列表,它可能成为一个记忆猪。

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

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

    虽然这可以解决问题,但是可以做出很多改进,使其性能更高,以及"清洁"。

    例如,让我们使用promises,因为这涉及异步操作。 假设Mongoose是configured to use the native Promise object或者当时/ 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
    });
    

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

答案 1 :(得分:0)

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