The problem: you have a large result set that you want to return, but you don’t need full blown ActiveRecord models for each result. In fact, doing so would likely bring the Ruby process to a crawl. Instead, you just need a few attributes in an array plucked from each result.
If you only want to fetch a single column from a query, you can use the normal pluck
method.
If you want to fetch more than one attribute, you unfortunately can’t use pluck.
Passing an Array to pluck
also doesn’t work and produces an invalid SQL query. Next, you might be tempted to try this:
Let’s assume that you’re an incredibly prolific writer about cats. In your time, you’ve written hundreds of thousands of articles about cats. It’s gotten to the point that you might want to see a therapist. Anyways, the code above is bad. It will create a Post object for every single database result that is found, and then pull the data from each one by one. It’s basically going to suck the life out of your server.
Next you might be tempted to try something tricky like this:
Ah ha, this will only query for the two attributes that are needed! Well, actually it creates hundreds of thousands of incomplete objects and will still suck the life out of your server. Still not good enough.
The solution actually came from inspecting the source of the pluck
method. One of the great advantages of open-source software.
If you look towards the bottom of the code snippet, you’ll see that ActiveRecord is using a select_all
method under the hood. When given a AREL relation, it runs a query that selects only the attributes that are needed from the database, and then maps the results to the model.
If we pull out bits and pieces of this code, we can bypass creating the model altogether by leveraging select_all
. The nice thing about ActiveRecord is that it lazy-loads data. This means we can build a query without touching the database.
There are two different routes we can take in order to make this code a little prettier and integrate it into our project. First, we can extend ActiveRecord::Relation
itself. If you’re feeling ballsy, you can change it to override the original pluck
method. The best place to put this code would be in an initializer.
If the thought of extending ActiveRecord brings a queasy feeling to your stomach, we can also wrap this into a pretty little Concern to include in our models. It’s not as clean, but it works.
Luckily, Rails 4 has multiple column plucking built in, so we don’t have to worry about this workaround. If you’re stuck with Rails 3 though, give it a try!
Posted June 11, 2013