合并两个集合时间戳数据并显示实时结果

时间:2017-07-08 06:08:41

标签: mongodb meteor aggregation-framework real-time minimongo

InocmesExpenses个集合在整个应用中的许多地方单独使用。只有一页具有以下要求。我不相信Mongodb没有解决方法,它确实拥有数百万用户:(

我在项目中使用React-Meteor有两个名为IncomesExpenses的集合。收入Doc看起来如下

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

以及Doc的费用

{
    "_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个。那怎么处理呢?我也使用了反技术,但是没有用。记住我的数据也是实时的。 任何帮助将不胜感激:)

1 个答案:

答案 0 :(得分:2)

  

这是快速加入问题的临时解决方案,您应该这样做   考虑您的数据集金额并在申请之前通过所有检查同时我将更改我的架构@NeilLunn建议并很快计划迁移。

对于符合显示要求的adhoc修正,我将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混淆

  

将合并集合单独发布为" transactions"

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

的实时合并结果