要求统计"过期日期"大于当前日期的客户记录数。
我在MongoDB中收集了一些客户。在客户文档中,有两个字段"合同日期"one_answers"期限"(期限以月为单位(。
[在此输入图像描述][1]
Mongo文档中没有可以使用的直接"过期日期"字段,但可以根据记录计算如下:
"合同日期"+"期限"(期限以月为单位(=到期日期。
我需要在DB级别计算每个客户记录的截止日期,并将该日期与当前日期进行比较。如何做到这一点?
如果DB中存在到期日期,那么我可以按照以下方式轻松实现:
final BasicDBList fromList = new BasicDBList();
fromList.add("$customer.expirationDate");
fromList.add(fromDate);
final BasicDBList cond1 = new BasicDBList();
cond1.add(new BasicDBObject("$gt", fromList));
cond1.add(1);
cond1.add(0);
DBObject count = new BasicDBObject("$sum", new BasicDBObject("$cond", cond1)))
groupFields.put("count", count );
BasicDBObject group = new BasicDBObject("$group", groupFields);
AggregationOutput output = template.getDb().getCollection("customer").aggregate(match, group);
如有任何帮助或建议,我们将不胜感激。
这确实是一种情况,你必须表明立场并做出改变,而不是试图"跳过重重困难"来处理其他人糟糕的设计决策。这里要延伸的"橄榄枝"是,最初的设计可能没有考虑到如何使用数据。
在不更改数据存储方式的情况下提出的查询需要付出不小的努力。我将这里的所有内容都保留为"shell"形式,使用JSON表示法或其他原始JavaScript。与其他语言一样,JSON很容易解析或转换为可用于为Java驱动程序构建BSON对象的方法。
因此,接下来,让我们看看这里的所有案例以及如何解决,以及在这里做出改变的局限性和好处。
在我们的"过期"集合中考虑以下样本:
{ "contractDate" : ISODate("2014-04-23T00:00:00Z"), "term" : 10 }
{ "contractDate" : ISODate("2014-04-23T00:00:00Z"), "term" : 7 }
{ "contractDate" : ISODate("2014-11-30T00:00:00Z"), "term" : 1 }
MongoDB有一个$where
操作符,它将在服务器上运行任意的JavaScript代码(作为JavaDriver的字符串提供(。定义的函数必须返回true/false
,以确定是否满足查询条件。基本上将"contractDate"+"term"计算为当前日期,或者由允许您将变量"范围化"到评估的JavaScript:的变体提供的日期
db.expiring.count({
"$where": function () {
var now = new Date(),
today = new Date(
now.valueOf() -
( now.valueOf() % ( 1000 * 60 * 60 * 24 ) )
);
var adjustedMonth =
this.contractDate.getMonth() + 1 + this.term;
var year = ( adjustedMonth > 12 ) ?
this.contractDate.getFullYear() + 1
: this.contractDate.getFullYear();
var month = ( adjustedMonth > 12 ) ?
adjustedMonth - 12 : adjustedMonth;
var day = this.contractDate.getDate();
var expiring = new Date( year + "-" + month + "-" + day );
return expiring > today;
}
})
这很可怕,因为您既要强行对集合中的每个文档评估条件,又要强制服务器端对集合中每个项目评估和执行JavaScript代码。由于它计算评估,所以不能使用索引来改进任何内容。
您还可以计算日期并通过聚合框架进行比较。为了提高可读性(同时也为了深入了解(,这里的示例分两个阶段给出,但它可以在一个$group
阶段完成:
db.expiring.aggregate([
{ "$project": {
"contractDate": 1,
"term": 1,
"expires": {
"year": {
"$cond": [
{ "$gt": [
{ "$add": [{ "$month": "$contractDate" }, "$term" ] },
12
]},
{ "$add": [{ "$year": "$contractDate" }, 1 ] },
{ "$year": "$contractDate" }
]
},
"month": {
"$cond": [
{ "$gt": [
{ "$add": [{ "$month": "$contractDate" }, "$term" ] },
12
]},
{ "$subtract": [
{ "$add": [{ "$month": "$contractDate" }, "$term" ] },
12
]},
{ "$add": [{ "$month": "$contractDate" }, "$term" ] }
]
},
"day": { "$dayOfMonth": "$contractDate" }
}
}},
{ "$group": {
"_id": null,
"count": {
"$sum": {
"$cond": [
{ "$or": [
{ "$gt": [ "$expires.year", thisYear ] },
{ "$and": [
{ "$eq": [ "$expires.year", thisYear ] },
{ "$gt": [ "$expires.month", thisMonth ] },
]},
{ "$and": [
{ "$eq": [ "$expires.year", thisYear ] },
{ "$eq": [ "$expires.month", thisMonth ] },
{ "$gt": [ "$expires.day", thisDay ] }
]}
]},
1,
0
]
}
}
}}
])
当然,在构造时输入外部变量以表示当前日期。在这里,它们被分解为thisYear
、thisMonth
和thisDay
以匹配所示的模式。您还可以使用类似于JavaScript代码的"日期数学"方法。
再说一遍,这太可怕了。即使在单个管道阶段,这仍然需要贯穿整个集合。原生操作符的速度稍微快一点,但没有那么快,当然你仍然不能使用索引。
这就是为什么应该更改数据的存储方式。考虑一下当文档看起来像这样时:
{
"contractDate" : ISODate("2014-04-23T00:00:00Z"),
"term" : 10,
"expiry": ISODate("2015-02-23T00:00:00Z")
}
{
"contractDate" : ISODate("2014-04-23T00:00:00Z"),
"term" : 7,
"expiry" : ISODate("2014-11-23T00:00:00Z"),
}
{
"contractDate" : ISODate("2014-11-30T00:00:00Z"),
"term" : 1,
"expiry": ISODate("2014-12-30T00:00:00Z")
}
现在还要考虑新的expiry
字段也被索引,并且现在获得计数的一种真正有效的方法是非常基本的:
db.expiring.count({ "expiry": { "$gt": new Date("2014-12-30") } })
就是这样!只有那些大于指定索引边界的项目被触摸,你只需要得到那些仍然活动的项目的计数,而不需要计算或评估任何东西。
因此,我认为,维护这些数据的代码需要更改,以在文档中保留这个额外的字段,并在任何更改时保留相对的两个字段"contractDate"one_answers"term"字段。
操作很简单,不应该很难,应该讨论维护代码的"非常小"的更改,再加上对现有数据的"一次性"更新。因此,平衡要么是"小更改",要么是实现"大混乱",只是为了报告不存在的东西。
我强烈建议你向能够做出改变决定的人展示这一点。这将为你和其他人节省时间。没有人希望查询速度慢、运行时间长。这也要花钱。
您无法按照自己想要的方式直接搜索MongoDB。关于解决方案,首先要问一个问题:您是否要在其他地方对Contract Date
和TERM
运行查询?如果没有,您可以去掉其中一个字段并存储Expiration Date
,因为A+B=C可以转换为B=C-A等等。如果您需要对所有这三个字段运行查询,您将需要添加这些重复的数据,或者过滤Java代码中的所有记录