ActiveRecord Get Max Value Without Loading - ruby-on-rails

I need to get the last record with a certain value, but without loading it.
I have to do something like:
Thing.where(cool: true).where(created_at: Thing.where(cool: true).maximum(:created_at))
The above is a way to do it, but it does a 2nd query to get the maximum value first. I want to do it all in one query and get the SQL equivalent of something = max(etc).
Just before someone mentions it: .last doesn't work because it returns an object not a relation.
In order words, I need to do .last but returning a relation instead of objects.

Try this way:
Thing.where("cool=1 AND created_at=(SELECT MAX(created_at) FROM things WHERE cool=1)")
On Rails 7, when I tested your query, the database is queried only once.

Related

How does Rails know to return all records even when it is not specfied with the method .all?

I was just playing around with ActiveRecord. While doing so, I noticed something unusual that if I type two separate queries, one with .all and the other one without. all, that the SQL query would return in both cases, all the records from the model:
User.includes(:cart) vs. User.includes(:cart).all vs. User.all.includes(:cart)
also,
User.all vs. User.order(:username)
(With exception to the fact that the second query is ordered, they both yield the same result. They both return all the queries.)
They both seem to be yielding the same result. How does Rails know to capture all the records, even when it is not explicitly stated with .all?
ActiveRecord cleverly delays performing a query until the results are needed.
In a REPL (Read-Eval-Print-Loop) like the Rails console, these ActiveRecord queries are automatically inspected (cast so they can be rendered on the console), which will execute the query so the results can be rendered.
Note that you'll still get back objects representing the query until it's cast to an Array.
User.all.class #=> User::ActiveRecord_Relation
User.where('1 = 1').class #=> User::ActiveRecord_Relation
User.all.to_a #=> Array
This became the norm way back in Rails 3.

How can I disable lazy loading of active record queries?

I want to query some objects from the database using a WHERE clause similar to the following:
#monuments = Monument.where("... lots of SQL ...").limit(6)
Later on, in my view I use methods like #monuments.first, then I loop through #monuments, then I display #monuments.count.
When I look at the Rails console, I see that Rails queries the database multiple times, first with a limit of 1 (for #monuments.first), then with a limit of 6 (for looping through all of them), and finally it issues a count() query.
How can I tell ActiveRecord to only execute the query once? Just executing the query once with a limit of 6 should be enough to get all the data I need. Since the query is slow (80ms), repeating it costs a lot of time.
In your situation you'll want to trigger the query before you your call to first because while first is a method on Array, it's also a “finder method” on ActiveRecord objects that'll fetch the first record.
You can prompt this with any method that requires data to work with. I prefer using to_a since it's clear that we'll be dealing with an array after:
#moments = Moment.where(foo: true).to_a
# SQL Query Executed
#moments.first #=> (Array#first) <Moment #foo=true>
#moments.count #=> (Array#count) 42
In this case, you can also use first(6) in place of limit(6), which will also trigger the query. It may be less obvious to another developer on your team that this is intentional, however.
AFAIK, #monuments.first should not hit the db, I confirmed it on my console, maybe you have multiple instance with same variable or you are doing something else(which you haven't shared here), share the exact code and query and we might debug.
Since, ActiveRecord Collections acts as array, you can use array analogies to avoid querying the db.
Regarding first you can do,
#monuments[0]
Regarding the count, yes, it is a different query which hits the db, to avoid it you can use length as..
#monuments.length

Neo4j gem - Distinct Query with Paginate

How can I get my plucked array to work with the paginate method (which I believe only works on queryproxy objects)
My results are pulling up a few of the same nodes as there are multiple paths to it, so I added pluck and distinct like so..
current_user.friends....where()...params().pluck('DISTINCT e').paginate..
Is there another way around it? or would a change have to be made in the neo4j paginate gem?
Right now, this isn't doable using the paginate method. WillPaginate returns WillPaginate::Collection objects that are already populated from the database. We might be able to make it return something else and evaluate lazily but I'd have to play around with it more.
You can create Neo4j::Paginated objects directly, but these are just plucked results from QP.
# match stupid friends with awful events, return distinct events
query = current_user.friends(:f).where(stupid: true).events(e:).rel_where(expected_attendees: 0)
#bad_events = Neo4j::Paginated.create_from(query, 1, 15).pluck('distinct e')
create_from returns a Neo4j::Paginated object that delegates each and pluck to its the QueryProxy object fed to it. Note that it's going to paginate based on the end of the chain, so it's doing the first page with 15 per page based on the events. Also note that you can't do a distinct count.
Check https://github.com/neo4jrb/neo4j/blob/master/lib/neo4j/paginated.rb for more. It's pretty easy to read.

Ohm, find all records from array of ids

I am looking for a way to find all Ohm affiliated objects with one query, by feeding it an array of attributes that are indexed. In Mongoid, this is done with something like:
Foo.any_in(:some_id => [list_of_ids])
ActiveRecord has the find_all family of methods.
I essentially want to be able to pull N records from the data store without calling find() 30 times individually.
You can pass find an array or list of IDs:
Foo.find(1,2,3) or Foo.find([1,2,3])
This does not seem to work with the latest Ohm (1.1.1). I looked through the source and it seems you need to do something like Model.all.send(:fetch, [1,2,3]). Problem is... you have to call a private method.
I created an issue to see if this is the right approach.
UPDATE: It was just made public!

Ruby on Rails/will_paginate: Ordering by custom columns

I've got a rather complicated SQL-Query whose results should be paginated and displayed to the user. I don't want to go into details here, but to put it simple I just select all columns of a model, and an additional column, that is used just for sorting purposes.
Note.select( ['notes.*', "<rather complicated clause> AS 'x'"] ).joins(...)...
After some .join()'s and a .group(), I finally call .order() and .paginate on the relation. If I order by any of the model's natural columns everything works fine, if I however order by the "artificial" column x, rails gives me the error:
no such column: x
This seems to occur because will_paginate seems to do a COUNT(*)-statement before getting the actual data, simply to get the amounts of data it has to process. This COUNT(*)-statement is (of course) generated from the original statement, which includes the ORDER BY x. The problem now is, that will_paginate keeps the ORDER BY in the statement but simply replaces the column definitions with COUNT(*) and thus cannot find the column x.
Using Rails 3 and will_paginate 3.0.pre2.
Is there any way to get around this?
thx for any help
You can disable the initial count query by passing in the value manually:
total_entries = Note.select(...).joins(...).count
Note.select( ... ).joins(...).group().paginate(:total_entries => total_entries)
The above is not a literal code sample and you may need to tweak your count query to get the correct results for your join. This should let you do a straight up complex query on the pagination.

Resources