Mongoid 聚合 $match 与 Date 对象不起作用?
Posted
技术标签:
【中文标题】Mongoid 聚合 $match 与 Date 对象不起作用?【英文标题】:Mongoid aggregation $match with Date objects not working? 【发布时间】:2012-11-24 23:45:04 【问题描述】:我在一个集合中有这样的结构的用户文档:
"_id" : ObjectId( "4fb54ef46d93b33b21003951" ),
"activities" : [
"id" : ObjectId( "4fd66f9001e7fe9f03000065" ),
"type" : "checkin",
"date_time_created" : Date( 1339453328000 ),
"date_time_created" : Date( 1337351732000 ),
"date_time_updated" : Date( 1337351952635 ),
"id" : ObjectId( "4fb65e346d93b3fe77000000" )
]
我可以根据日期轻松查询这些文档:
User.where(
:activities =>
'$elemMatch' =>
:date_time_created => '$gte' => start_date, '$lt' => end_date
).length
根据日志:
MOPED: 127.0.0.1:27017 COMMAND database=db command=:count=>"users", :query=>"activities"=>"$elemMatch"=>"date_time_created"=>" $gte"=>2012-05-10 00:00:00 UTC,"$lt"=>2012-07-12 00:00:00 UTC(0.5260 毫秒)
我通过这种方式得到了我需要的结果。
但是,当我尝试使用新的聚合函数和基于相同条件的 $match 时:
User.collection.aggregate( [
"$match" =>
:activities =>
'$elemMatch' =>
:date_time_created => '$gte' => start_date, '$lt' => end_date
]).length
根据日志:
MOPED: 127.0.0.1:27017 COMMAND database=db command=:aggregate=>"users", :pipeline=>["$match"=>:activities=>"$elemMatch"=> "date_time_created"=>"$gte"=>2012 年 5 月 10 日星期四,"$lt"=>2012 年 7 月 12 日星期四] (0.6049 毫秒)
“start_date”和“end_date”是 Ruby Date 对象,在两个查询中基本相同。但是,当我查看日志时,它们会变成不同的格式。当我尝试使用 start_date.strftime("%Y-%m-%d") 之类的东西强制格式时,它仍然不起作用。
聚合管道中还有其他函数,但我把它们取出来仍然报错。
如何让聚合函数在日期上匹配?
【问题讨论】:
你不需要 $elemMatch 这里 - 你可以删除它,看看这是否会改变正在发生的事情? $elemMatch 用于比较数组元素的两个不同属性时,这里是测试单个值。 【参考方案1】:以下测试使用聚合框架来近似查询的逻辑等效项。
首先 $unwind 数组,$match,然后 $group 使用 $addToSet 以防有多个匹配的数组元素。请注意,这是一个有损过程,但如果您只需要计数或 id,这是可以接受的。
希望这会有所帮助。
test/unit/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
User.delete_all
end
test "user aggregate" do
User.create(
activities: [
id: Moped::BSON::ObjectId("4fd66f9001e7fe9f03000065"),
type: "checkin",
date_time_created: Time.at(1339453328000) ,
date_time_created: Time.at(1337351732000),
date_time_updated: Time.at(1337351952635),
id: Moped::BSON::ObjectId("4fb65e346d93b3fe77000000")
]
)
start_date = Time.at(1339453328000)
end_date = start_date + 24*60*60
assert_equal(1, User.where(
:activities =>
'$elemMatch' =>
:date_time_created => '$gte' => start_date, '$lt' => end_date
).length)
assert_equal(1, User.collection.aggregate( [
'$unwind' => '$activities' ,
'$match' => '$and' => [
'activities.date_time_created' => '$gte' => start_date ,
'activities.date_time_created' => '$lt' => end_date
] ,
'$group' => '_id' => '$_id', 'activities' => '$addToSet' => '$activities'
] ).length)
end
end
【讨论】:
【参考方案2】:这样的聚合匹配的正确类型:
:$match=> :created_at=>:$gte=>2017-03-07 00:36:30 +0700
这意味着你应该使用方法:
match_na =
'$match': created_at: '$gte': (Time.now-30.days)
请注意:不要使用 Time.zone.now(因为输出会是另一种格式)。 我很确定大约 100%。我需要 3 个小时。
再次:像这样制作日期或时间的格式:
2017-03-07 00:36:30 +0700
不是这个:
Mon, 03 Apr 2017 00:34:14 ICT +07:00
或者简单的方法:使用方法 mongoize
(Date.today - 30.days).mongoize
【讨论】:
【参考方案3】:我找到了this short post,这对我有帮助。这是基本的代码示例 - 相比之下似乎非常简单:
Stat.collection.aggregate(
'$match' =>
'$and' => [
'day' => '$gte' => from.to_date.mongoize ,
'day' => '$lte' => to.to_date.mongoize
]
,
)
感谢作者!
【讨论】:
【参考方案4】:我对 ruby 的了解不多,但如果你能粘贴 mongo shell,我可以提供帮助。
有一些提示: 1. 你正在使用 elemMatch 所以你应该使用 $unwind 2. 之后你尝试使用 $match
【讨论】:
以上是关于Mongoid 聚合 $match 与 Date 对象不起作用?的主要内容,如果未能解决你的问题,请参考以下文章
不能将 $match 与 mongoose 和聚合框架一起使用