文档菜单
文档首页
/
MongoDB 手册
/ /

避免无界数组

在本页

  • 示例
  • 子集模式
  • 参考数据
  • 使用 $lookup 在数组字段上连接
  • 了解更多

将数组作为字段值存储允许您嵌入数据并确保一起访问的数据存储在一起。然而,如果您不限制数组中的元素数量,您的文档可能会超过16MB BSON 文档大小限制。无界数组可能会耗尽应用程序资源并降低索引性能。

而不是嵌入整个数据集,您可以使用子集和引用来限制数组,这可以提高性能并保持可管理的文档大小。当您子集数据时,您只选择需要处理的数据的必要部分,这通过仅关注相关数据来减少内存使用和处理时间。当您引用数据时,您链接到外部数据源而不是直接在文档中嵌入它们。这种方法提高了性能并减少了文档大小。通过使用子集和引用,您可以限制数组并更有效地管理数据。

考虑以下模式,该模式跟踪书店应用的图书评论。初始模式使用数组来表示reviews 字段。

{
title: "Harry Potter",
author: "J.K. Rowling",
publisher: "Scholastic",
reviews: [
{
user: "Alice",
review: "Great book!",
rating: 5
},
{
user: "Bob",
review: "Didn't like it!",
rating: 1
},
{
user: "Charlie",
review: "Not bad, but could be better.",
rating: 3
}
]
}

在此模式中,reviews 字段是一个无界数组。每次为这本书创建新的评论时,应用程序都会将新的子文档添加到 reviews 数组中。随着更多评论的添加,数组可能会变得过大,从而耗尽应用程序资源。

在此示例中,书店应用只需要显示每本书的三个评论。为了避免无界数组,您可以使用子集设计模式或 文档引用,具体取决于您的用例。

当您需要快速访问不常更新的数据时,使用子集模式最为合适。使用子集模式,您可以将文档中的三个评论嵌入到书中,以单个操作返回所有所需信息。其他评论存储在单独的 reviews 集合中。此模式设计提供了以下好处

  • 消除无界数组

  • 控制文档大小

  • 避免使用多个查询

books 集合

db.books.insertOne( [
{
title: "Harry Potter",
author: "J.K. Rowling",
publisher: "Scholastic",
reviews: [
{
reviewer: "Alice",
review: "Great book!",
rating: 5
},
{
reviewer: "Charlie",
review: "Didn't like it.",
rating: 1
},
{
reviewer: "Bob",
review: "Not bad, but could be better.",
rating: 3
}
],
}
] )

reviews 集合

db.reviews.insertMany( [
{
reviewer: "Jason",
review: "Did not enjoy!",
rating: 1
},
{
reviewer: "Pam",
review: "Favorite book!",
rating: 5
},
{
reviewer: "Bob",
review: "Not bad, but could be better.",
rating: 3
}
] )

这种方法会重复数据,导致更新成本高昂。如果评论不经常更新,此方法最佳。

当您需要管理大或频繁更新的数据集而不增加文档大小时,参考数据最为合适。

要参考数据,将评论存储在单独的集合中,并在 reviews 集合的文档中添加一个 review_id 字段。使用 review_id 字段在 books 集合中引用评论。

这种方法解决了无界数组的问题,但因为它需要查询 reviews 集合来检索 books 集合的评论信息,所以引入了延迟。根据您的用例,这种额外的延迟可能是为了避免无界数组引起的问题而可以接受的权衡。

books 集合

db.books.insertMany( [
{
title: "Harry Potter",
author: "J.K. Rowling",
publisher: "Scholastic",
reviews: ["review1", "review2", "review3"]
},
{
title: "Pride and Prejudice",
author: "Jane Austen",
publisher: "Penguin",
reviews: ["review4", "review5"]
}
] )

reviews 集合

db.reviews.insertMany( [
{
review_id: "review1",
reviewer: "Jason",
review: "Did not enjoy!",
rating: 1
},
{
review_id: "review2",
reviewer: "Pam",
review: "Favorite book!",
rating: 5
},
{
review_id: "review3",
reviewer: "Bob",
review: "Not bad, but could be better.",
rating: 3
},
{
review_id: "review4",
reviewer: "Tina",
review: "Amazing!",
rating: 5
},
{
review_id: "review5",
reviewer: "Jacob",
review: "A little overrated",
rating: 4,
}
] )

如果您的 书籍评论 信息存储在单独的集合中,应用程序需要执行 $lookup 操作来连接数据。

以下聚合操作连接了前面示例中的 书籍评论 集合。

db.books.aggregate( [
{
$lookup: {
from: "reviews",
localField: "reviews",
foreignField: "review_id",
as: "reviewDetails"
}
}
] )

该操作返回以下内容

[
{
_id: ObjectId('665de81eeda086b5e22dbcc9'),
title: 'Harry Potter',
author: 'J.K. Rowling',
publisher: 'Scholastic',
reviews: [ 'review1', 'review2', 'review3' ],
reviewDetails: [
{
_id: ObjectId('665de82beda086b5e22dbccb'),
review_id: 'review1',
reviewer: 'Jason',
review: 'Did not enjoy!',
rating: 1
},
{
_id: ObjectId('665de82beda086b5e22dbccc'),
review_id: 'review2',
reviewer: 'Pam',
review: 'Favorite book!',
rating: 5
},
{
_id: ObjectId('665de82beda086b5e22dbccd'),
review_id: 'review3',
reviewer: 'Bob',
review: 'Not bad, but could be better.',
rating: 3
} ]
},
{
_id: ObjectId('665de81eeda086b5e22dbcca'),
title: 'Pride and Prejudice',
author: 'Jane Austen',
publisher: 'Penguin',
reviews: [ 'review4', 'review5' ],
reviewDetails: [
{
_id: ObjectId('665de82beda086b5e22dbcce'),
review_id: 'review4',
reviewer: 'Tina',
review: 'Amazing!',
rating: 5
},
{
_id: ObjectId('665de82beda086b5e22dbccf'),
review_id: 'review5',
reviewer: 'Jacob',
review: 'A little overrated',
rating: 4
} ]
}
]

在这个例子中,$lookup 操作使用书籍文档中的 评论 数组以及评论文档中的 review_id 字段将 书籍 集合与 评论 集合连接起来。reviewDetails 文档存储了合并后的数据。

返回

模式设计反模式