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

Sto*_*its 6 database mongoose mongodb nosql

这是场景:

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

目前我能想到的最好的方法是使用聚合。这些阶段看起来像这样:

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

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

代码示例:

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
        }
    }
 ])
Run Code Online (Sandbox Code Playgroud)

And*_*rea 2

问题

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

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

$skip基本上,你问的问题可以让你在 before $lookup$matchbefore $skip$lookupbefore 之前做$skip。这不可能!

解决方案

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

使您的数据库嵌入

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

MongoDB 中最好的事情之一就是更改文档结构是多么容易。如果可以的话,在不破坏其他功能的情况下,就这样做。通过嵌入文档,您不再需要它$lookup,完全消除问题,(您甚至可以使限制更大,因为唯一“慢”的事情将是收集阶段)

创建物化视图

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

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

Mongo 5 改进$lookup

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

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

总结

何时改变结构

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

何时使用物化视图

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

何时使用 $lookup

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