1. ReferenceField
- 功能:用于在一个文档中引用另一个文档,类似于关系型数据库中的外键。
- 存储方式:存储被引用文档的 ObjectId。
- 查询行为:当访问该字段时,直接加载被引用的目标文档。
- 适用场景:适合用于多对一关系或文档之间有独立管理需求的情况。
特点:
- 引用的目标文档保存在单独的集合中。
- 目标文档独立存在,可以被其他文档引用。
- 访问引用字段时会触发数据库查询,从而获取目标文档的数据。
示例:
from mongoengine import Document, StringField, ReferenceField
class Author(Document):
name = StringField()
class Book(Document):
title = StringField()
author = ReferenceField(Author) # 引用 Author 文档
使用:
# 创建引用关系
author = Author(name="John Doe").save()
book = Book(title="My Book", author=author).save()
# 获取数据
print(book.author.name) # 访问时查询 Author 集合,输出 "John Doe"
2. EmbeddedDocumentField
- 功能:用于在文档中嵌套另一个文档,直接将嵌套文档作为字段存储在父文档中。
- 存储方式:嵌套文档数据直接存储在父文档的 BSON 中。
- 查询行为:父文档查询时,嵌套文档的数据会随父文档一起返回。
- 适用场景:适用于一对一或一对多关系,且嵌套文档与父文档紧密相关,不需要独立查询。
特点:
- 嵌套文档的数据是父文档的一部分,查询父文档时自动返回嵌套数据。
- 嵌套文档不能被其他文档引用。
- 文档大小可能变大,适合子文档较小的场景。
示例:
from mongoengine import Document, StringField, EmbeddedDocument, EmbeddedDocumentField
class Address(EmbeddedDocument):
city = StringField()
street = StringField()
class User(Document):
name = StringField()
address = EmbeddedDocumentField(Address) # 嵌套 Address 文档
使用:
# 嵌套文档数据存储
address = Address(city="New York", street="5th Avenue")
user = User(name="Alice", address=address).save()
# 获取数据
print(user.address.city) # 输出 "New York"
3. LazyReferenceField
- 功能:与
ReferenceField
类似,但支持延迟加载,被引用的目标文档只有在访问时才会触发查询。 - 存储方式:存储被引用文档的 ObjectId。
- 查询行为:当访问该字段时,延迟加载目标文档数据。
- 适用场景:适合需要引用其他文档,但在大部分情况下不访问引用文档的场景,优化性能。
特点:
- 引用目标文档的方式与
ReferenceField
相同,但支持延迟加载。 - 访问引用字段时会触发查询;如果不访问,则不会加载目标文档数据。
- 提升性能,减少数据库查询次数。
示例:
from mongoengine import Document, StringField, LazyReferenceField
class Author(Document):
name = StringField()
class Book(Document):
title = StringField()
author = LazyReferenceField(Author) # 延迟加载 Author 文档
使用:
# 创建引用关系
author = Author(name="Jane Doe").save()
book = Book(title="Another Book", author=author).save()
# 延迟加载目标文档
print(book.author.fetch().name) # fetch() 查询 Author 文档,输出 "Jane Doe"
三者比较总结
特性 | ReferenceField | EmbeddedDocumentField | LazyReferenceField |
---|---|---|---|
存储方式 | 存储目标文档的 ObjectId |
直接存储嵌套文档的数据 | 存储目标文档的 ObjectId |
查询行为 | 查询时直接加载目标文档 | 查询父文档时直接返回嵌套数据 | 延迟加载目标文档,只有在访问时才查询目标文档 |
性能 | 查询时需要额外加载目标文档 | 查询时目标文档数据已包含在父文档中 | 查询时不会加载目标文档,访问时才触发查询 |
适用关系 | 一对一、多对一 | 一对一、一对多 | 一对一、多对一 |
数据耦合性 | 父文档和目标文档独立,松耦合 | 父文档和嵌套文档紧密耦合 | 父文档和目标文档独立,松耦合 |
文档大小 | 文档大小较小,引用目标文档数据 | 文档大小可能较大,嵌套文档数据增加父文档大小 | 文档大小较小,引用目标文档数据 |
适用场景 | - 目标文档需要独立管理 - 数据访问较频繁 | - 父文档与子文档总是一起操作 - 数据嵌套结构较小 | - 延迟访问目标文档 - 优化性能,减少查询次数 |
推荐使用场景
- ReferenceField:
- 目标文档独立且可能被多次引用。
- 数据需要在多个地方管理,不适合嵌套存储的场景。
- 例如:用户(
User
)和文章(Article
)之间的关系。
- EmbeddedDocumentField:
- 父文档与子文档紧密关联,通常一起使用。
- 数据结构较小且耦合紧密,适合一对一、一对多的嵌套存储。
- 例如:用户(
User
)和地址(Address
)的关系。
- LazyReferenceField:
- 引用文档数据较大,访问不频繁,适合延迟加载。
- 需要减少数据库查询时的性能优化场景。
- 例如:订单(
Order
)引用的用户(User
)信息,但订单查询中不常用用户详情。