我有 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_id 和 id 字段以及 orders 集合中的 id 和 created_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"
}
}
}
]
请指导我们如何进行此聚合,以便它能在更短的时间内返回结果。
检索过去 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
文档中,再次完全避免查找。