how to select fields from joined table properly? - ruby-on-rails

Here are my simplified models, I have joined models:
class Apartment < ApplicationRecord
has_many :towers
end
and
class Tower < ApplicationRecord
belong_to :apartment
end
and then I tried to join both tables in controller. I also tried it in rails console like this :
Apartment.joins(:towers).select('apartments.id', 'apartments.name', 'towers.id' , 'towers.name')
the problem is above query only returns
apartments.id and apartments.name
also tried to use alias like this, still no luck
Apartment.joins(:towers).select('apartments.id', 'apartments.name',
'towers.id as towerid' , 'towers.name as towername')
I have confirmed that all towers have an apartment, I know i could do this to get 1 record
Apartment.joins(:towers).select('apartments.id', 'apartments.name',
'towers.id' , 'towers.name').first.towers.id
and etc, but i need all records and all those fields, please advice.
here is the latest result i got in rails console :
Apartment Load (1.0ms) SELECT apartments.id, apartments.name, towers.id as towerid, towers.
tower_name as towername FROM `apartments` INNER JOIN `towers` ON `towers`.`apt_id` = `
apartments`.`id`
=> #<ActiveRecord::Relation [#<Apartment id: 5, name: "basura">, #<Apartment id: 5, apt_
name: "basura">, #<Apartment id: 124, name: "hydra">, #<Apartment id: 124, name: "hy
dra">, #<Apartment id: 126, name: "mediterania">, #<Apartment id: 126, name: "mediterania">, #<Apartment id: 142, name: "apartement gajah mada">, #<Apartment id: 142, name: "apartement gajah mada">]>
as you can see, above query only return 2 fields, i need the result to be like this :
#<Apartment id: 126, name: "mediterania", tower_id: 12, tower_name: "tower A">,
#<Apartment id: 126, name: "mediterania", tower_id: 15, tower_name: "tower F">
etcc...

The only way I see this is possible is using as
q = Apartment.joins(:towers).select('apartments.id, apartments.name, towers.id as t_id, towers.name as t_name')
q.first.t_id
q.first.t_name
Why first.towers.id will not work?
apartment.towers will return ActiveRecord::Associations::CollectionProxy. You can think of it as a collection of towers. In SQL query you are referring to towers table. But when you run apartment.towers.id you are calling id on CollectionProxy object which will not work. You can get first tower using towers.first.
Regarding,
Apt Load (1.0ms) SELECT apts.id, apts.apt_name, towers.id as towerid, towers.
tower_name as towername FROM `apts` INNER JOIN `towers` ON `towers`.`apt_id` = `
apts`.`id`
=> #<ActiveRecord::Relation [#<Apt id: 5, apt_name: "basura">, #<Apt id: 5, apt_
name: "basura">, #<Apt id: 124, apt_name: "hydra">, #<Apt id: 124, apt_name: "hy
dra">, #<Apt id: 126, apt_name: "mediterania">, #<Apt id: 126, apt_name: "mediterania">, #<Apt id: 142, apt_name: "apartement gajah mada">, #<Apt id: 142, apt_name: "apartement gajah mada">]>
What you see in console is result returned by inspsect method. The inspect method is not designed to show non column attributes. Hence even if you have towername in memory it will only show attributes which are columns of Apartment model.
More about inspect
I also recommend to try following:
Apartment.joins(:towers).pluck('apartments.id, apartments.name, towers.id as t_id, towers.name as t_name')
Above statement will get all data in array. The same result you get with select but select will not load all data in array.

You should use
Apartment.joins(:towers).select('apartments.id, apartments.name, towers.id , towers.name')
that is all column names inside a single string.
Refer this.

Rails 7 adds support for such queries, now select accepts hash
You can use aliases like this
Apartment
.joins(:towers)
.select(
apartments: { id: :apartment_id, name: :apartment_name },
towers: { id: :tower_id, name: :tower_name }
)
It produces such query
SELECT apartments.id AS apartment_id, apartments.name AS apartments_name,
towers.id AS tower_id, towers.name AS tower_name
FROM apartments
INNER JOIN towers ON towers.apartment_id = apartments.id

You can try the alias names like below
Apartment.joins(:towers).select('apartments.id as apartment_id, apartments.name as apartment_name, towers.id as tower_id , towers.name as tower_name)

You can try this
Apartment.joins(:towers).select('apartments.id as id, apartments.name as apartment_name, towers.id as tower_id , towers.name as tower_name)
you will get the response like this
#ActiveRecord::Relation [#<Apt id: 126, apt_name: "mediterania", tower_id: 12, tower_name: "tower A">,
#<Apt id: 126, apt_name: "mediterania", tower_id: 15, tower_name: "tower F">]>

Related

How to use the PostgreSQL JSON array query ActiveRecord::Relation in Ruby on Rails

I want to query ActiveRecord::Relation with 51 fields.
the data structure is like this:
matings: {"ids"=>[50, 51, 64]}
or
matings: {"ids"=>51}
If I do:
MouseColony.where("CAST(matings AS TEXT) LIKE ?", "%51%")
=>#<ActiveRecord::Relation [
#<MouseColony id: 604, animal_no: "a0008", animal_desc: "", gender: "M♂", source: "外购", animal_status: "配对", cage_id: nil, generation: 0, birth_date: "2018-12-25", weaning_date: "2019-01-15", disable_date: nil, received_date: "2019-03-20", created_at: "2019-06-03 02:45:03", updated_at: "2019-06-03 03:14:37", user_id: 1, strain_id: 1, mating_id: 64, litter_id: nil, purchase_mouse_number: "17203", age: 223, mating_quantity: 3, matings: {"ids"=>[50, 51, 64]}, genotype_id: 10, experiment_id: nil, experiment_date: nil, age_weeks: 32>,
#<MouseColony id: 624, animal_no: "a0028", animal_desc: "", gender: "F♀", source: "外购", animal_status: "配对", cage_id: nil, generation: 0, birth_date: "2018-12-25", weaning_date: "2019-01-15", disable_date: nil, received_date: "2019-03-20", created_at: "2019-06-03 02:50:07", updated_at: "2019-06-03 03:09:11", user_id: 1, strain_id: 1, mating_id: 51, litter_id: nil, purchase_mouse_number: "17138", age: 223, mating_quantity: 5, matings: {"ids"=>51}, genotype_id: 9, experiment_id: nil, experiment_date: nil, age_weeks: 32>
]>
I tried to use MouseColony.where("CAST(matings ->> 'ids' AS TEXT) LIKE ?", "%51%") or using MouseColony.where("matings ->> 'ids' = ?", "51"), but the result is such a MouseColony Load (1.0ms) SELECT "mouse_colonies".* FROM "mouse_colonies" WHERE (CAST(matings ->> 'ids' AS TEXT) LIKE '%51%') LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
I've also tried to use this:
MouseColony.where("matings #>> '{ids}' = ?", "51")
But still can't find any data.
I think my problem might be here:
My models: mouse_colony.rb
store :matings, :accessors => [:ids], coder: JSON
and I storage record like this:
#mouse_colony.matings[:ids] = [50, 51, 64]`` #mouse_colony.save
You can try using the "contains" operator #>:
MouseColony.where("matings->'ids' #> '?'", 51)
You should be able to use the LIKE operator like this:
MouseColony.where("matings::json->>'ids' LIKE ?", "%51%")

Select rows having max column value in rails

I have an array of ActiveRecord of table Person (:id, :name, :city_id, :work_id, :date). For eg :
[<Person id: 4, name: John, city_id: 10, work_id: 1, date: 2015-10-10>,
<Person id: 5, name: Jack, city_id: 11, work_id: 2, date: 2015-10-08>,
<Person id: 8, name: John, city_id: 10, work_id: 2, date: 2015-10-11>,
<Person id: 9, name: John, city_id: 10, work_id: 3, date: 2015-10-11>,]
For a particular person, I want the rows with the maximum date. So, for Jack, I'll get row with id = 5. For John, I'll get the rows with id = [8,9] as the date is maximum for these rows. I can't figure out an efficient way to to this in Rails. Please help.
You can use maximum class method of rails.
Try this:
Person.find_all_by_date(self.maximum('date'))
See some details here.

select all the values of the records belonging to a specific field of the database and save them in a array Active Record

I have the following set of records . I want to save in a array called #texts only
the values of the field text doing an each o a for
1.9.3-p547 :074 > Tweet.all
Tweet Load (0.3ms) SELECT "tweets".* FROM "tweets"
=> [#<Tweet id: 1, text: "hola a todos", zombie_id: 5, created_at: "2014-12-29 23:52:40", updated_at: "2014-12-29 23:52:40">, #<Tweet id: 2, text: "hola como estas", zombie_id: 5, created_at: "2014-12-30 00:09:40", updated_at: "2014-12-30 00:09:40">, #<Tweet id: 3, text: "hello", zombie_id: 5, created_at: "2014-12-30 12:44:41", updated_at: "2014-12-30 12:44:41">]
1.9.3-p547 :075 >
Example
#texts=["hola a todos","hola cmo estas", "hello"];
I would do this:
#texts = Tweet.all.map(&:text)
Or:
#texts = Tweet.pluck(:text)
You should use pluck, as for the documentation:
Use pluck as a shortcut to select one or more attributes without
loading a bunch of records just to grab the attributes you want.
This way you are only select the desired columns and not the "full" record.

Nested group query in Rails

I have a list of Answers:
Answer.last
Answer Load (0.8ms) SELECT "answers".* FROM "answers" ORDER BY "answers"."id" DESC LIMIT 1
=> #<Answer id: 235, question_id: 15, choice_id: 23, user_id: 3, created_at: "2013-08-28 23:51:24", updated_at: "2013-08-28 23:51:24", club_id: 11>
As you can see, each Answer belongs to a question, choice, user and club. I'm trying to create a nested group query to produce a hash of:
{ club_id { choice_id => answers_count, choice_id => answers_count}, etc, etc }
What I have so far:
Answer.where(:question_id => 14).group(:club_id, :choice_id).count
Which produces:
{[4, 21]=>7, [11, 21]=>2, [4, 22]=>4, [11, 22]=>7}
This is:
{ [club_id, choice_id] => answers_count, etc, etc }
Is there anyway to do what I require in one query or will I have to merge/re-jig this hash? If so, how can I do this?
As #tadman says, the result you're seeing is all the query can get you. If you want a custom format, you'll need to run it through an algorithm. Something like this should work for you:
result = Answer.where(:question_id => 14).group(:club_id, :choice_id).count
better_result = result.each_with_object({}) do |((club_id, choice_id), answers_count), m|
m[club_id] ||= {}
m[club_id][choice_id] = answers_count
end

Unexpected behaviour of array.count in ruby

Hi i am using Rails3 when i puts array of subscribers like this
#subscribers = User.all
and puts it.gives me this array
[#<User id: 62, is_activated: true, subscriber: "TEST_DB2", ports_order: 100, created_at: "2012-05-21 14:47:48">, #<User id: 66, is_activated: true, subscriber: "JOHI", ports_order: 100, created_at: "2012-05-22 12:06:19">, #<User id: 68, is_activated: true, subscriber: "ALI-NAQWI", ports_order: 100, created_at: "2012-05-24 11:01:22">]
And when i give command
#subscribers.count #it give me 0 count
Why?????????
Try length instead, it can be a bug in AREL :)
That he is trying to lazy do something. He is giving back proxy object so it can be a problem there. Length will always work on your result value ~ array.

Resources