英文:
How to get reference collection data in mongoose using lookup?
问题
代码示例已翻译如下:
查找:
======
const course = await mongoose.model('Course').aggregate()
.match({ _id: new mongoose.Types.ObjectId(id) })
.lookup({
from: 'User',
foreignField: '_id',
localField: 'userId',
as: 'user'
})
课程模式:
=============
const CourseSchema = new Schema({
title: String,
userId: {
type: mongoose.Types.ObjectId,
ref: 'User'
},
}, { strict: false });
预期输出:
===============
{
"course": [
{
"_id": "64b000c085e9b1aec5975249",
"title": "test",
"userId": "64affc47d81a4bfba92b5540",
"user": [], // 此处的数据应该存在但为空,为什么?
}
]
}
如何在上述输出中获取用户数据?
使用 populate 可行:
====================
const course = await mongoose.model('Course')
.find({ _id: new mongoose.Types.ObjectId(id) })
.populate('userId').exec();
但如何使用聚合管道实现这一点?
请注意,这些翻译只包括代码和标记的内容,不包括其他内容。如果您需要进一步的帮助或有其他问题,请随时提出。
英文:
Lookup:
const course = await mongoose.model('Course').aggregate()
.match({ _id: new mongoose.Types.ObjectId(id) })
.lookup({
from: 'User',
foreignField: '_id',
localField: 'userId',
as: 'user'
})
Course schema:
const CourseSchema = new Schema({
title: String,
userId: {
type: mongoose.Types.ObjectId,
ref: 'User'
},
}, { strict: false });
Expected output:
{
"course": [
{
"_id": "64b000c085e9b1aec5975249",
"title": "test",
"userId": "64affc47d81a4bfba92b5540",
"user": [], // data here should be present but empty why?
}
]
}
How do I get user data in the above output?
Works using populate:
const course = await mongoose.model('Course')
.find({ _id: new mongoose.Types.ObjectId(id) })
.populate('userId').exec();
But how to achieve this using aggregation pipeline?
答案1
得分: 1
集合名称是 users,而不是 User。请查看选项:collection
> 默认情况下,Mongoose通过将模型名称传递给 utils.toCollectionName 方法来生成集合名称。该方法会将名称变为复数形式。
让我们看看 utils.toCollectionName 方法:
exports.toCollectionName = function(name, pluralize) {
if (name === 'system.profile') {
return name;
}
if (name === 'system.indexes') {
return name;
}
if (typeof pluralize === 'function') {
return pluralize(name);
}
return name;
};
function pluralize(str) {
let found;
str = str.toLowerCase();
if (!~uncountables.indexOf(str)) {
found = rules.filter(function(rule) {
return str.match(rule[0]);
});
if (found[0]) {
return str.replace(found[0][0], found[0][1]);
}
}
return str;
}
请查看完整源代码
正如您所看到的,模型名称 User 将被转换为集合名称 users。因此,在 lookup() 方法中应该是 from: "users" 而不是 from: "User"。
localField 和 foreignField 是错误的。localField 是本地集合中的字段,即 cources。foreignField 是外部集合中的字段,即 users。
from: <外部集合>localField: <本地集合文档中的字段>,foreignField: <外部集合文档中的字段>
一个有效的示例:
import mongoose from 'mongoose';
import util from 'util';
import { config } from '../../config';
mongoose.set('debug', true);
console.log(mongoose.version);
const UserSchema = new mongoose.Schema({ name: String });
const User = mongoose.model('User', UserSchema);
const CourseSchema = new mongoose.Schema(
{
title: String,
userId: {
type: mongoose.Types.ObjectId,
ref: 'User',
},
},
{ strict: false },
);
const Course = mongoose.model('Course', CourseSchema);
(async function main() {
try {
await mongoose.connect(config.MONGODB_URI);
// seed
const [u1] = await User.create([{ name: 'user-a' }]);
const [c] = await Course.create([{ title: 'course a', userId: u1 }]);
const r = await Course.aggregate()
.match({ _id: c?._id })
.lookup({
from: 'users',
localField: 'userId',
foreignField: '_id',
as: 'user',
});
console.log(util.inspect(r, false, null));
} catch (error) {
console.error(error);
} finally {
await mongoose.connection.close();
}
})();
日志:
7.3.2
Mongoose: users.insertOne({ name: 'user-a', _id: ObjectId("64b0e2768c75418f819995af"), __v: 0 }, {})
Mongoose: courses.insertOne({ title: 'course a', userId: ObjectId("64b0e2768c75418f819995af"), _id: ObjectId("64b0e2778c75418f819995b1"), __v: 0}, {})
Mongoose: courses.aggregate([ { '$match': { _id: new ObjectId("64b0e2778c75418f819995b1") } }, { '$lookup': { from: 'users', localField: 'userId', foreignField: '_id', as: 'user' } }], {})
[
{
_id: new ObjectId("64b0e2778c75418f819995b1"),
title: 'course a',
userId: new ObjectId("64b0e2768c75418f819995af"),
__v: 0,
user: [
{
_id: new ObjectId("64b0e2768c75418f819995af"),
name: 'user-a',
__v: 0
}
]
}
]
英文:
The collection name is users, not User. See option: collection
> Mongoose by default produces a collection name by passing the model name to the utils.toCollectionName method. This method pluralizes the name.
Let's see the utils.toCollectionName method:
exports.toCollectionName = function(name, pluralize) {
if (name === 'system.profile') {
return name;
}
if (name === 'system.indexes') {
return name;
}
if (typeof pluralize === 'function') {
return pluralize(name);
}
return name;
};
function pluralize(str) {
let found;
str = str.toLowerCase();
if (!~uncountables.indexOf(str)) {
found = rules.filter(function(rule) {
return str.match(rule[0]);
});
if (found[0]) {
return str.replace(found[0][0], found[0][1]);
}
}
return str;
}
See full source code
As you can see, the model name 'User' will be converted to the collection name "users". So it should be from: "users", not from: "User" in lookup() method.
The localField and foreignField are wrong. The localField is the field in the local collection which is cources. The foreignField is the field in the foreign collection which is users.
from: <foreign collection>localField: <field from local collection's documents>,foreignField: <field from foreign collection's documents>
A working example:
import mongoose from 'mongoose';
import util from 'util';
import { config } from '../../config';
mongoose.set('debug', true);
console.log(mongoose.version);
const UserSchema = new mongoose.Schema({ name: String });
const User = mongoose.model('User', UserSchema);
const CourseSchema = new mongoose.Schema(
{
title: String,
userId: {
type: mongoose.Types.ObjectId,
ref: 'User',
},
},
{ strict: false },
);
const Course = mongoose.model('Course', CourseSchema);
(async function main() {
try {
await mongoose.connect(config.MONGODB_URI);
// seed
const [u1] = await User.create([{ name: 'user-a' }]);
const [c] = await Course.create([{ title: 'course a', userId: u1 }]);
const r = await Course.aggregate()
.match({ _id: c?._id })
.lookup({
from: 'users',
localField: 'userId',
foreignField: '_id',
as: 'user',
});
console.log(util.inspect(r, false, null));
} catch (error) {
console.error(error);
} finally {
await mongoose.connection.close();
}
})();
Logs:
7.3.2
Mongoose: users.insertOne({ name: 'user-a', _id: ObjectId("64b0e2768c75418f819995af"), __v: 0 }, {})
Mongoose: courses.insertOne({ title: 'course a', userId: ObjectId("64b0e2768c75418f819995af"), _id: ObjectId("64b0e2778c75418f819995b1"), __v: 0}, {})
Mongoose: courses.aggregate([ { '$match': { _id: new ObjectId("64b0e2778c75418f819995b1") } }, { '$lookup': { from: 'users', localField: 'userId', foreignField: '_id', as: 'user' } }], {})
[
{
_id: new ObjectId("64b0e2778c75418f819995b1"),
title: 'course a',
userId: new ObjectId("64b0e2768c75418f819995af"),
__v: 0,
user: [
{
_id: new ObjectId("64b0e2768c75418f819995af"),
name: 'user-a',
__v: 0
}
]
}
]
答案2
得分: 0
如果在Course集合中,您的userId是objectId而不是普通字符串,您可以按照以下查询进行聚合:
db.Course.aggregate([{$match:{_id:ObjectId("yourId")}}
,{$lookup:{
from: 'User',
localField: 'userId',
foreignField: "_id",
as: 'users'
}}])
如果您的userId是普通字符串,您需要使用$addFields将您的字符串id转换为objectId。
db.Course.aggregate([{$match:{_id:ObjectId("yourId")}}
,{$addFields:{'userId':{"$toObjectId":"$userId"}}}
,{$lookup:{
from: 'User',
localField: 'userId',
foreignField: "_id",
as: 'users'
}}])
英文:
If your userId in Course collection is objectId instead of plain String
you can aggregate result by following query,
db.Course.aggregate([{$match:{_id:ObjectId("yourId")}}
,{$lookup:{
from: 'User',
localField: 'userId',
foreignField: "_id",
as: 'users'
}}])
if you have userId as plain String you need to use $addFields to convert your string id to ObjectId.
db.Course.aggregate([{$match:{_id:ObjectId("yourId")}}
,{$addFields:{'userId':{"$toObjectId":"$userId"}}}
,{$lookup:{
from: 'User',
localField: 'userId',
foreignField: "_id",
as: 'users'
}}])
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论