英文:
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'
}}])
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论