2016-09-21 57 views
3

是否有可能在同一個查詢管道中有效地同時執行map reduce和lookup?mongo db - map reduce and lookup

比方說,我有兩個集合:

  • 項目:{ _id, group_id, createdAt }
  • 採購:{ _id, item_id }

我想要得到的前n個項目組的基礎上,購買的數量每組最近的x個項目。

如果我在項目文檔中有可用購買數量,那麼我可以聚合並排序,但事實並非如此。

我可以得到每組最近的X項目像這樣:

let x = 3; 
let map = function() { 
    emit(this.group_id, { items: [this] }); 
}; 
let reduce = function (key, values) { 
    return { items: getLastXItems(x, values.map(v => v.items[0])) }; 
}; 
let scope = { x }; 

db.items.mapReduce(map, reduce, { out: { inline: 1 }, scope }, function(err, res) { 
    if (err) { 
    ... 
    } else { 
    // res is an array of { group_id, items } where items is the last x items of the group 
    } 
}); 

但是我錯過了購買數,所以我不能用它來排序組,輸出前N個組(其順便說一句,我甚至不知道我能做什麼)

我在Web服務器上使用它,並根據用戶上下文運行與範圍變量的查詢,所以我不想輸出結果到另一個收集並且必須做一切內聯。

===編輯1 ===添加數據例如:

樣本數據可以是:

// items 
{ _id: '1, group_id: 'a', createdAt: 0 } 
{ _id: '2, group_id: 'a', createdAt: 2 } 
{ _id: '3, group_id: 'a', createdAt: 4 } 
{ _id: '4, group_id: 'b', createdAt: 1 } 
{ _id: '5, group_id: 'b', createdAt: 3 } 
{ _id: '6, group_id: 'b', createdAt: 5 } 
{ _id: '7, group_id: 'b', createdAt: 7 } 
{ _id: '8, group_id: 'c', createdAt: 5 } 
{ _id: '9, group_id: 'd', createdAt: 5 } 

// purchases 
{ _id: '1', item_id: '1' } 
{ _id: '2', item_id: '1' } 
{ _id: '3', item_id: '3' } 
{ _id: '4', item_id: '5' } 
{ _id: '5', item_id: '5' } 
{ _id: '6', item_id: '6' } 
{ _id: '7', item_id: '7' } 
{ _id: '8', item_id: '7' } 
{ _id: '9', item_id: '7' } 
{ _id: '10', item_id: '3' } 
{ _id: '11', item_id: '9' } 

並用n = 3x = 2樣品的結果將是:

[ 
    group_id: 'a', numberOfPurchasesOnLastXItems: 4, 
    group_id: 'b', numberOfPurchasesOnLastXItems: 3, 
    group_id: 'c', numberOfPurchasesOnLastXItems: 1, 
] 
+2

你可以張貼一些示例項目和購買的,隨着一個例子結果?從您的描述中不清楚這裏涉及的數據類型,例如購買是否包含item_id或單個item_id的數組? –

+0

我編輯了這個問題,謝謝 – Guig

+0

對不起,對於您的示例數據,'n = 3'和'x = 2'是什麼意思? –

回答

0

我認爲這可以通過聚合管道解決,但我不知道這是多麼糟糕,尤其是性能方面。

關注我的是:

  • 將聚合管道能夠受益於指數,在查找和排序?
  • 可它只是用來計算匹配項查詢+投影簡化

無論如何,我認爲一個解決方案,我可能是:

x = 2; 
n = 3; 

items.aggregate([ 
    { 
    $lookup: { 
     from: 'purchases', 
     localField: '_id', 
     foreignField: 'item_id', 
     as: 'purchases', 
    }, 
    }, 
    /* 
    after the join, the data is like { 
    _id: <itemId>, 
    group_id: <itemGroupId>, 
    createdAt: <itemCreationDate>, 
    purchases: <arrayOfPurchases>, 
    } 
    */ 

    { 
    $project: { 
     group_id: 1, 
     createdAt: 1, 
     pruchasesCount: { $size: '$purchases' }, 
    } 
    } 
    /* 
    after the projection, the data is like { 
    _id: <itemId>, 
    group_id: <itemGroupId>, 
    createdAt: <itemCreationDate>, 
    purchasesCount: <numberOfPurchases>, 
    } 
    */ 

    { 
    $sort: { createdAt: 1 } 
    }, 

    { 
    $group: { 
     _id: '$group_id', 
     items: { 
     $push: '$purchasesCount', 
     } 
    } 
    } 
    /* 
    after the group, the data is like { 
    _id: <groupId>, 
    items: <array of number of purchases per item, sorted per item creation date>, 
    } 
    */ 

    { 
    $project: { 
     numberOfPurchasesOnMostRecentItems: { $sum: { $slice: ['$purchasesCount', x] } }, 
    } 
    } 
    /* 
    after the projection, the data is like { 
    _id: <groupId>, 
    numberOfPurchasesOnMostRecentItems: <number of purchases on the last x items>, 
    } 
    */ 

    { 
    $sort: { numberOfPurchasesOnMostRecentItems: 1 } 
    }, 

    { $limit : n } 
]);