Node.js 如何在同一查询中从当前集合获取对象数组和从另一个集合获取数据
我有一个用户集合。每个用户可以有多个朋友,其_id值存储在其用户文档的数组中 我想呈现一个列表,其中包含用户的朋友姓名列表,在每个姓名下,我想有一个位置,但位置在另一个文档中,因为每个用户可以有多个位置,而不受数量限制,比如他们访问过的/最喜欢的地方 这是两个独立的查询,但我需要以某种方式将它们组合起来 这是一个查询,它查看用户朋友数组中的用户ID值,然后根据ID获取相关的用户信息Node.js 如何在同一查询中从当前集合获取对象数组和从另一个集合获取数据,node.js,mongodb,mongoose,Node.js,Mongodb,Mongoose,我有一个用户集合。每个用户可以有多个朋友,其_id值存储在其用户文档的数组中 我想呈现一个列表,其中包含用户的朋友姓名列表,在每个姓名下,我想有一个位置,但位置在另一个文档中,因为每个用户可以有多个位置,而不受数量限制,比如他们访问过的/最喜欢的地方 这是两个独立的查询,但我需要以某种方式将它们组合起来 这是一个查询,它查看用户朋友数组中的用户ID值,然后根据ID获取相关的用户信息 friends = await User.findOne({ _id: userId }) .populate
friends = await User.findOne({ _id: userId })
.populate("friends", "name mobile", null, { sort: { name: 1 } })
.exec();
然后,要获取特定用户的位置,请执行以下操作:
const place = await Place.find({ creator: userId });
所以,我基本上想在一个循环中列出朋友,每个人的名字下面都有他们的位置,比如:
Joe Soap
- London Bridge
- Eiffel Tower
Bob Builder
- Marienplatz
mongoDb中的数据如下所示:
用户:
{
"_id": {
"$oid": "5f2d9ec5053d4a754d6790e8"
},
"friends": [{
"$oid": "5f2da51e053e4a754d5790ec"
}, {
"$oid": "5f2da52e053d4a754d6790ed"
}],
"name": "Bob",
"email": "bob@gmail.com",
"created_at": {
"$date": "2020-08-07T18:34:45.781Z"
}
}
{
"_id": {
"$oid": "5f3695d79864bd6c7c94e38a"
},
"location": {
"latitude": -12.345678,
"longitude": 12.345678
},
"creator": {
"$oid": "5f2da51e053e4a754d5790ec"
},
}
位置:
{
"_id": {
"$oid": "5f2d9ec5053d4a754d6790e8"
},
"friends": [{
"$oid": "5f2da51e053e4a754d5790ec"
}, {
"$oid": "5f2da52e053d4a754d6790ed"
}],
"name": "Bob",
"email": "bob@gmail.com",
"created_at": {
"$date": "2020-08-07T18:34:45.781Z"
}
}
{
"_id": {
"$oid": "5f3695d79864bd6c7c94e38a"
},
"location": {
"latitude": -12.345678,
"longitude": 12.345678
},
"creator": {
"$oid": "5f2da51e053e4a754d5790ec"
},
}
第一个连接基本上是从同一用户集合中的数组获取数据。第二个是从另一个集合,地点获取数据 更新:几乎正常运行
friends = await User.aggregate([
{ $match: { _id: new mongoose.Types.ObjectId(userId) } },
{
$lookup: {
from: "users",
localField: "friends",
foreignField: "_id",
as: "friends_names",
},
},
{ $unwind: "$friends_names" },
{
$lookup: {
from: "places",
localField: "friends",
foreignField: "creator",
as: "friends_places",
},
},
{ $unwind: "$friends_places" },
{
$project: {
"friends_names.name": 1,
"friends_places.saved_at": 1,
},
},
]);
返回的数据:
[
{
_id: 5f2d9ec5053d4a754d6790e8,
friends_names: { name: 'Bob' },
friends_places: { saved_at: 2020-08-17T13:40:28.334Z }
},
{
_id: 5f2d9ec5053d4a754d6790e8,
friends_names: { name: 'Natalie' },
friends_places: { saved_at: 2020-08-17T13:40:28.334Z }
}
]
编辑好的,我相信我已经正确地反向设计了您的最小模式:
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/test", { useNewUrlParser: true });
mongoose.set("debug", true);
const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", async function () {
await mongoose.connection.db.dropDatabase();
// we're connected!
console.log("Connected");
const userSchema = new mongoose.Schema({
friends: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
name: String,
});
const placesSchema = new mongoose.Schema({
latitude: String,
creator: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
});
const User = mongoose.model("User", userSchema);
const Place = mongoose.model("Place", placesSchema);
const bob = new User({ name: "Bob", friends: [] });
await bob.save();
const natalie = new User({ name: "Natalie", friends: [bob] });
await natalie.save();
//const chris = new User({ name: "Chris", friends: [] });
//await chris.save();
const john = new User({ name: "John", friends: [natalie, bob] });
await john.save();
const place1 = new Place({ latitude: "Place1", creator: bob });
const place3 = new Place({ latitude: "Place3", creator: bob });
const place2 = new Place({ latitude: "Place2", creator: natalie });
await place1.save();
await place2.save();
await place3.save();
await User.find(function (err, users) {
if (err) return console.error(err);
console.log(users);
});
await Place.find(function (err, places) {
if (err) return console.error(err);
//console.log(places);
});
var cc = await mongoose
.model("User")
.aggregate([
{ $match: { _id: john._id } },
{ $unwind: "$friends" },
{
$lookup: {
from: "places",
localField: "friends",
foreignField: "creator",
as: "friends_places",
}
}, { $lookup: {from: 'users', localField: 'friends', foreignField: '_id', as: 'friend_name'} },//, { $include: 'friends' }
{ $unwind: "$friends_places" }, { $unwind: "$friend_name" }//, { $skip: 1}, {$limit: 1}
])
.exec();
console.log(cc);
});
这是最相关的部分:
var cc = await mongoose
.model("User")
.aggregate([
{ $match: { _id: john._id } },
{ $unwind: "$friends" },
{
$lookup: {
from: "places",
localField: "friends",
foreignField: "creator",
as: "friends_places",
}
}, { $lookup: {from: 'users', localField: 'friends', foreignField: '_id', as: 'friend_name'} },//, { $include: 'friends' }
{ $unwind: "$friends_places" }, { $unwind: "$friend_name" }//, { $skip: 1}, {$limit: 1}
])
.exec();
第一个展开:friends
首先“展平”收藏。因此,基本上我们为每个用户和朋友都提供了“userId | friendId”。然后,对于每一行,我们只需查找他创建的位置($lookup)。最后,我们释放好友位置,因为我们不希望它们在控制台输出中呈现为[object]。此外,这里还有一个$match
,因为我们只想检查一个用户的朋友的位置。考虑到我们还想知道朋友的名字,我们必须再做一次加入
——这就是为什么会有第二次$lookup
。之后,一个简单的$unwind
来获取朋友的详细信息,就这样
代码产生以下结果:
[ { _id: 5f3aa3a9c6140e3344c78a45,
friends: 5f3aa3a9c6140e3344c78a44,
name: 'John',
__v: 0,
friends_places:
{ _id: 5f3aa3a9c6140e3344c78a48,
latitude: 'Place2',
creator: 5f3aa3a9c6140e3344c78a44,
__v: 0 },
friend_name:
{ _id: 5f3aa3a9c6140e3344c78a44,
friends: [Array],
name: 'Natalie',
__v: 0 } },
{ _id: 5f3aa3a9c6140e3344c78a45,
friends: 5f3aa3a9c6140e3344c78a43,
name: 'John',
__v: 0,
friends_places:
{ _id: 5f3aa3a9c6140e3344c78a46,
latitude: 'Place1',
creator: 5f3aa3a9c6140e3344c78a43,
__v: 0 },
friend_name:
{ _id: 5f3aa3a9c6140e3344c78a43,
friends: [],
name: 'Bob',
__v: 0 } },
{ _id: 5f3aa3a9c6140e3344c78a45,
friends: 5f3aa3a9c6140e3344c78a43,
name: 'John',
__v: 0,
friends_places:
{ _id: 5f3aa3a9c6140e3344c78a47,
latitude: 'Place3',
creator: 5f3aa3a9c6140e3344c78a43,
__v: 0 },
friend_name:
{ _id: 5f3aa3a9c6140e3344c78a43,
friends: [],
name: 'Bob',
__v: 0 } } ]
所以,我们有一张约翰朋友的住处的单子
重要提示:
from:places
在$lookup
中非常重要,因为我们必须使用MongoDB的集合名称,而不是模型名称。第一个连接基本上是从同一用户集合中的数组获取数据。第二个是从另一个集合places获取数据。再次感谢,我尝试了你的代码,得到了一个错误无法读取未定义的属性“friends”
,但当然,在你的情况下,你尝试了User.aggregate
和Place
,而不是查询中的Place
,是吗?我用大写的Place和Place:friends=await User.aggregate([{$lookup:{from:“Place”,localField:“friends”,foreignField:“creator”,as:“friends_places”,},},])。findOne({{id:userId})
再次感谢,我们越来越近了!现在的问题是,它没有列出朋友的名字,只有当前用户的名字。我需要显示朋友的姓名和位置。在我最初的问题查询中使用“填充”,我可以得到朋友的名字,但不能得到位置数据。现在我有位置数据和你的代码,但没有朋友的名字