MongoDB Mongoid 下按时间分类取数据的问题

arthur_h · 2013年11月14日 · 最后由 Arthur_h 回复于 2013年11月15日 · 8369 次阅读

新手求抱大腿^_^

最近在用 mongoid,在写一个 model 的时候发现有地方需要用类似于 group_by 的命令,但是找了一圈貌似没有对应的包装方法,小伙伴们有类似的方法经验能指教下嘛?。。

情况:比如有一组新闻数据年份不等,需要按照年份分别归类拿出。

自己的笨办法:翻了半天 mongodb 文档发现 map_reduce 可以满足我的需求写出类似与 group_by 命令后在拿数据出来。

想到的问题: 1.map_reduce 据说效率不高而且这样加上后面的查询貌似一次取数据 2 次 hit 数据库 (map_reduce,where) 2.map_reduce 中基本为 mongodb 的 js command 所完成所以增加了后面的人的维护难度 (比较喜欢 ruby 的语法)

写的随手了点纯文字流了。。感觉满常见的需求应该有高效解决办法,小伙伴们回答下感激感激~~~~~~~

Model.between(created_at: [t_start, t_end]) ?

#1 楼 @dddd1919 事先不知道 t_start, t_end。。。所以要类似 group_by 一下?

#2 楼 @Arthur_h group_by把日期按 range 分组也是要自己写范围,不过要是分组统计的话 mongoid 也提供 group_by的啊

建议参考 map_reduce 或者 mongodb 的 aggregate map_reduce 一般用来做一些复杂的数据模型,如果简单的话 aggregate framework 就可以解决。 你所说的两次访问 db 的性能暂时不考虑,以后可以用其他方法来解决,担心数据库性能不如担心 rails 性能。

如果你想写成 ruby style 的 code 的话,可以做一些 module,例如下面以前抽象出来的 module,直接集成在 model 上,api 就像这样

Model.aggregate_by_date(:field => FIELD, :from =>FROM, :to =>TO, :duration => year | month | day)
def aggregate_by_date *args, &block
        options = args.extract_options!
        duration = options[:duration]

        self.aggregate do
          match do |m|
            if options[:field] && (options[:from] || options[:to])
              m[options[:field]] = {}
              m[options[:field]]["$gte"] = options[:from] if options[:from]
              m[options[:field]]["$lt"] = options[:to] if options[:to]
            end
          end

          group do |g|
            g['count'] = {"$sum" => 1}
          end

          group_id do |id|
            id.year = {"$year" => "$created_at"} if duration.include?("year")
            id.month = {"$month" => "$created_at"} if duration.include?("month")
            id.day = {"$dayOfMonth" => "$created_at"} if duration.include?("day")
          end

          yield(self)

          sort do |s|
            s._id = 1
          end
        end
      end

#5 楼 @hxplove01 谢谢,aggregate 这个方案非常好完全满足了需求(看了和 mapreduce 的性能对比),惭愧自己看文档的时候没注意到 aggregate。。。数据库两次查询的话 aggregate 一次后把 group 的结果丢到 redis 里面算了,反正不会是已经变动的。再次感谢!

需要 登录 后方可回复, 如果你还没有账号请 注册新账号