Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to group by date with Mongoid

I'm trying to make a query grouping by date with different formats (day, month, year), I know that it's a easy query on typically SQL databases.

You can see the code on this link: https://gist.github.com/jrdi/b3f824fa4e7531c43bfd

Know I can run:

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

In my opinion is really weird all this code for make a simple group by. Am I missing something important?

PD: I know that method self.map and some interpolation are not the best way but know I care about Mongo stuff.

like image 812
jrdi Avatar asked Oct 15 '13 14:10

jrdi


People also ask

How to group by month and year in MongoDB?

Simply add it: {$project : { month : {$month : "$entryTime"}, year : {$year : "$entryTime"}, expenseAmount : 1 }}, Note that if field does not exist in document, then $sum returns 0.

How should dates be stored in MongoDB?

The best format to store date and time in MongoDB is native javascript Date() or ISO date format as it internally converts it into BSON native Date object.


Video Answer


1 Answers

Here's an answer that uses the aggregation framework to group by date. I hope that you like it.

app/model/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

test/unit/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

$ rake test

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
like image 132
Gary Murakami Avatar answered Sep 18 '22 10:09

Gary Murakami