RoR: How to sort an array with the help of scopes - ruby-on-rails

I have an array #products. Each element of the array is a hash, containing a few fields (but not all) from Product table and the corresponding values.
I have a scope descend_by_popularity in Product which allows me to sort the products based on popularity field. I'd like to sort the array #products using this scope.
What I tried:
#product_group = Array.new
#products.each do |product|
#product_group.push(Product.find(product['id']))
end
#product_group1 = #product_group.descend_by_popularity
But this gives me error:
undefined method `descend_by_popularity' for #<Array:0xb2497200>
I also want to change the sorted Product list back to the format of #products array.
Thanks

Scopes only make sense within the ActiveRecord context for requests to the database (since it is used to change the SQL query). What you did is throwing a lot of products into an array. This array then knows nothing about the scope anymore. You would have to use the scope when you create the #products object. (and it does not seem to make a lot of sense to move the result of a query into an array)
So something like
#products = Product.descend_by_popularity.where(some more stuff)
should work for you. After that you should have the records in the order defined by the scope and can then either use them directly or still push them into an array if that's what you want to do.
With the updated info from the comments it looks like maybe the best way to go would be to first collect only the Product ids from the solr response into an array and then run that as search together with your scope:
#product_group = #products.map{|product| product.id}
#result = Product.where(id: #product_group).descend_by_popularity
this should technically work, peformance is a different question. I would consider aggregating this data into the Solr document, if it doesn't change too often.
Now assuming you are only interested in the order of products as such, you could do something like this to get #products into this order:
#result.map{|r| #products.find{|p| p[:id] == r.id}
though this may slow down things a bit.

Try this: find_by_id as params
#product_group = Array.new
#products.each do |product|
#product_group.push(Product.find(params['id']))
end
and return the array of #product_group
#product_group1 = #product_group.descend_by_popularity

Related

Find records related to other records in controller (ruby on rails)

I'm grabbing a list of users and storing in #users.
Now I need to find properties related to only this list of users I have queried.
if params[:company].present?
#users = User.where(parent_id: params[:company]).or(User.where(id: params[:company]))
##properties = #properties.where(user_id: params[:company])
end
I would basically like to include #users inside #properties.where()
I need to get each property that has a user_id present in my #users array
edit:
I just did the following which gives me the result, however, I'm sure there's a much better way of doing this via activerecord:
ids = []
#users.each do |user|
ids.push(user.id)
end
#properties = #properties.where(user_id: ids)
#properties.where(user_id: #users.ids)
That should work. It'll take the id of user ids and perform a filter using the IN clause.
Perhaps adding your models and their relationships we can think about something better.

Iterating through IDs Ruby on Rails

I have been advised not to store arrays in my DB, but instead just IDs. In my project, I am storing IDs for line-items. If I don't have a line_item OBJECT, but only IDs, could I still iterate through a loop to get all of the data associated with it?
To explain further...
Traditionally I feel like I have
#line_item
>> <LineItem id: 63, product_id: 2, created_at: time, (etc) >
Then I could do something like
#line_item.title
>> "T-Shirt:Small"
But what if I did
#line_item
and got
>>[12, 14]
Could I do a #line.item.each and get to all the information I need from the just the IDs in the view?? or would I have to create my own scary method?
Hopefully I've posed my question understandably.
Thanks.
You can do this:
#items = [12,14] # your list of ids you got from somewhere
#items.each do |id|
#line_item = LineItem.find(id) # Get the instance for that id.
# do stuff.
end
You could also do something like this:
#line_items = LineItem.find(#id_list)
If the reason you are storing lists of ids is to capture relationships then you should look at the belongs_to and has_many relationships that rails provides. De-normalising data by storing lists is nasty because you don't know how long your list will be. It stops your database from indexing things properly and it's hard to maintain.
I suggest you to use:
LineItem.where(id: [12,14])
As it will return always an array of items. If you try to find for an item who's not in the database you will find a record not found error. This will render an empty array in case no record is found.
As long as you have an array, empty or not, you won't have to rescue the .each method because it won't fail in any case.

Ruby on Rails: where returning nil

In my app I'm obtaining a certain category, and I'm filtering the associated items based on their name.
The following code should be pretty clear:
categories = Category.where(:id => params[:category_id]).includes(:items).where("lower(items.name) like ?", "%#{params[:keywords].downcase}%")
However, if the name filter excludes all the items, the categories object returned by where is nil. Is this the expected behaviour? How can I get the category even either items exist or not?
The easiest way might be to just split the query:
#category = Category.find(params[:category_id])
#items = #category.items.where("lower(items.name) like ?", "%#{params[:keywords].downcase}%")
Based on your code it seems like category_id references only 1 category so I've changed it to singular.
You should look into doing an OUTER JOIN against the items table which will give you categories regardless of whether or not their items meet the name filter.

Ruby - Order an Array

I have a table with 2 columns. "car" "year". I want to do a collect into an array of all cars and then sort them by year. How do I do that?
<% #cars_pre = Car.find(:all) %>
<% #cars = #cars_pre.collect {|x| x.car} %>
I need the steps to order the #cars array by year, keep in mind I am not keeping year values in the #cars array, just cars. Thanks in advance.
From the code sample, I'm guessing you are using Ruby on Rails. If you are, I would recommend you allow your database to order the results. For example:
#cars = Card.order("year").all
Use sort:
#cars.sort_by!(:year) # in-place sort
Why remove the information by which you wish to sort then sort it by that information? Just keep the information in the array–there's no need to remove it. Plus you need it for the sort.
That said, why not sort it on the DB side using order("year")?

rails where() sql query on array

I'll explain this as best as possible. I have a query on user posts:
#selected_posts = Posts.where(:category => "Baseball")
I would like to write the following statement. Here it is in pseudo terms:
User.where(user has a post in #selected_posts)
Keep in mind that I have a many to many relationship setup so post.user is usable.
Any ideas?
/EDIT
#posts_matches = User.includes(#selected_posts).map{ |user|
[user.company_name, user.posts.count, user.username]
}.sort
Basically, I need the above to work so that it uses the users that HAVE posts in selected_posts and not EVERY user we have in our database.
Try this:
user.posts.where("posts.category = ?", "Baseball")
Edit 1:
user.posts.where("posts.id IN (?)", #selected_posts)
Edit 2:
User.select("users.company_name, count(posts.id) userpost_count, user.username").
joins(:posts).
where("posts.id IN (?)", #selected_posts).
order("users.company_name, userpost_count, user.username")
Just use the following:
User.find(#selected_posts.map(&:user_id).uniq)
This takes the user ids from all the selected posts, turns them into an array, and removes any duplicates. Passing an array to user will just find all the users with matching ids. Problem solved.
To combine this with what you showed in your question, you could write:
#posts_matches = User.find(#selected_posts.map(&:user_id).uniq).map{ |user|
[user.company_name, user.posts.size, user.username]
}
Use size to count a relation instead of count because Rails caches the size method and automatically won't look it up more than once. This is better for performance.
Not sure what you were trying to accomplish with Array#sort at the end of your query, but you could always do something like:
#users_with_posts_in_selected = User.find(#selected_posts.map(&:user_id).uniq).order('username DESC')
I don't understand your question but you can pass an array to the where method like this:
where(:id => #selected_posts.map(&:id))
and it will create a SQL query like WHERE id IN (1,2,3,4)
By virtue of your associations your selected posts already have the users:
#selected_posts = Posts.where("posts.category =?", "Baseball")
#users = #selected_posts.collect(&:user);
You'll probably want to remove duplicate users from #users.

Resources