引言
在开发 Web 应用的过程中,我们常常需要对数据进行嵌套,以表现数据之间的逻辑关系。在 MongoDB 中,文档嵌套是一种常见的数据设计方式。而 Mongoose 是 MongoDB 的一个非常流行的 Node.js ORM 库,它封装了 MongoDB 的操作,提供了更加友好和稳定的数据访问接口。本文将介绍如何在 Mongoose 中使用文档嵌套,以及嵌套文档的一些坑点和实践经验。
文档嵌套的基本概念
文档嵌套是指在 MongoDB 的一个文档中,插入另一个文档作为某个字段的值。例如,一个 User 文档可能包含多个 Address 文档,每个 Address 包含 address_line1 和 address_line2 字段。那么在 MongoDB 中,我们可以这样定义一个 User 文档:
// www.javascriptcn.com code example
{
"name": "张三",
"addresses": [
{
"address_line1": "浙江省杭州市滨江区",
"address_line2": "长河街道200号"
},
{
"address_line1": "上海市浦东新区",
"address_line2": "唐镇万达广场"
}
]
}可以看到,addresses 字段是一个数组,它嵌套了多个 Address 文档。这种文档嵌套方式,在 MongoDB 中被称为 Embedded Document。
在 Mongoose 中使用文档嵌套
在 Mongoose 中使用文档嵌套,也非常简单。我们只需要在模型定义中,将嵌套的字段设置为 Schema.Types.Embedded。
// www.javascriptcn.com code example
const mongoose = require('mongoose');
const AddressSchema = new mongoose.Schema({
address_line1: String,
address_line2: String
});
const UserSchema = new mongoose.Schema({
name: String,
addresses: [AddressSchema]
});
const User = mongoose.model('User', UserSchema);上述代码中,我们定义了 User 模型,该模型中的 addresses 字段,使用了 AddressSchema ,即文档嵌套了 Address。在使用时,我们可以像普通字段一样使用 addresses 字段:
// www.javascriptcn.com code example
const user = new User({
name: '张三',
addresses: [
{
address_line1: '浙江省杭州市滨江区',
address_line2: '长河街道200号'
},
{
address_line1: '上海市浦东新区',
address_line2: '唐镇万达广场'
}
]
});
user.save(function (err) {
if (err) {
console.log(err);
} else {
console.log('保存成功!');
}
});坑点与实践经验
1. 文档嵌套的级数
在使用文档嵌套时,需要注意文档嵌套的级数。在 MongoDB 中,最多支持嵌套 100 级文档。如果超过了这个限制,MongoDB 将无法存储数据。而在 Mongoose 中,默认为 1 级,即只支持嵌套 1 级文档。如果需要支持更多级别的嵌套,需要手动设置 Schema 中的嵌套层数。
// www.javascriptcn.com code example
const AddressSchema = new mongoose.Schema({
address_line1: String,
address_line2: String
}, { _id: false });
const UserSchema = new mongoose.Schema({
name: String,
addresses: [AddressSchema],
friends: [{
name: String,
friends: [{
// ...
}]
}]
}, { _id: false });
UserSchema.add({
friends: [UserSchema]
}, '_id: false');上述代码中,我们定义了一个 User 模型,该模型中的 addresses 字段支持 1 级文档嵌套,而 friends 字段支持多级嵌套。可以看到,我们使用了 Schema.add() 方法,为 UserSchema 动态添加了 friends 字段,以支持多级文档嵌套。
2. subdocument 中的 _id 细节
在使用文档嵌套时,需要注意 subdocument 中的 _id 细节。在 MongoDB 中,默认情况下,每个文档都会有一个唯一的 _id 字段。但 subdocument 中的 _id 字段并不是必需的。如果不需要 _id 字段,则可以在 Schema 中设置 _id:false 选项。
// www.javascriptcn.com code example
const AddressSchema = new mongoose.Schema({
address_line1: String,
address_line2: String
}, { _id: false });
const UserSchema = new mongoose.Schema({
name: String,
addresses: [AddressSchema]
});上述代码中,我们定义了一个 Address 模型,该模型中的文档不需要 _id 字段。
3. populate 的使用
当 subdocument 嵌套过深时,在查询中使用 populate 可能会失效。例如,我们查询一个 User,想要同时获取其所有 Address 的详细信息:
User.findById(userId).populate('addresses').exec(function(err, user) {
if (err) {
console.log(err);
} else {
console.log(user);
}
});可以看到,上述代码使用了 populate 方法,将 addresses 字段从 ID 转换为了实际的 Address 对象。这种方法在一级嵌套时非常好用,但在多级嵌套时可能会失效。
在多级嵌套时,我们可以手动查询每个级别的文档:
// www.javascriptcn.com code example
User.findById(userId, function(err, user) {
if (err) {
console.log(err);
} else {
Address.find({ _id: { $in: user.addresses } }, function(err, addresses) {
if (err) {
console.log(err);
} else {
user.addresses = addresses;
console.log(user);
}
});
}
});上述代码中,我们使用了两个查询,分别查询了 User 和 Address 对象,然后手动将两个对象关联起来。这种方法虽然会增加代码复杂度,但能够有效避免 populate 失效的问题。
小结
本文介绍了 MongoDB 中的 Embedded Document,以及在 Mongoose 中使用文档嵌套的方法和坑点。同时,我们也分享了一些文档嵌套的实践经验和技巧。在开发中,适当的文档嵌套可以让数据逻辑更加清晰和紧凑,代码更加精简和易读。希望本文能够对读者有所启发和帮助,欢迎大家多多交流和分享。
Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/678238ef935627c900fd38aa