我一直在努力解决这些问题,现在仍然没有解决方案。
基本上我有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
]
每个用户最多返回三个按时间戳排序的项目。
在此先感谢,我尝试了很多次并且失败了。
答案 0 :(得分: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
});
});
虽然这可以解决问题,但是可以做出很多改进,使其性能更高,以及"清洁"。
例如,让我们使用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)
这使得它在主观上更具可读性,但由于我们在内存中进行了大量操作,所以它并没有真正帮助。同样,如果文档集很小,这可能不是一个问题。但是,如果是,则可以通过将项目请求分解为每个用户一个来进行权衡。因此,一次只能处理项目列表的块。