2017-07-08 34 views
3

InocmesExpenses集合在整個應用程序中的許多地方分開使用完成。只有一個頁面具有以下要求。我不相信這是在MongoDB中沒有解決方法,從而真正擁有數百萬用戶:(合併兩個收集時間戳數據並顯示實時結果

我使用的陣營,流星的項目中有兩個集合命名IncomesExpenses。收入文件看起來像下面

{ 
    "_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") 
} 

及以下如​​何爲代價的文件看起來像

{ 
    "_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") 
} 

現在我有一個基於時間稱爲頁面的交易,我要顯示所有交易(收入和支出)爲反式,所以我的代碼公佈動作看起來像下面

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

到這裏的每一件事工作正常,直到我要實現的交易頁面分頁,所以這裏的數據必須按日期排序。這是真正的問題。假設我的兩個集合都有10條記錄,而我的模板頁面必須包含前10條結果,所以我發送了跳過0和限制10,作爲回報,我得到了10條收入和10條支出記錄,第二頁上沒有記錄,因爲跳過和限制兩人都送了10張。那麼如何處理呢?我也使用反科技,但沒有奏效。記住我的數據也是實時的。 任何幫助,將不勝感激:)

+0

您可以簡單而實際地通過簡單地「合併」集合來解決您的問題。如果我是你,我會這樣做,而是簡單地爲每個集合中的項目添加一個「類型」作爲「收入」和一個「費用」併合並它們。除此之外,您所要求的就像是一個SQL UNION查詢,它對結果的「聯合」進行排序和分頁。實際上沒有與此直接相關的MongoDB操作。哪些葉子實際上將它們的來源合併爲最合理的選項 –

+0

請解釋「合併」術語,使用map reduce ||聚合還是僅刪除兩個集合,並完成單個集合? –

+0

讓我更清楚。 MongoDB沒有方法合併來自兩個數據源的數據以您想要的方式進行查詢。您需要實際創建一個結合您的兩個收藏的「新收藏」。所以是的。刪除這兩個並且製作一個。那麼沒有問題。 –

回答

2

這是一個快速的加入問題治標不治本,你應該考慮 您的數據集量,並通過所有檢查前申請與此同時我會改變我的架構爲@NeilLunn不久建議並計劃遷移。

對於滿足顯示要求的adhoc Fix,我申請了aggregate$out的組合。現在代碼看起來像下面

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

現在發佈交易(一個單獨的集合),我希望作爲一個合併集合。注意,這與一個在s名稱結束的transactions所以不要用一個以上(transaction

分別發佈合併集合作爲「交易」

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

,這裏是最迷惑重要的方法,在出版物中要合併兩個集合在第三集合中我首先aggregated所有結果與$out,然後附加第二集合與batch插入

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

如果第二個系列aggregated也與$out那麼它將 覆蓋:(

@client我只是認購與我的選擇和查詢「交易」,並獲得實時合併來自Transactions的結果

+0

我使用了'$ project',因爲不幸的是,我目前的版本不支持'$ rename'和'$ addField' –

相關問題