2013-10-15 5 views
4

Я пытаюсь сделать группировку запросов по дате в разных форматах (день, месяц, год), я знаю, что это простой запрос для типичных баз данных SQL.Лучший способ группировки по дате с Mongoid

Вы можете увидеть код по этой ссылке: https://gist.github.com/jrdi/b3f824fa4e7531c43bfd

знаю, что могу работать:

> Patient.group_by('created_at', 'day') 
=> [{"_id":"11/10/2013","value":{"count":3.0}}] 

На мой взгляд, это действительно странно, все это код сделать простую группу путем. Я пропустил что-то важное?

PD: Я знаю, что метод self.map и некоторая интерполяция - это не лучший способ, но знаю, что я забочусь о вещах Монго.

+0

Похоже, у вас есть подобный код, чтобы этот ответ http://stackoverflow.com/a/12034204/1354978 – earlonrails

+0

@earlonrails да, конечно, но это означает, что это хорошее решение? Мне просто интересно, потому что я вижу, что мой (или ваш) код действительно странный. – decay

ответ

7

Вот ответ, который использует структуру агрегации для группировки по дате. Надеюсь, вам понравится.

приложение/модель/patient.rb

class Patient 
    include Mongoid::Document 
    field :name, type: String 

    def self.group_by(field, format = 'day') 
    key_op = [['year', '$year'], ['month', '$month'], ['day', '$dayOfMonth']] 
    key_op = key_op.take(1 + key_op.find_index { |key, op| format == key }) 
    project_date_fields = Hash[*key_op.collect { |key, op| [key, {op => "$#{field}"}] }.flatten] 
    group_id_fields = Hash[*key_op.collect { |key, op| [key, "$#{key}"] }.flatten] 
    pipeline = [ 
     {"$project" => {"name" => 1, field => 1}.merge(project_date_fields)}, 
     {"$group" => {"_id" => group_id_fields, "count" => {"$sum" => 1}}}, 
     {"$sort" => {"count" => -1}} 
    ] 
    collection.aggregate(pipeline) 
    end 
end 

тест/блок/patient_test.rb

require 'test_helper' 
require 'pp' 

class PatientTest < ActiveSupport::TestCase 
    def setup 
    Patient.delete_all 
    end 

    test "group by date" do 
    [ 
     {"name" => "John", "created_at" => Date.new(2012, 10, 10).mongoize}, 
     {"name" => "Jane", "created_at" => Date.new(2012, 10, 31).mongoize}, 
     {"name" => "Mary", "created_at" => Date.new(2012, 10, 31).mongoize}, 
     {"name" => "Mark", "created_at" => Date.new(2012, 12, 12).mongoize}, 
     {"name" => "Alex", "created_at" => Date.new(2013, 11, 10).mongoize}, 
     {"name" => "Andy", "created_at" => Date.new(2013, 10, 31).mongoize}, 
     {"name" => "Toni", "created_at" => Date.new(2013, 10, 31).mongoize}, 
     {"name" => "Cori", "created_at" => Date.new(2013, 11, 10).mongoize} 
    ].each do |patient| 
     Patient.create(patient) 
    end 
    puts "\nMongoid::VERSION:#{Mongoid::VERSION}\nMoped::VERSION:#{Moped::VERSION}" 
    pp Patient.group_by('created_at', 'month') 
    end 
end 

тест $ рейка

Run options: 

# Running tests: 

[1/1] PatientTest#test_group_by_date 
Mongoid::VERSION:3.1.5 
Moped::VERSION:1.5.1 
[{"_id"=>{"year"=>2012, "month"=>10}, "count"=>3}, 
{"_id"=>{"year"=>2013, "month"=>10}, "count"=>2}, 
{"_id"=>{"year"=>2013, "month"=>11}, "count"=>2}, 
{"_id"=>{"year"=>2012, "month"=>12}, "count"=>1}] 
Finished tests in 0.042561s, 23.4957 tests/s, 0.0000 assertions/s. 
1 tests, 0 assertions, 0 failures, 0 errors, 0 skips 
+0

кажется лучшим кодом, спасибо! – decay

+0

Каков наилучший способ включить условие в этот запрос? Он работает нормально, но мне нужно ограничить специфику: post_id. Как это сделать? –

+0

Просто добавьте этап $ match к началу конвейера, см. Http://docs.mongodb.org/manual/reference/operator/aggregation/match/ –

Смежные вопросы