Grouping by attribute of associated objects - ruby-on-rails

class Event < ActiveRecord::Base
has_many :meetings
end
class Meeting < ActiveRecord::Base
belongs_to :event
end
How to write mysql query to search all events group_by meeting DATE(start_at)?
Event.inludes(:meetings).group ...
As a result I want to get a Hash:
{"2014-01-24"=>[#<Event id: , title: "First", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">, #<Event id: 2, title: "Second", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">], "2013-01-29"=>[#<Event id: 3, title: "Third", created_at: "2013-01-29 05:30:40", updated_at: "2014-01-29 05:30:40">], ...]}
P.S: I am using PostgreSQL
Now I get it by this way:
hash = {}
Meeting.where("extract(month from start_at) = ?", Date.today.month).pluck('DATE(start_at)').uniq.each do |date|
hash[date] = Event.includes(:meetings).where("DATE(meetings.start_at) = ?", date).references(:meetings)
end
But it produced so many queries to the database :(

Event.joins(:meetings).group('meetings.start_at') should do. But want you want is a group_by array method http://apidock.com/ruby/Enumerable/group_by so what you should do is
#events.group_by {|e| e.meeting.start_date}
In case of many to many you should be better off with
result = Hash.new
Meeting.include(:events).each {|m| result[m.start_at]||=[]; result[m.start_at] << m.events}
and with one liner you could
Meeting.includes(:events).inject(Hash.new) do |result, m|
result[m.start_at]||=[]
result[m.start_at] << w.events
result
end
This code should execute two database calls i think

Related

Rails where.not skips some records

I try to fetch all videos except the ones with categories [20,21,22]
this is my query
#cc = Video.joins(:categories).where.not(categories: { id: [20,21,22]})
but when I do #cc.find(113).categories I get this
#<ActiveRecord::Associations::CollectionProxy
[#<Category id: 21, title: "music">, #<Category id: 22, title: "movies">,
#<Category id: 28, title: "collage">]>
What am I doing wrong?
Try this,
#cc = Video.includes(:categories).references(:categories).where.not(categories: { id: [20,21,22]})
Refer,
https://robots.thoughtbot.com/activerecords-wherenot
Try this:
array = [21,22,23]
#cc = Video.joins(:categories).where("category.id not in (?)", array)
EDIT
Think I spot the problem. Suppose your Video model is in a has_many relationship with Category. So you should do:
class Video < ActiveRecord::Base
has_many :categories
has_many :excluded, -> (array) { where("id not in (?)", array) }, class_name: 'Category'
end
And you call it like that:
Video.find(113).excluded([21,22,23])
You are doing a wrong query.
Try with:
Video.where.not(id: Video.joins(:categories).where(categories: { id: [20,21,22]}).pluck(:id))

Query of searchkick get wrong result

Query of searchkick get wrong result.
I have this one record in db:
2.2.0 :047 > Product.first
#<Product id: 1, title: "Ball", description: "<p>Ball</p>\r\n", price: 10, material: "lalala", created_at: "2015-04-21 04:30:53", updated_at: "2015-04-21 04:30:53", preview: "images__1_.jpg", count: 20>
in controller Product and action search i have this code:
def search
#products = Product.search "*", where:
{
count: 10..18
}
end
and after this, a get result, that the with this values, count is exist.
But in db count = 20. And all time, get wrong result. I don't know why?
I get not correct result, 'cause i don't have hash, in model and need reindex of Model.
In model:
def search_data
{
count: count,
price: price,
title: title,
category_id: categories.map($:id)
}
end
and after that, in rails console wrote:
Product.reindex

Sort records into buckets with Rails

I have a Game model that has a datetime, storing the date and time the game will be played. I'd like to put those records into an array of arrays of each date.
Example of a possible outcome array:
[
[
#<Game id: 7, datetime: "2014-03-03 05:30:00">,
#<Game id: 12, datetime: "2014-03-03 11:30:00">,
#<Game id: 14, datetime: "2014-03-03 12:30:00">,
#<Game id: 17, datetime: "2014-03-03 12:45:00">
],
[
#<Game id: 1, datetime: "2014-04-06 05:30:00">,
#<Game id: 2, datetime: "2014-04-06 11:30:00">,
#<Game id: 4, datetime: "2014-04-06 12:30:00">,
#<Game id: 7, datetime: "2014-04-06 12:45:00">
]
]
Here's what I'm trying so far
#games = Array.new
Game.all.each do |game|
# in my model I have a function date that gets the date part of my datetime
quit = false
index = 0
while !quit do
if index < #games.length
quit = true
#games << [game]
elsif #games[index].first.date == game.date
quit = true
#games[index] << game
end
end
end
But this seems not very ruby-like and probably not the most efficient way. Is there a better approach for this?
This is probably not the most efficient way to do this, but it is short and readable:
#games = {}
Game.all.each do |game|
#games[game.date] = [] if !#games.has_key? game.date
#games[game.date] << game
end
#games = #games.values
Note that it is idiomatic Ruby to use array and hash literals [] and {} instead of their respective Array.new and Hash.new constructors.
Update
Here's an even better way, using the group_by method of Enumerable, which collections can include as a module:
#games = Game.all.group_by { |game| game.date }
Note that this will return a hash using the dates as keys, but you can easily turn this into an array by calling the values method on a hash.

Creating an array of values in FactoryGirl, each of which is unique

I have this factory defined:
factory :post, :parent => :post_without_countries, class: Post do |p|
p.country_ids {|country_ids| [country_ids.association(:country), country_ids.association(:country)]}
end
And I'm wanting it to output two unique countries. Instead it just inserts the same country as the association twice:
#<Post id: nil, title: "Atque id dolorum consequatur.", body: "Praesentium saepe ullam magnam. Voluptatum tempora ...", created_at: nil, updated_at: nil, user_id: 1>
[#<Country id: 1, name: "Dominican Republic", isocode: "lyb", created_at: "2012-10-20 13:52:18", updated_at: "2012-10-20 13:52:18">, #<Country id: 1, name: "Dominican Republic", isocode: "lyb", created_at: "2012-10-20 13:52:18", updated_at: "2012-10-20 13:52:18">]
Any ideas?
Better use the build_list or create_list methods:
post.countries = create_list(:country, 2)
Instead of doing:
2.times { post.countries << FactoryGirl.create(:country) }
in RSpec, you can make an after_create hook like this:
after_create do |post|
2.times { post.countries << FactoryGirl.create(:country) }
end
If you need to customize the number of times you want to create a country, you can make a transient attribute:
#in the post factory definition
ignore do
num_countries 0 #default to zero
end
#different after_create
after_create do |post, proxy|
proxy.num_countries.times { post.countries << FactoryGirl.create(:country) }
end
It looks like factory girl might not be iterating properly. The two questions that pop into my mind are.
Are you using FactoryGirl.build when you meant to use FactoryGirl.create?
Have you tried replacing p.country_ids with p.sequence(:country_ids)
I hope that those point you in the right direction. If not, perhaps more information?
OK, I fixed this by taking my creation of the many Countries relationship out the factory and just creating it in RSpec instead:
post = FactoryGirl.build(:post)
2.times { post.countries << FactoryGirl.create(:country) }

looping and casting problem in rails

I've got following method in User model
def get_employees
#employees = []
groups.each do |i|
#employees << Group.find(i).employees
end
#employees
end
This is what the console prints when I call this method:
> >> User.find(4).get_employees
> => [[#<Employee id: 4, first_name: "test", last_name: "test1",
> email_address: "test#gmail.com",
> created_at: "2010-08-25 04:23:02",
> updated_at: "2010-08-25 04:23:02">,
> #<Employee id: 5, first_name: "hello", last_name: "hello1", email_address:
> "hello#gmail.com", created_at:
> "2010-08-25 04:51:37", updated_at:
> "2010-08-25 04:51:37">]]
however, the following code does not work:
>> #user.get_employees.each{|i| p i.first_name}
NoMethodError: undefined method `first_name' for #<Class:0x9e372f0>
What do I need to do in order to get the first_name of the employees from the loop?
The Group.find(i).employees call returns an array, so your get_employees method is returning an array of arrays. Replacing the last line of get_employees with #employees.flatten! should do the trick.
Looks to me that the variable i is still an array. You declare #employee as an empty array and you insert another array which is what is returned by Group.find(i).employees.
i[0] should contain:
#<Employee id: 4, first_name: "test", last_name: "test1",
> email_address: "test#gmail.com",
> created_at: "2010-08-25 04:23:02",
> updated_at: "2010-08-25 04:23:02">,
> #<Employee id: 5, first_name: "hello", last_name: "hello1", email_address:
> "hello#gmail.com", created_at:
> "2010-08-25 04:51:37", updated_at:
> "2010-08-25 04:51:37">
As the previous poster noted, you've got an array within an array. Why recreate some logic that is already built into rails? I would have done something like this in the user model:
class User < ActiveRecord::Base
has_many :groups
has_many :employees, :through => :groups
end
Then you can just do User.employees

Resources