2017-07-08 1 views
3

Die Inocmes und Expenses Sammlungen verwendet getrennt an vielen Orten in ganz App. Es gibt nur eine Seite, die die folgende Anforderung hat. Ich glaube nicht, gibt es keine Abhilfe in MongoDB, die wirklichZusammenführen von zwei Sammlungen Zeitstempel Daten und zeigt Echtzeit Ergebnis

Ich verwende React-Meteor :(in einem Projekt Millionen von Nutzern haben zwei Sammlung namens Incomes und Expenses. Einkommen Doc aussehen wie unten

{ 
    "_id" : "euAeJYArsAyFWLJs6", 
    "account" : "3m5Zxsije9b6ZaNpu", 
    "amount" : 3, 
    "receivedAt" : ISODate("2017-07-07T06:21:00.000Z"), 
    "type" : "project", 
    "project" : { 
     "_id" : "ec2WLt3GzHNwhK7oK", 
     "name" : "test" 
    }, 
    "owner" : "nM4ToQEbYBsC3NoQB", 
    "createdAt" : ISODate("2017-07-07T06:21:37.293Z") 
} 

und unten, wie die Kosten Doc aussehen wie

{ 
    "_id" : "snWDusDbkLHHY2Yry", 
    "account" : "3m5Zxsije9b6ZaNpu", 
    "amount" : 4, 
    "spentAt" : ISODate("2017-07-07T06:21:00.000Z"), 
    "description" : "4", 
    "category" : { 
     "_id" : "vh593tw9dZgNdNwtr", 
     "name" : "test", 
     "icon" : "icon-icons_tution-fee" 
    }, 
    "owner" : "nM4ToQEbYBsC3NoQB", 
    "createdAt" : ISODate("2017-07-07T06:22:04.215Z") 
} 

Jetzt habe ich eine Seite Transaktionen genannt, wo ich alle Transaktions (Einnahmen und Ausgaben) zeigen müssen, basierend auf Zeit so für trans meine Veröffentlichung Code Aktionen siehe unten

import { Meteor } from 'meteor/meteor'; 
import { Incomes } from '../../incomes/incomes.js'; 
import { Expenses } from '../../expences/expenses.js'; 
import { Counter } from 'meteor/natestrauser:publish-performant-counts'; 


let datefilter = (options, query) => { 
    let dateQuery = {$gte: new Date(options.dateFilter.start), $lte: new Date(options.dateFilter.end)}; 
    let temp = {$or: [{receivedAt: dateQuery}, {spentAt: dateQuery}]}; 
    query.$and.push(temp); 
}; 
Meteor.publish('transactions', function(options) { 
    let query = { 
     owner: this.userId, 
     $and: [] 
    }; 

    if(options.accounts.length) 
     query['account'] = {$in: options.accounts}; 

    options.dateFilter && datefilter(options, query); 
    //here i also apply other filter based on category and project which does not matter so i removed 

    if(!query.$and.length) delete query.$and; 

    //computing 'Transactions' below 
    return [ 
     Incomes.find(query, { 
      sort: { 
       receivedAt: -1 
      }, 
      limit: options.limit, 
      skip: options.skip 
     }), 
     Expenses.find(query, { 
      sort: { 
       spentAt: -1 
      }, 
      limit: options.limit, 
      skip: options.skip 
     }) 
    ] 
}); 

feine Arbeit alles Bis hier wie bis ich Paginierung auf Transaktions Seite zu implementieren, so dass hier müssen Daten nach Datum sortiert werden. Das ist das wahre Problem. Nehmen wir an, meine beiden Sammlungen haben jeweils 10 Datensätze und meine Vorlagenseite muss die ersten 10 Ergebnisse enthalten, also habe ich 0 und 10 gesendet, im Gegenzug 10 Einnahmen und 10 Aufwandsdatensätze und auf der zweiten Seite gibt es keinen Datensatz, weil überspringen und begrenzen schickte 10 für beide. also wie man damit umgeht? Ich habe auch Technik benutzt, aber das hat nicht funktioniert. Erinnere dich, dass meine Daten auch in Echtzeit sind. jede Hilfe wird sehr geschätzt :)

+0

Sie könnten einfach und tatsächlich Ihr Problem lösen, indem Sie einfach die Sammlungen an erster Stelle "zusammenführen". Wenn ich Sie wäre, würde ich genau das tun und stattdessen einfach einen "Typ" für "Einkommen" und einen für "Aufwand" zu den Artikeln in jeder Sammlung hinzufügen und sie zusammenführen. Abgesehen davon ist das, wonach Sie fragen, eine SQL UNION-Abfrage mit Sortierung und Paging über die "Vereinigung" der Ergebnisse. Es gibt tatsächlich keine MongoDB-Operation, die direkt damit korreliert. Welche Blätter tatsächlich die Sammlungen an ihrer Quelle als die vernünftigste Option zusammenführen, –

+0

Bitte erklären Sie den Begriff "Zusammenführen", indem Sie map reduce || verwenden Aggregat oder einfach nur zwei Sammlungen entfernt und eine einzelne Sammlung vollständig machen? –

+0

Lass mich klarer sein. MongoDB hat keine Methode, um die Daten aus zwei Quellen für die Abfrage in der gewünschten Weise zu verbinden. Sie müssen tatsächlich eine "neue Kollektion" erstellen, die Ihre beiden Kollektionen kombiniert. Also ja. Entferne die beiden und mache eine Single. Dann gibt es kein Problem. –

Antwort

2

Dies ist eine temporäre Lösung für eine schnelle Ausgabe kommen, sollten Sie Ihre Daten-Set Menge prüfen und bestanden alle Prüfungen vor Anwendung der Zwischenzeit werde ich mein Schema als @NeilLunn ändern Vorschläge und Planungen in Kürze.

Für die Ad-hoc-Fix, der die Anzeigeanforderungen erfüllen angewendet ich die aggregate mit der Kombination von $out. Nun schauen Sie den Code wie unten

Meteor.publish('transaction', function(options){ 
    //here I perform many filter based on query and options 
    //which deleted to shorten code on SO 

    //call asynchronous method just to get result delay :) 
    Meteor.call('copyTransactions', (err, res) => { 
     //something do here 
    }); 
    //note the we return results from **Transactions** which is comes as third collection 
    return [ 
     Transactions.find(query, { 
      sort: sortbyDate, 
      skip: options.skip, 
      limit: options.limit, 
     }), 
     new Counter('transactionsCount', Transactions.find(query, { 
      sort: sortbyDate 
     })) 
    ]; 
}); 

Jetzt Transaktionen (eine getrennte Sammlung) veröffentlichen, die ich als merge Sammlung gewünscht.Hinweis: Dies ist ein endet mit s in Namen wie transactions also nicht mit über einer verwechseln (transaction)

die Zusammenführung Sammlung separat als „Transaktionen“

Meteor.publish('transactions', function(limit){ 
    return Transactions.find(
     { 
      owner: this.userId 
     }); 
}); 

veröffentlichen und hier ist die wichtige Methode, die Veröffentlichung aufgerufen, um zwei Sammlung in der dritten Sammlung zusammenzuführen, in der ich zuerst aggregated alle mit $out Ergebnis und dann zweite Sammlung mit batch einfügen einfügen

import { Expenses } from '../../../api/expences/expenses' 
import { Incomes } from '../../../api/incomes/incomes' 
import { Transactions } from '../transactions' 
import Future from 'fibers/future'; 
export const copyTransactions = new ValidatedMethod({ 
    name: 'copyTransactions', 
    validate:null, 
    run() { 
     //we are using future here to make it asynchronous 
     let fu = new Future(); 
     //here Expenses directly copied in new collection name Transactions 
     // TODO: use $rename or $addField in aggregate instead of $project 
     Expenses.aggregate([{ 
      $project: { 
       account : "$account", 
       amount : "$amount", 
       transactionAt : "$spentAt", 
       description : "$description", 
       category : "$category", 
       type: { 
        $literal: 'expense' 
       }, 
       owner : "$owner", 
       createdAt : "$createdAt" 
      } 
     }, { 
      $out: "transactions" 
     } ]); 
     //now append Transactions collection with incomes with batch insert 
     Incomes.aggregate([{ 
      $project: { 
       account : "$account", 
       amount : "$amount", 
       transactionAt : "$receivedAt", 
       type:{ 
        $literal: 'income' 
       }, 
       project : "$project", 
       owner : "$owner", 
       createdAt : "$createdAt" 
      } 
     }], function (err, result) { 
      //if no doc found then just return 
      if(!result.length){ 
       fu.return('completed') 
      } 
      else{ 
       Transactions.batchInsert(result, function(err, res){ 
        fu.return('completed') 
       }) 
      } 

     }); 
     return fu.wait(); 
    } 
}); 

Wenn Zweite Sammlung aggregated auch mit $out dann wird es :(

@client überschreiben muss ich nur noch die ‚Transaktion‘ mit meinen Optionen und Abfrage abonnieren und bekam die Echtzeit merge Ergebnisse von Transactions

+0

Ich habe '$ project' verwendet, weil Meine aktuelle Version unterstützt leider nicht '$ rename' und' $ addField' –

Verwandte Themen