查找后,根据外部集合中存在的字段对过滤器数据进行 MongoDB 查询需要时间

问题描述 投票:0回答:1

我有 2 个集合客户和订单。我必须过滤过去 60 天内下过订单的客户。我为获取这些数据而编写的聚合花费了很多时间。

客户收藏

_id
id
name
phone
order_id

订单收藏

_id
id
created_at

数据样本大小

Customers collection has 1M customers and Orders Collection has 160K Orders.

我已在 customers 集合中的 order_idid 字段以及 orders 集合中的 idcreated_at 字段创建了索引。

我正在运行以下聚合。这需要时间。

聚合1:

[
  {
    $lookup: {
      from: "orders",
      localField: "order_id",
      foreignField: "id",
      pipeline:[
        {
          $project:{
            "id":1,
            "created_at":1
          }
        }
      ],
      as: "order"
    }
  },
  {
    $unwind: "$order"
  },
  {
    $project: {
      "id":1,
      "name":1,
      "phone":1,
      "order":1
    }
  },
  {
    $match: {
      "order.created_at":{
        $gt:"2023-12-10"
      }
    }
  }
]

聚合2:

[
  {
    $lookup: {
      from: "orders",
      localField: "order_id",
      foreignField: "id",
      pipeline:[
        {
          $project:{
            "id":1,
            "created_at":1
          }
        },
        {
          $match:{
            created_at: {
               $gt:"2023-12-10"
            }
          }
        }
      ],
      as: "order"
    }
  },
  {
    $unwind: "$order"
  },
  {
    $project: {
      "id":1,
      "name":1,
      "phone":1,
      "order":1
    }
  },
  {
    $match: {
      "order.created_at":{
        $gt:"2023-12-10"
      }
    }
  }
]

请指导我们如何进行此聚合,以便它能在更短的时间内返回结果。

mongodb mongodb-query aggregation-framework mongodb-lookup
1个回答
0
投票

检索过去 60 天内下过订单的客户的最佳方法是在

customer
文档中添加最后下订单的日期(例如
lastOrderDate
)。这样,您可以通过仅查询
$lookup
集合来避免昂贵的
customers
阶段。下订单时,您不仅会存储订单,而且如果
customer
小于新值,还会更新
lastOrderDate
文档。

通过这种方式,您可以在

customers
集合上创建索引并仅查询一个集合,从而避免查找每个客户文档对性能造成影响。

如果您查看数字并假设

customers
orders
之间存在 1:n 关系,则需要将需要查询的
customer
文档数量从 1M 减少到最多 160k,这将进一步减少通过使用索引支持查询来减少(
lastOrderDate_1
就足够了,但如果您想覆盖查询,您还可以创建
lastOrderDate_1_id_1_name_1_phone_1
,以便所有投影字段都包含在索引中)。


如果无法更改

customers
集合中的架构,则应尝试尽可能减少通过管道传递的文档量。在您的示例中,您将所有 100 万个客户放入管道中,查询每个客户的
orders
集合 - 尽管只有 160k 客户可以下订单。

有很多客户还没有订单,但就目前情况而言,你无法事先识别他们(我假设

order_id
的设置不考虑客户是否实际有订单)。

因此,我会尝试扭转局面并查询

Orders
并从那里查找客户信息,例如

[
  {
    $match: {
      created_at: {
        $gt: "2023-12-10",
      },
    },
  },
  {
    $sort: {
      id: 1,
    },
  },
  {
    $group: {
      _id: "$id",
      created_at: { $max: "$created_at" },
    },
  },
  {
    $lookup: {
      from: "customers",
      localField: "_id",
      foreignField: "order_id",
      as: "customer",
    },
  },
  {
    $unwind: "$customer",
  },
  {
    $replaceWith: {
      id: "$customer.id",
      name: "$customer.name",
      phone: "$customer.phone",
      order: {
        created_at: "$created_at",
      },
    },
  },
]

以上管道为示例,请根据需要进行测试和调整。它的目的是充分利用

id_1_create_at_1
集合上的索引
orders
,以便尽可能减少需要检索的文档量。由于
$lookup
阶段,每个客户只能进行一次昂贵的
$group
操作。

最后一点,还可以将客户的相关详细信息存储在

order
文档中,再次完全避免查找。

© www.soinside.com 2019 - 2024. All rights reserved.