【问题标题】:MongoDB match on document and subdocuments, what to use as indexes?MongoDB 匹配文档和子文档,使用什么作为索引?
【发布时间】:2020-06-28 07:56:32
【问题描述】:

我有很多这样的文件:

[{
        "title": "Luxe [daagse] [verzorging] @ Egypte! Incl. vluchten, transfers & 4* ho",
        "price": 433,
        "automatic": false,
        "destination": "5d26fc92f72acc7a0b19f2c4",
        "date": "2020-01-19T00:00:00.000+00:00",
        "days": 8,
        "arrival_airport": "5d1f5b407ec7385fa2963623",
        "departure_airport": "5d1f5adb7ec7385fa2963307",
        "board_type": "5d08e1dfff6c4f13f6db1e6c"
    },
    {
        "title": "Luxe [daagse] [verzorging] @ Egypte! Incl. vluchten, transfers & 4* ho",
        "automatic": true,
        "destination": "5d26fc92f72acc7a0b19f2c4",
        "prices": [{
                "price": 433,
                "date_from": "2020-01-19T00:00:00.000+00:00",
                "date_to": "2020-01-28T00:00:00.000+00:00",
                "day_count": 8,
                "arrival_airport": "5d1f5b407ec7385fa2963623",
                "departure_airport": "5d1f5adb7ec7385fa2963307",
                "board_type": "5d08e1dfff6c4f13f6db1e6c"
            },
            {
                "price": 899,
                "date_from": "2020-04-19T00:00:00.000+00:00",
                "date_to": "2020-04-28T00:00:00.000+00:00",
                "day_count": 19,
                "arrival_airport": "5d1f5b407ec7385fa2963623",
                "departure_airport": "5d1f5adb7ec7385fa2963307",
                "board_type": "5d08e1dfff6c4f13f6db1e6c"
            }
        ]
    }
]

如您所见,自动交易有多个价格(可能很多,在 1000 到 4000 之间)并且没有可用的原始字段。

现在我需要在原始文档以及子文档中搜索以查找匹配项。

这是我用来搜索文档的聚合:

[{
    "$match": {
        "destination": {
            "$in": ["5d26fc9af72acc7a0b19f313"]
        }
    }
}, {
    "$match": {
        "$or": [{
            "prices": {
                "$elemMatch": {
                    "price": {
                        "$lte": 1500,
                        "$gte": 400
                    },
                    "date_to": {
                        "$lte": "2020-04-30T22:00:00.000Z"
                    },
                    "date_from": {
                        "$gte": "2020-03-31T22:00:00.000Z"
                    },
                    "board_type": {
                        "$in": ["5d08e1bfff6c4f13f6db1e68"]
                    }
                }
            }
        }, {
            "price": {
                "$lte": 1500,
                "$gte": 400
            },
            "date": {
                "$lte": "2020-04-30T22:00:00.000Z",
                "$gte": "2020-03-31T22:00:00.000Z"
            },
            "board_type": {
                "$in": ["5d08e1bfff6c4f13f6db1e68"]
            }
        }]
    }
}, {
    "$limit": 20
}]

我想加快速度,因为它可能会很慢。我想知道,这个聚合的最佳索引策略是什么,我使用哪些字段?这是最好的方法还是有更好的方法?

【问题讨论】:

  • 它确实有效,也许我复制了错误的东西。你可以像这样使用 $in 吗? docs.mongodb.com/manual/reference/operator/query/in
  • 没错,但在$match 中我们必须使用查询。我的聚合有效,所以请不要担心。我们可以专注于这个问题吗?
  • 一般来说,当你有不同的值时,索引是最足够的。在这种情况下,我会尝试为 createIndex( {"prices.board_type": 1} ) 创建索引

标签: mongodb indexing aggregation-framework


【解决方案1】:

需要包含匹配过滤器字段的复合索引才能使聚合快速运行。在聚合查询中,如果在过滤器字段上定义了索引,则在管道的早期(最好是第一阶段)拥有$match 阶段会利用索引。在发布的查询中就是这样,快速查询都需要定义索引。但是,索引哪些字段?

索引将是复合索引;即,对查询条件的多个字段进行索引。索引前缀以destination 字段开头。剩余的索引字段有待确定。剩下的字段是什么?

其中大部分字段位于 prices 数组的子文档字段 - pricedate_fromdate_toboard_type。主文档中还有 date 字段。复合索引中需要用到哪些字段?

在数组元素(或数组中子文档的字段)上定义索引会创建大量索引键。这意味着大量的存储空间和使用内存(或 RAM)的索引。这是一个重要的考虑因素。数组元素上的索引称为多键索引。要正确使用索引,集合的文档和查询使用的索引(统称为工作集)必须适合 RAM。

您需要考虑的另一个方面是查询选择性。使用索引字段的过滤器选择了多少文档是一个因素。过滤字段必须选择一小部分输入文档才能生效。见Create Queries that Ensure Selectivity

根据以上两个因素,很难确定还需要考虑哪些其他字段(确定prices的一些字段)。所以,索引将是这样的:

{ destination: 1, fld1: 1, fld2: 1, ... }

fld1fld2、...将是 prices 数组子文档字段和/或 date 字段。我认为只有一组日期字段可以与索引一起使用。示例索引可以是以下之一:

{ destination: 1, date: 1,  "prices.price": 1, "prices.board_type": 1}
{ destination: 1, "prices.price": 1, "prices.date_from": 1, "prices.date_to": 1, "prices.board_type": 1}

注意索引键orderpricedate_fromdate_toboard_type必要性是根据这两个来确定的主要因素 - 工作集的要求和查询选择性 - 这很重要。

注意:在一个结构相似的小样本数据集上,显示了复合索引的使用情况,主要是 destination 字段和来自 prices 的两个字段(一个具有相等条件,一个具有范围条件)。使用explain查询计划在复合索引上显示了 IXSCAN(索引扫描),使用索引肯定会提高查​​询性能。

【讨论】:

    【解决方案2】:

    来自 Mongo 的 $or 文档:

    当评估 $or 表达式中的子句时,MongoDB 要么执行集合扫描,要么,如果索引支持所有子句,则 MongoDB 执行索引扫描。也就是说,为了让 MongoDB 使用索引来评估 $or 表达式,$or 表达式中的所有子句都必须由索引支持。否则,MongoDB 将执行集合扫描。

    因此,考虑到这一点,为了避免在此管道中进行集合扫描,您必须创建一个包含 priceprices 字段的 compound index

    请记住,复合索引中的顺序很重要,因此字段的顺序应根据您可能的使用情况而有所不同。

    在我看来,您要创建的索引类似于:

    {destination: 1, date: 1, board_type: 1, price: 1, prices: 1}
    

    【讨论】:

    • 当您使用prices: 1 时,是通配符还是什么?例如dateboard_type 字段怎么样,我还需要在它们上设置索引吗?我发现很难考虑设置什么索引,因为访问者可以选择他想要使用的过滤器类型。有时,访问者在 priceboard_type 上进行过滤,而其他时候访问者在 date / date_from / date_toprice 上进行过滤。
    • prices: 1 只是表示它的升序索引,您确实需要添加 dateboard_type 是的,我忘记了。如果您关心所有这些查询的性能,您应该考虑所有可能的情况并创建一个索引以适应每种情况。请记住,您也许可以对两个不同的查询使用相同的索引。
    • 我不确定您是否可以创建价格指数(如果有数千个)Index Limit
    • 视情况而定,有些文档有 300 个价格子文档,有些有 3000 个。看起来我可以在尝试时创建价格索引。
    • @tomslabbaert 我不需要指定要索引的子文档中的字段吗?我必须手动指定很多不同的索引,有更快或更好的替代方法吗?
    猜你喜欢
    • 1970-01-01
    • 2015-05-25
    • 2021-02-25
    • 1970-01-01
    • 2013-07-27
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 2015-10-31
    相关资源
    最近更新 更多