Rails include with options - sql

Is it possible to limit an ActiveRecord :include to say only pull one record?
Item.find(:all,
:include => [ :external_ratings, :photos => LIMIT 1 ])
I have a list of items, and each item has between 5 and 15 photos. I want to load a photo id into memory, but I don't need all of them. I just want to preview the first one.
Is there a way to do this with ActiveRecord?

I don't believe it is possible to do it directly, but as finding one record is only a single query, why not do it outside the include?
e.g
first_photo = Photo.find(records.first.photo_id)
After your main find

I'm not going to investigate the detailed query, but I'm thinking find_by_sql is in order for this particular case.

Related

Rails order by count on association

I have 2 models. Gif, GifStatistic
gif has_many :gif_statistics
GifStatistics has column called state. It can be either like or dislike
What i want to achieve is to query gifs, but order them in highest count of likes
Something like(Pseudo code)
Gif.joins(:gif_statistics).order(count(gif.gif_statistics.state))
How do i achieve this?
Try this query.
Gif.joins(:gif_statistics).select("*, count(gif_statistics.state) as state_count").order("state_count desc")
My personal recommendation is that you create two new fields in the gif model so that you can store the count of like and dislike as and when its created so that you don't have to do such a complex query. Placing the counter cache would improve the speed.

Rails 3 Searching Multiple Models by created_at using sunspot

I'm trying to get a "What's new" section working in my Rails app that takes into account new records created for various tables that don't share any relationships. The one thing they do have in common is that they all have a created_at field, which I'm going to use to determine if they're indeed "new" and then I'm wanting to sort the results by that common field. I tried doing this with Sunspot, but I couldn't figure out how to make use of the the result set returned from the Sunspot search...
For instance in my Uploads and Article models I have:
searchable do
time :created_at
end
and in my search action I'll do this:
#updates = Sunspot.search(Upload,Article) do
with(:created_at).greater_than(1.hour.ago)
end
Which does seem to return something, if I do an #updates.total it returns the number of records I was expecting to find. Beyond this I'm not sure how to actually make use of the records. What I'd like to do is send #updates to a view and determine the model type of each record and then proceed to print out the relevant information, i.e names, descriptions, parent/child record information (for instance upload.user.username).
I might be going at this all wrong, perhaps there's a better option than sunspot for the simple search I'm attempting to perform?
Refer readme for details of how to use the search results. The method you are looking for is "results", which will give you first 30 results, by default:
#updates.results # array of first 30 results

Rails 3 - defining :order using a combination of attributes, while treating the *latest* record differently

My Application has the following :order requirements:
Order by the importance (measured by say number of likes) AND
Order by created_at DESC
So I am currently using the following to define the :order
:order => 'model.likes_count DESC, created_at DESC'
However, in my views, I create new model entries using AJAX and therefore, I reload the partials where I display the database entries for this particular model and would like to use jQuery effects to (say) highlight the record that was just created.
Now due to the above :order definition, the record just created would not show up as the first one if an older record has greater number of 'likes'.
Is there a way to define an order which takes into account the "latest" record differently and then order the rest of the records as per the defined order? If possible what would be the clean way to achieve this.
Thanks!
The Rails API does not have any functionality to treat a newly created record differently.
Your options are:
Do not select the new record in the query, eg. with .where("id <> ?",idofnewrecord)
You can select the new record in a separate query if required
Or, select the records as normal, then find the new record in ruby, eg:
#newrecord = #records.find{|r|r.id==idofnewrecord}
or to delete it from the array of records:
#newrecord = #records.delete_if{|r|r.id==idofnewrecord}
If you don't know the id, you may be able to filter it out with some other attribute as well, eg. created_at.

Rails have activerecord grab all needed associations in one go?

I have a model Comment, which has_a User.
In my display page, I am doing a Comment.all, and then displaying each comment.
In the view I need to display not only the comment, but also information about the associated user (ie the author).
<% #comments.each do |comment| %>
<%= comment.user.name %>
<%= comment.user.email %>
...etc...
<% end %>
This is fine and all, but activerecord translates this into one SELECT * FROM users WHERE USER.id = commentId query per EACH comment I have.
This is a little ridiculous, especially on a page with hundreds of comments. (That's hundreds of individual separate DB hits!)
When I am doing the Comment.all, is there away to tell rails to not only grab the comments, but also grab the associated users, and then when I call comment.user.blah later, for it to not grab it from db again? (This way it would do it all in one db statement).
You should use .includes.
From the doc:
Solution to N + 1 queries problem
Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the includes method of the Model.find call. With includes, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.
Revisiting the above case, we could rewrite Client.all to use eager load addresses:
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
Or, in your case, it would be
Comment.includes(:user)
You can do this easily with ActiveRecord, you just need to use the eager loading feature in it. So Comment.all(:include => :user) will run one large query rather than one query per record.
Checkout http://stackoverflow.com/questions/29908230/rails-have-activerecord-grab-more-than-one-association-in-one-go/29908317#29908317 if you want to associate with more than one association in the includes statement.
The gist is using Comment.includes(:user, :dates, :whatevertableetc).

ActiveRecord counting with conditions over two tables

After watching, the latest RailsCasts with the include and joins information, I know I can do what I am doing in a much more efficient way. I have a very simple User model and a Status model. The User belongs to Status and the Status has many Users. I need to count how many users have a specific kind of status, this creates a new SQL count query for every single status and I know that this is not a good way to do it. It looks like this right now.
statuses = Status.all
statuses.each do |status|
status.users.count
end
I end up with 4 queries of:
SELECT count(*) AS count_all FROM "users" WHERE ("users".status_id = 1)
SELECT count(*) AS count_all FROM "users" WHERE ("users".status_id = 2)
It goes on like that for as many different statuses as exist in the database. The big problem is that now I need to filter by another association as well which is Organization. So I need to find the count for all users who have a certain status in a certain organization. This ends up quadrupling the amount of queries I am making and feels horrible. I'm not sure what kind of join I could use to cut down on this or what I could possibly do to fix this. Thanks for the help :)
Ok so I am answering my own question, just in case anyone has the same issue.
Status.all(:joins => :users,
:select => "statuses.*, count(users.id) as users_count",
:group => "statuses.id")
This returns every status that has users and the count of the users as users_count. In order to further refine the query and only count users that belong to a certain organization the query changes to this.
Status.all(:joins => :users,
:select => "statuses.*, count(users.id) as users_count",
:conditions => {:users => {:organization_id => ORG_ID_HERE}},
:group => "statuses.id")
I hope this helps anyone with the same issue and thanks to Eimantas, and Ryan Bates(RailCasts).
You can try plain SQL:
SELECT s.name,COUNT(u.id) AS users_count FROM statuses s, users u WHERE s.id=u.status_id GROUP BY s.id;
Out of interest, what does your Status model contain? Does it really need to be its own model? I'm guessing here, but you may want to consider implementing a finite state machine. There are a number of rails plugins that make it easy to implement an FSM e.g. acts_as_state_machine