Inocmes
和Expenses
个集合在整个应用中的许多地方单独使用。只有一页具有以下要求。我不相信Mongodb没有解决方法,它确实拥有数百万用户:(
我在项目中使用React-Meteor有两个名为Incomes
和Expenses
的集合。收入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个。那怎么处理呢?我也使用了反技术,但是没有用。记住我的数据也是实时的。 任何帮助将不胜感激:)
答案 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