2016-09-21 3 views
3

Ist es möglich, sowohl ein Map Reduce mit einem Lookup in der gleichen Query Pipeline effizient durchzuführen?mongo db - map reduce und lookup

Sagen wir, ich habe zwei Kollektionen:

  • Artikel: { _id, group_id, createdAt }
  • Käufe: { _id, item_id }

ich die Top-n Artikelgruppe erhalten möchten, basierend auf der Anzahl der Käufe auf die letzten x Artikel pro Gruppe.

Wenn ich die Anzahl der Käufe in den Artikeldokumenten hatte, dann könnte ich aggregieren und sortieren, aber das ist nicht der Fall.

kann ich die letzten x Artikel pro Gruppe erhalten, wie so:

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 
    } 
}); 

Aber ich vermisse Kauf zählen, damit ich es nicht Gruppen zu sortieren verwenden und gibt die Top-n-Gruppen (die Übrigens bin ich mir nicht einmal sicher, ob ich das kann)

Ich benutze dies auf einem Webserver und führe die Abfrage mit einer Bereichsvariablen abhängig vom Benutzerkontext aus, so dass ich das Ergebnis nicht auf ein anderes ausgeben möchte Sammlung und muss alles inline machen.

=== bearbeiten 1 === fügen Sie Daten Beispiel:

Beispieldaten sein könnten:

// 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' } 

und Probenergebnis mit n = 3 und x = 2 wäre:

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

Könnten Sie einige Beispielartikel & Einkäufe veröffentlichen, zusammen mit ein Beispielergebnis? Aus Ihrer Beschreibung ist nicht klar, um welchen Datentyp es sich hier handelt, z. Ist die Käufe enthält ein Array von Item_id oder einzelne Item_id? –

+0

Ich habe die Frage bearbeitet, danke – Guig

+0

Sorry, was bedeuten 'n = 3' und 'x = 2' in Bezug auf deine Beispieldaten? –

Antwort

0

I denke, dass dies mit der Aggregationspipeline gelöst werden kann, aber ich habe keine Ahnung, wie schlecht das ist, vor allem hinsichtlich der Leistung.

Bedenken ich habe, sind:

  • wird die Aggregation Pipeline Vorteile von Indizes, auf Suche und sortieren können?
  • kann das Nachschlagen + Projektion, die passenden Artikel nur verwendet wird werden

Wie dem auch sei vereinfacht zu zählen, ich eine Lösung einfiel ich:

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 } 
]);