我正在尝试在MongoDB中执行日期范围查询。假设我要查找在“ 2018年5月”中创建的记录。现在,根据知识,我们可以通过以下方式实现这一目标。
一种方法是对createdAt进行简单的范围查询。例如
{createdAt: { $gte: ISODate('2018-05-01'), $lte: ISODate('2018-05-31')}}
另一种方法是聚合
db.collection.aggregate([{ $project: { year: { $year: '$createdAt' }, month: { $month: '$createdAt' }},
{ $match: { year: 2018, month: 5}}])
那么,如果可以通过简单的查询获得结果,何时使用聚合?如果以上查询或方法不正确或过旧,请更正。我正在Compass中进行测试。
谢谢
答案 0 :(得分:1)
您不需要$project
阶段(实际上,您编写投影的方式输出文档将只包含投影值和_id
字段,无论您是不是想)。因此,剩下的只是聚合查询$match
。可以假定查询执行时间是相同的,只是阶段处理要付出较小的代价。另外,我个人希望“正常” find()
在内部几乎完全融合到聚合框架中,如果这还没有完全发生的话。保留两种查询数据的方式只是没有道理...
出于清楚的原因,我个人倾向于使用find()
版本。
答案 1 :(得分:1)
如果您可以通过简单的查询来做到这一点,只需保持简单即可。 有许多框架可以轻松支持查询,但不能支持聚合。
聚合很难编写和维护。在某些情况下,您确实需要它们(例如在处理数组或子文档时),但如果不需要,请使用find()保留它们。 有关何时需要聚合的示例:
MongoDB - Get latest non-null field value from documents with timestamp
此外,我不确定您的聚合代码是否与find()代码相同。 如果检查正确,则首先使用$ project阶段,然后进行匹配。 这意味着您要投影数据库的所有对象,然后在它们之间进行匹配,因为没有使用$ match开始聚合。 如果您有很多物体,这可能会很慢。
此外,$ match仅在处于第一阶段时才使用索引:
将$ match尽可能早地放在聚合管道中。 因为$ match限制了聚合中文档的总数 管道,较早的$ match操作可最大程度地减少处理量 下管道。如果您在$的开头放置$ match 管道,查询可以像其他任何索引一样利用索引 db.collection.find()或db.collection.findOne()。
答案 2 :(得分:1)
根据我的经验,执行基本的find()
后不久,您的需求就会发生变化,并需要管道的力量。例如,考虑以下文档:
{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 3 }
{ "_id" : 4, "date" : ISODate("2018-01-20T00:00:00Z") }
{ "_id" : 5 }
{ "_id" : 6 }
{ "_id" : 7, "date" : ISODate("2018-01-18T00:00:00Z") }
{ "_id" :8, "date" : ISODate("2018-01-10T00:00:00Z") }
我们要查找日期<= 2018-01-15的所有地方:
db.foo.aggregate([
{$match: {"date": {$lte: new ISODate("2018-01-15")}} }
]);
{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 8, "date" : ISODate("2018-01-10T00:00:00Z") }
糟糕!我们也需要空白日期:
db.foo.aggregate([
{$match: {"$or": [ {"date": {$lte: new ISODate("2018-01-15")}}, {"date": {$exists: false}} ] }}
]);
{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 3 }
{ "_id" : 5 }
{ "_id" : 6 }
{ "_id" : 8, "date" : ISODate("2018-01-10T00:00:00Z") }
我们希望对它进行排序:
db.foo.aggregate([
{$match: {"$or": [ {"date": {$lte: new ISODate("2018-01-15")}}, {"date": {$exists: false}} ] }}
,{$sort: {"date":1}}
]);
{ "_id" : 3 }
{ "_id" : 5 }
{ "_id" : 6 }
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 8, "date" : ISODate("2018-01-10T00:00:00Z") }
嗯。但是我们希望空格出现在排序列表的末尾。因此,我们用自身覆盖date
字段,或者如果覆盖null,则实际上是一个很远的日期,这现在为我们提供了我们想要的顺序:
db.foo.aggregate([
{$match: {"$or": [ {"date": {$lte: new ISODate("2018-01-15")}}, {"date": {$exists: false}} ] }}
,{$addFields: {"date": {$ifNull: [ "$date", new ISODate("3000-01-01")] }}}
,{$sort: {"date":1}}
]);
{ "_id" : 2, "date" : ISODate("2018-01-03T00:00:00Z") }
{ "_id" : 1, "date" : ISODate("2018-01-07T00:00:00Z") }
{ "_id" : 0, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 8, "date" : ISODate("2018-01-10T00:00:00Z") }
{ "_id" : 3, "date" : ISODate("3000-01-01T00:00:00Z") }
{ "_id" : 5, "date" : ISODate("3000-01-01T00:00:00Z") }
{ "_id" : 6, "date" : ISODate("3000-01-01T00:00:00Z") }
在此主题上有多种变体,例如执行初始$project
以创建公共的非空排序字段,但是将$match
放在最前面可以利用索引(如果存在)。当您的文档包含需要查询和操作的数组数据时,agg管道的真正威力便会彰显出来。