Query in Rails to fetch from database - ruby-on-rails

I have a table Info(:name, :friend_id) and another table Friends(:id, :network). The attribute :network can be Facebook/Gmail/Linkedin. I want to count all my friends from all services like count of friends from FB, Gmail and Linkedin. I have a friends_list
friends_list = Info.where(:name => my_name)
I don't want to iterate over friends_list and find for each friend_id the normal way. Is there a single line query which can give me the count of all friends from Facebook?

You can use #count with #group, the query might need a little tweaking, but it's generally possible.
friends_counts = Friends.joins(:info).group(:network).where(infos: {name: my_name}).count
This should return an array of hashes, the hash key is the network id, and the hash value is the number of friends in that network.

Related

Sort users based on most recent response to a particular answer in a survey?

I'm trying to sort users based on their most recent response to a certain question in a survey using Rails 5, PostgeSQL 9.4.5
So far I've got:
User.includes(responses: [answer: :question]).where(questions: {id: X}).order(...)
Not sure what to put in the order. The responses all have numerical 'scores' representing which answer it is. I'm imagining something at the end like:
.order("answers.score ASC")
But I'm struggling to get the two to attach. I only want to sort the Users by their most recent answer to that specific question. (They can take the survey multiple times)
I'm assuming I need to set a string function in some SELECT, but I'm struggling to wrap my head around it.
Any help is appreciated!
You can print the actual SQL of the rails query like this:
User.includes(responses: [answer: :question]).where(questions: {id: X}).to_sql
Then you can order by the right table.field (find the table name in the SQL returned by to_sql) and the field in db/schema.rb
It should be a created_at. User...order('responses.created_at DESC')
UPDATE
But this will sort all responses and not users by their last response on question, as you've commented below.
In this case you have to:
group the users by their responses
calculate the last response(MAX(user_responses.created_at)) for each user
sort the users by last response
Something like this:
User
.includes(responses: [answer: :question])
.where(questions: {id: X})
.group('users.id')
.order('MAX(responses.created_at) DESC')

Order a set of results by an externally calculated score

I have a site that fetches the ID and SCORE of records for a user from an external source.
I want to then fetch the records that match the ID's, ordered by each ID's SCORE.
The ID's and SCORES come to the app as XML which I'm currently parsing into an array (of hashes):
[{90=>279}, {32=>400}]
How can I use that score to order the records returned?
src_data = [{90=>279}, {32=>400}]
ids = src_data.sort_by{|hash| hash.values.first}.reverse.collect(&:keys).flatten
# => [32,90]
ids should now be an array of ids, in order of highest score to lowest score. Now we can do the find.
#users = User.where("id in (?)", ids).order("ORDER BY FIELD(ID,#{ids.join(',')})")
This should generate sql like
select * from users where id in (32,90) ORDER BY FIELD(ID,32,90);
which should give you the users back in the right order.
EDIT - for postgres
Postgresql doesn't have the field() function. You can define your own, see Simulating MySQL's ORDER BY FIELD() in Postgresql, but you might prefer to not bother and just reorder the results after you get them:
#users = User.where("id in (?)", ids).sort_by{|user| ids.index(user.id)}
If it's a standard ruby array you should be able to sort it by using "sort_by".
Two examples of sorting based on the keys / values:
results = [{90=>279}, {32=>400}]
results.sort_by{|result| result.keys[0]}
# => [{32=>400}, {90=>279}]
results.sort_by{|result| result.values[0]}
# => [{90=>279}, {32=>400}]
Check this blogpost on more information regarding sort_by and sort in Ruby:
http://brandon.dimcheff.com/2009/11/18/rubys-sort-vs-sort-by.html

Count returns the count of all joined data instead of the main object

I'm trying to count users who have dogs.
User.joins(:pets).where("pets.type = ?", :dog).count
This returns the count of the users + their dogs combined, instead i just want the count of actual users.
What am i doing wrong?
Update
I've also tried to just fetch the users using the above query and it returns an array of the same users repeated multiple times depending on how many dogs they have.
Try this:
User.joins(:pets).where("pets.type = ?", :dog).count(distinct: true)
See api doc.
The joins would return all the duplicate rows for which the user has multiple associations...
For example if a user, U1 has one dog and user, U2 has two dogs then total three rows would be returned by the joins....so instead of using joins, try to use the includes option...
refer to this Railscast, http://railscasts.com/episodes/181-include-vs-joins for more...

Ruby on Rails 3: sort array based on data from ActiveRecord

I have one array, which are IDs from one ActiveRecord table.
so I would like to sort that array based on last name which is associated with that ID...how can I do that?
To clarify:
array #students=[], inside are IDs and I would like to sort by Student.find(ID).last
Thank you.
Dorijan
Without fully understanding the question, if you're given a list of id's, you can sort by last_name when you're doing the query:
Student.where("id IN (?)", #students).order(:last_name)
Of course, this assumes that #students is an array of ids and nothing else.
Responding to your comment, I'm not sure why you'd need to do that, but if your #student array is just a list of ids ignorant of the Student model and its attributes, and you would still like to order that array, you can do this:
#students = Student.where("id IN (?)", #students).order(:last_name).collect(&:id)
This will return an array of ids sorted by last name. But again, I don't really know what you have going on behind the scenes, and I'm not sure what you're asking for.
Based on your comment, you want to do the following:
Take in a list of IDs of students as input
Return a list of IDs ordered by the student's last name in the database.
You should be able to do the following:
Student.where(:id => #ids).order(:last_name).map(&:id)
Breaking this down:
where(:id => #ids) only selects Students with an ID in the ID array.
order(:last_name) sorts the results by last name.
map(&:id) takes in an array of Students and returns just the ID column. Essentially, the method in brackets (which is a shortcut for calling id for each student) is called for each student found, and the return values are assembled into a new array (which will only contain the ids).
Some gotchas:
If an ID doesn't exist in the database, it will be excluded from the results - if your result array is smaller than your input array, you may be trying to access a record that no longer exists.
If the Students table has a lot of columns, you may want to consider calling select(:id) so that you don't pull every column of the Student records from the database.
Student.find( #students ).sort_by { |s| s.last_name }

Problem with sorting by row ( special case )

I have a requirement for sorting Contacts records by primary_contact_no.
My Contact fields contain primary_contact_no ,email , mobile_no.
this is no brainier....
BUT my view requires me to show mobile_no under Contact Number(view label) when primary_contact_no is not present.
Contacts.find(:all, :order => "primary_contact_no")
Now When i sort it by primary_contact , in the view , the records where these fields are absent get replaced with mobile_no but since they are already sorted by contact_no they appear at the bottom of the search result.
How can i combine the two results ( in case primary_contact is not present and carry out search on the combined record )
Is there any other solution to the problem where i can combine the row search records or something like that???
P.S.
I have used will paginate.
You could order once you retrieve them from the database.
So
contacts = Contact.all
u.sort!{|a,b| a.con_number<=> b.con_number}
Then in your Contact Model
def con_number
primary_contact_no||mobile_no
end
MySQL and PostgreSQL both have COALESCE function, so you can do something like:
Contacts.find(:all, :order => "COALESCE(primary_contact_no,mobile_no)")
to sort the records as you want. But beware, using sql functions and raw sql has its caveats. If you decide to switch databases, you have to check if each raw sql and sql function you used like this is supported in your new RDBMSI.
I would not sort the records in my application, as that means, I can not use pagination of will paginate to select limited data and have to retrieve full set of records, sort them and then use the relevant records based on pagination parameters. It will increase the response time consistently as the contacts table grows.

Resources