我有一個 Order 集合,其中的記錄如下所示:
{
"_id": ObjectId,
"status": String Enum,
"products": [{
"sku": String UUID,
...
}, ...],
...
},
我的目標是找到用戶一起購買的產品。給定一個 sku,我想瀏覽過去的訂單并查找包含 1 個以上產品的訂單,當然還有帶有查找 sku 的產品,還購買了哪些其他產品。
所以我創建了一個有效的聚合管道:
[
// exclude cancelled orders
{
'$match': {
'status': {
'$nin': [
'CANCELLED', 'CHECK_OUT'
]
}
}
},
// add a fields with product size and just the products sku
{
'$addFields': {
'size': {
'$size': '$products'
},
'skus': '$products.sku'
}
},
// limit to orders with 2 products or more including the looked up SKU
{
'$match': {
'size': {
'$gte': 2
},
'skus': {
'$elemMatch': {
'$eq': '3516215049767'
}
}
}
},
// group by skus
{
'$unwind': {
'path': '$skus'
}
}, {
'$group': {
'_id': '$skus',
'count': {
'$sum': 1
}
}
},
// sort by count, exclude the looked up sku, limit to 4 results
{
$sort': {
'count': -1
}
}, {
'$match': {
'_id': {
'$ne': '3516215049767'
}
}
}, {
'$limit': 4
}
]
雖然這有效,但該集合包含超過 10K 的檔案,并且我的 MongoDB 實體上有一個警報,告訴我掃描物件/回傳的比率已超過 1000。
所以我的問題是,如何改進我的查詢?我可以添加哪些索引來改善這一點?
db.Orders.stats();
{
size: 14329835,
count: 10571,
avgObjSize: 1355,
storageSize: 4952064,
freeStorageSize: 307200,
capped: false
nindexes: 2,
indexBuilds: [],
totalIndexSize: 466944,
totalSize: 5419008,
indexSizes: { _id_: 299008, status_1__created_at_1: 167936 },
scaleFactor: 1,
ok: 1,
operationTime: Timestamp({ t: 1635415716, i: 1 })
}
uj5u.com熱心網友回復:
讓我們從稍微重寫查詢以使其更高效開始。目前,您正在將所有訂單與特定狀態進行匹配,然后開始進行資料操作,這意味著每個階段都在處理比所需資料集更大的作業。
我們可以做的是將所有查詢移動到第一階段,這可以使用 Mongo 的點符號來實作,如下所示:
{
'$match': {
'status': {
'$nin': [
'CANCELLED', 'CHECK_OUT',
],
},
'products.sku': '3516215049767', // mongo allows you to do this using the dot notation.
'products.1': { $exists: true }, // this requires the array to have at least two elements.
},
},
現在這實作了兩件事:
- 我們只用相關的結果來啟動管道,不再需要
$size對許多不相關的檔案計算陣列。這已經會大大提高你的表現。 - 現在我們可以創建一個復合索引來支持這個特定的查詢,之前我們不能這樣做,因為索引使用僅限于第一步,并且只包括
status欄位。(就像軼事是 Mongo 實際上確實優化了管道,但在這種特定情況下,不可能對 的使用進行優化$addFields)
我建議建立的索引是:
{ status: 1, "products.sku": 1 }
這將允許最佳匹配開始您的管道。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/340246.html
