how active record sample works? - ruby-on-rails-3

I happened to see a line
Item.where(conditions).limit(10).order('created_at desc')
And I wonder it is similar to
Item.where(conditions).order('created_at desc').limit(10)
Seems ok as per new changes to rails 3, active record
But how about if we want, sample 10 items, *ordered by created_at*. This was my question #1
question 2 is 'limit' works within query, 'sample' does not..right?, it seems to be taken care of ruby array sample..right?

I recommend you check the railscast episodes 239 active relation walkthrough to understand how it works.
Basically, the internal work is done by Arel gem, activerecord object simply maintains the arel object when you do chaining. By the end of the day, it lets arel generate the sql statement, then it calls the find_by_sql with the generated sql statement.
The thing is that whether you called order before limit or the other way round, the generated sql statement is simply the same. Everything is executed by the database backend, activerecord simply parse the results and build up objects back.

Related

ActiveRecord - Query with Condition of Last Element in Association

Trying to do a sort of complex query in ActiveRecord right now. Not sure if it's possible to do entirely in ActiveRecord (i.e. without just filtering intermediate results in Rails). I'd like to do as much manipulation as possible in the database.
I have a PullRequest and Release model with a many-many relationship. There exist fields on the Release model is_rollback(boolean) and ended_at (datetime). I am trying to find all pull_requests for which their releases association 1) is nonempty and 2) ends with a release that has is_rollback = true when the releases are ordered by ended_at.
Here's what I have so far:
PullRequest
.joins(:releases) # filter pull requests with empty release associations
.having('releases.is_rollback = true AND releases.ended_at = MAX(releases.ended_at)') # where latest release is a rollback
.group('pull_requests.id, releases.ended_at, releases.is_rollback') # necessary with 'having'
The having clause is not working as expected though and the query is still returning pull requests whose latest release is not a rollback. Not hugely experienced with complex SQL queries so any help is appreciated. I'm using Rails 4, PostgreSQL db.
Thanks!
I was able to figure it out - here is the working query:
pull_requests
.joins(:releases).distinct
.group('releases.ended_at, pull_requests.id, releases.id')
.having('releases.ended_at IN (SELECT MAX(releases.ended_at))')
.where('releases.is_rollback = true')
Thanks.

ActiveRecord giving wrong results in Rake task

I have created my first Rails Rake task which imports some data. It uses the URL to identify if the page needs to be updated or inserted. I am however having som really weird issue with some records being inserted multiple times instead, instead of just being updated.
My query looks like this:
existingCompany = Company.find_by_external_link(company.external_link)
I then look at
existingCompany.nil?
to see if the record needs to be created or updated. Some of the companies are not found by active record even though the external link exists. I have tried to print out the url and then look in the database (I use PostgreSQL) and it finds it correctly. The even weirder thing is that it doesn't happen to all records, only a few of them.
Anyone got an idea what might make ActiveRecord believe that a record doesn't exist?
You don't give a lot of information, but a couple of things to try:
How is company.external_link getting set? Either in the debugger or
a simple puts can tell you if it is what you think. For example
"http://www.ups.com/" != "https://www.ups.com/" !=
"http://www.ups.com"
You may need to be consistant on capitalization
or removing white space in company.external_link (.downcase, .strip)
Another thing to keep in mind is ActiveRecord creates a method Company.find_or_create_by_external_link which will do this in one step

find_by_sql renders an array

I got some problems here, I can't make my find_by_sql request to render an ActiveRecord relation. Indeed, I need an activerecord relation to make a new request:
#searches = #searches.find_by_sql('SELECT *, COUNT( follower_id ) FROM follows GROUP BY followable_id LIMIT 0 , 3') if params[:only_famous_projects]
#project_pages = #project_pages.where(:project_id => #searches.pluck(:'followable.id')) if params[:only_famous_projects]
I can't use "pluck" without an activerecord relation. Therefore, I think I have to convert my sql request to an Activerecord request. However, as soon as I use "count" on ActiveRecord, I have an huge problem: I don't have on the end an ActiveRecord relation, but a FixNum!
I don't know where to find the answer anymore, I would be really gratefull if you could help me.
Thanks
find_by_sql will return an ActiveRecord object only if you call it with YourModel.find_by_sql.
Why not use the ActiveRecord query interface. It does a good job with calculations.
UPDATED
#searches = #searches.group(:followable_id).limit(3).offset(0).count(:follower_id) if params[:only_famous_projects]
Notice that it will give you a Hash containing the count for each followable_id.
Isn't LIMIT 0, 3 equivalent to LIMIT 3 ?
COUNT will always return a FixNUM, because you asked the database to count the number of rows.
You should really try to use find_by_sql as a last resort as it is only meant to bypass ActiveRecord for things that it can not do. And even for things that ActiveRecord doesn't support, you can always see if you can use the Squeel or Valium gems to handle edge-cases.
Another reason not to use find_by_sql is that, for example, using MySQL specific terms will lock you out of using other databases in the future.

Rails Query Issue

I have photos which have_many comments.
I want to select whatever photos have recent comments and display those photos in a kind of "timeline" where the most recently commented photo is at the top and other photos fall below.
I tried this, and it worked on SQLite:
#photos = Photo.select('DISTINCT photos.*').joins(:comments).order('comments.created_at DESC')
However testing on PostgreSQL raises this error:
PGError: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
\n: SELECT DISTINCT photos.* FROM \"photos\" INNER JOIN \"comments\" ON \...
So, the problem is, I'm selecting Photos but ordering by recency of comments... and Postgre doesn't like that.
Can anyone suggest either:
A: How I can fix this query...
or
B: A different way to retrieve photos by the recency of their comments?
The important reason I'm doing it this way instead of through the comments model is I want to show each photo once with any recent comments beside it, not show each comment by itself with the same photos appearing multiple times.
Thanks!
Check out the :touch parameter of of the belongs_to association:
:touch
If true, the associated object will be
touched (the updated_at/on attributes
set to now) when this record is either
saved or destroyed. If you specify a
symbol, that attribute will be updated
with the current time instead of the
updated_at/on attribute.
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to
In your Comment model, therefore, you would have:
belongs_to :photo, :touch => :comments_updated_at
Now, in order to create a time line of photos with recently updated comments all you need to do is:
Photo.order('comments_updated_at DESC').all
Just be sure to add the "comments_updated_at" datetime field to your Photo model.
Make sense?
Just for the future readers of this question, the real answer to your SQL issue in SQlite vs Postgresql is that in the SQL "standard", every selected column needs to be in the GROUP BY or be an aggregate function.
https://www.techonthenet.com/sql/group_by.php (or whatever SQL ref you want to take a look at)
Your SQLite query used SELECT * instead of specific columns. That would have blown up with a similar error on most databases like Postgresql (MySQL, Maria, probably MSSQL Server). It's definitely invalid SQL grammar for a lot of good reasons.
Under the hood, I have no clue what SQlite does -- maybe it expands the * into fields and adds them to the GROUP BY under the hood? But its not a good SQL statement which is which it threw the error.

not on a query in RoR

In Ruby on rails 3 I want to query on a has_many field of a model as follows:
#project.items.where(:status => 1)
The problem is I'm trying to get the exact opposite result than this. What i want is all items of #project where the status is not 1. Been looking for the answer to this for a while, anyone?
There are many ways to accomplish what you are trying to do, however, some are better than others. If you will always be searching for a hardcoded number (i.e. 1 in this case), then the following solution will work:
#project.items.where('status != 1')
However, if this value is not hard-coded, you are openly vulnerable to SQL injection as Rails will not (cannot) escape this kind of query. As a result, it is preferred among Rails developers to user the following syntax for most custom conditions (those that can't be constructed via Hash):
#project.items.where(['status != ?', 1])
This syntax is slightly confusing, so let me go over it. Basically you are providing the where clause an Array of values. The first value in the array is a String representing the query you want executed. Anywhere you want a value in that string, you place a ?. This serves as a placeholder. Next, you add an element for every question mark in you query. For example, if I had the following:
where(['first_name = ? AND last_name = ?', params[:first_name], params[:last_name]]
Rails will automatically match these up forming the query for you. In that process, it also escapes potentially unsafe characters, preventing injection.
In general, it is preferred to use the Array syntax, even for a hardcoded value. I've been told that pure string conditions in Rails 3.5 will raise a warning (unverified), so it doesn't hurt to get in the process of using the Array syntax now.