rails categorizing nested resources - ruby-on-rails

I have users with nested resources posts. I have a page that shows all posts from all users. However, on the left column I want to categorize them by user, as follows, with links to a URL with a parameter like ?username=laura, for example, to show only their posts. I'm pretty sure I can figure out the URL params, but getting them sorted, as shown below, is difficult for me. Any suggestions? Thanks!
Adam (23)
Brad (12)
Mike (1)

Users.includes(:posts).map{|u| [u.name, u.posts.count, u.id]}.sort
Will give you a sorted array of users, post counts, and user id like
[
["Adam", 23, 23],
["Brad", 12, 2],
["Mike", 1, 44]
]
You can this user the first two elements for your link and the user id for the link destination.
This might get slow with thousands of users and/or posts, but it should get you started.

You should sort them:
#sorted_users = users.sort { |left,right| left.posts.count <=> right.posts.count }
Also, if you have a cache_column for posts, you should probably use the database to implement this.

Related

Include a left joined has_many association with condition

There is an association query I seem to be unable to do without triggering a N+1 query.
Suppose I host Parties. I have many Friends, and each time a friend comes to a party, they create a Presence.
And so:
Presence.belongs_to :party
Presence.belongs_to :friend
Friend.has_many :presences
Party.has_many :presences
So far so good.
I want to obtain a list of every one of my Friends, knowing whether or not they are present at this Party, without triggering a N+1 query.
My dataset would look like this:
friends: [
{name: "Dave Vlopment", presences: [{created_at: "8pm", party_id: 2012}]},
{name: "Brett E. Hardproblem", presences: [nil]},
{name: "Ann Plosswan-Quarry", presences: [{created_at: "10pm", party_id: 2012}]},
...
]
and so on.
I have a lot of friends and do a lot of parties, of course. (This is of course a fictional example.)
I would do:
Friend.all.includes(:presence).map{ |them| them.parties }
# But then, `them.parties` is not filtered to tonight's party.
Friend.all.includes(:presence).map{ |them| them.parties.where(party_id: pid) }
# And there I have an N+1.
I could always filter at the Ruby layer:
Friend.all.includes(:presence).map{ |them| them.parties.select{ |it| it.party_id = party.id } }
But this works pretty badly with as_json(includes: {}) and so on. I'm discovering this is very error-prone as I'll be making calculations on the results.
And I make a lot of parties, you know? (still fictional)
If I where on the first query, I lose the left join:
Friend.all.includes(:presence).where(party: party)
I have no idea that tonight, Brett and a bunch of friends, who are always there, are absent. (this one is not guaranteed to be a fictional experience)
I will only see friends who are present.
And if I go through party, well of course I will not see who is absent either.
Now I know there are ways I can do this in SQL, and other ways we can wrangle around some Ruby to pull it together.
However, I'm looking for a "first-class" way to do this in Activerecord, without getting N+1s.
Is there a way to do this using only the Activerecord tools? I haven't found anything yet.
I'm not sure whether this meets your expectation about "first-class" way or not.
But you can use this approach to avoids N+1
# fetch all friends
friends = Friend.all
# fetch all presences. grouped by friend_id
grouped_presences = Presence.all.group_by(&:friend_id)
# arrange data
data = []
friends.each do |friend|
json = friend.as_json
json["presences"] = grouped_presences[friend.id].as_json
data << json
end
puts data
It only executes 2 queries
SELECT `friends`.* FROM `friends`
SELECT `presences`.* FROM `presences`

Rails - Create order and order-rows with REST api

I just have a question regarding how to implement some logic.
Im building a API that allows the client to create orders.
This is solved by a OrderController#create so no problem!
Now, the issue is that an order can have many order-rows, all the relations are set correct but where should i create the order-rows in for the order?
Should the OrderController handle this or should i have a new controller that creates the order-rows for the particular order?
The clients post is sending the following json-data:
{
"status": "paid",
"total_sum": 20,
"payment": "card",
"order_rows": [
{
"id": 12,
},
{
"id":13
}
]
}
I ran into something similar with a project I'm working on now. The best (and long term simplest) solution was definitely to make a whole new model/controller.
*Order
status (should be an int or enum probably)
total (should loop through all order rows and total)
payment (should be an int or enum probably)
has_many order_rows
**OrderRow
belongs_to Order
item_sku
item_name
item_descr
item_cost
etc. etc.
This allows you to easily search for not just items, but orders that include items by name or sku, orders that include items by description.
Your totals are dynamic.
You can retrieve total order numbers or movement numbers on a per item basis.
It is so much easier to create and update orders.
The benefits go on.
It can easily be scoped;
scope :this_orders_rows, -> (order_id) {where(order_id: order_id)}
And it saves you from having to parse through hashes and arrays everytime.
To get technical about it, your order_controller should control ONLY your orders. If you start adding in a heap of other code to read through the arrays its going to get VERY cluttered. It's always better to move that to some other area.

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')

Get all ids from a collection

I got my collection items like this :
hotels = Hotel.where('selection = ?', 1).limit(4)
How can I get all ids of this items without a loop? Can i use something like :
hotels.ids ?
Thank you
What about trying hotels.map(&:id) or hotels.map{|h| h.id }?
They both mean the same thing to Ruby, the first one is nicer to accustomed ruby-ists usually, whilst the second one is easier to understand for beginners.
If you only need an array with all the ids you should use pluck as it makes the right query and you don't have to use any ruby. Besides it won't have to instantiate a Hotel object for each record returned from the DB. (way faster).
Hotel.where(selection: 1).pluck(:id)
# SELECT hotels.id FROM hotels WHERE hotels.selection = 1
# => [2, 3]
If you are using Rails > 4, you can use the ids method:
Person.ids # SELECT people.id from people
More info: http://apidock.com/rails/ActiveRecord/Calculations/ids
You can also pull just the id's.
hotels.select(:id).where(selection: 1)

Rails select distinct

in a scenario i want to retrieve the records with different values so i used distinct for that,
Book.where(["user_id = ?",#user_id]).select('distinct title_id')
`this, only retrives the records like this [#<Book title_id: 30>, #<Book title_id: 31> ]`
but i want to fetch the id of Book as well along with title_id
so, please advise me how to work on this
thanks
use grouping:
Book.where(:user_id => #user.id).grouped('title_id')
problem is that if you do grouping you can't have different book ids, they are all grouped into single row. You can use GROUP_CONCAT to workaround that:
Book...select('books.*, GROUP_CONCAT(id) as ids')
that way you'll have book ids attribute for every group

Resources