在MongoDB中查询两个集合以获取分页搜索结果的最有效方法是什么?



这是场景:

  • 有两个集合
  • 第一个集合与第二个集合具有一对多关系。第二个集合与第一个集合一对一。
  • 查询对 3 个字段执行,所有字段都是索引
  • 其中 2 个索引位于第一个集合上,1 个位于第二个集合上
  • 结果需要支持分页

目前我能想到的最好的是使用聚合。阶段如下所示:

集合体 -> 第一个集合上的 2 个索引值匹配 ->排序 ->使用管道进行查找,该管道在两个集合中的关系属性上都匹配,并根据第二个集合中索引值的潜在搜索值进行匹配 ->与使用正则表达式查看第一个集合中的 2 个搜索字段的 OR 匹配,或者查找中的项目是否包含任何结果 ->限制 ->有价值的项目

问题是,在查找过程中,搜索将对第一个集合中的所有文档与第二个集合进行联接。请记住,搜索的所有内容都是索引,但查找是这里的主要关注点。建议以正确的方式做到这一点?更好的方法?

代码示例:

db.collection1.aggregate( [
{ 
$match: { // initial filters based on indexed values
field1: "somevalue", 
field2: "somevalue" 
},
},
{
$sort: {
firstSortField: -1, _id: -1 // sort results by needed order
}
},
{
$lookup: // join with another collection to search on a specific value
{
from: collection2,
localField: someLocalField,
foreignField: someForeignField,
as: "someJoinedFields"
}
},
{
$addFields: {
extraField: ["$someJoinedFields.someExtaField"] // add potential array of values
}
},
{
$match: (
{
$or: [
{ field3: {$regex: ""}}, // potential search field
{ field4: {$regex: ""}}, // potential search field
{ extraField: {$regex: ""}} // potential search field
]
}
)
},
{
$limit: 100 // limit to 100 results for pagination
},
{
$project: { // final results
finalField: 1,
finalField2: 1,
finalField3: 1
}
}
])

问题

遗憾的是,由于以下原因,您的架构不能有效地满足您的需求:

  • 分页需要对集合进行不可变的排序,以跟踪最后一个元素,并确保没有跳过或重复任何元素
    • 你用_id来做,这很好,因为它保证是独一无二的。
    • 您不会跟踪最后一个元素(基本上在您的示例中使用$skip)
  • 查找应在$limit后完成(如您:D所述)
    • 通过这样做,您可以避免合并许多元素。(它变得非常慢!
  • $limit后不应进行任何匹配(就像您目前:D所做的那样)
    • 如果你把一个匹配项放在后面,你不会保留你想要的元素数量

基本上,你问的是一些允许你在$lookup之前做$skip$match$skip之前,在$skip之前做$lookup的事情。这不可能!

解决方案

想到的所有解决方案实际上都很难实施。

使您的数据库嵌入

https://docs.mongodb.com/manual/tutorial/model-embedded-one-to-many-relationships-between-documents/

MongoDB中最好的事情之一是更改文档结构是多么容易。如果可以这样做,同时又不破坏其他功能,请这样做。通过嵌入文档,您不再需要$lookup,从而完全消除了问题(然后您甚至可以使限制更大,因为唯一"缓慢"的事情将是收集阶段)

创建实例化视图

https://docs.mongodb.com/manual/core/materialized-views/

这将允许您在具有嵌入式查询速度的同时不更改原始结构。此方法会减慢您的写入速度,因为您必须在每次插入或编辑其中一个集合时重新创建视图,但读取会更快

蒙戈5改进$lookup

https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#correlated-subqueries-using-concise-syntax

使用此方法,您将合并更少的文档,并且可以轻松丢弃它们。

概括

何时更改结构

要走的方法是更改数据库结构。我理解,由于各种原因,这是不可能的。

何时使用实例化视图

您没有很多写入操作,也没有空间问题(因为这将占用双倍的空间)

何时使用$lookup

如果无法更改结构且不允许较慢的写入,请使用查找。这是迄今为止 3 种方法中最慢的,但仍然会给你带来提升。从长远来看,这种方法可能根本不是解决方案,这完全取决于您的用例:D

最新更新