Rails Active Record Omitting Where Clause - ruby-on-rails

I have an Active Record query that sits inside of a gem. Database used is postgres.
Client.where(date:#date,client:#business_id)
The gem uses a get request to pull this data. When there are too many values in #business_id, the URI is too long. Gem does not have post requests.
Workaround:
The business problem is when all the #business_id get passed to the app. I could have an "all" button, that triggers all the client values to show. I would need to ignore the client:#business_id part of the query.
How could I construct the query so that when all of the #business_id need to be passed, it ignores the client:#business_id part of the query?

chain the where clause and then conditionally include or exclude the #business_id part:
relation = Client.where(date:#date)
if your_conditional_is_true
relation = relation.where(client:#business_id)
end
the_clients = relation.all
The reason this works is that where actually returns an ActiveRelation, not the results of executing the SQL. The SQL is not built and executed until you do something to work on the results of the relation.

Related

Rails SQL select, doesn't seem to work as expected with the CASE statement

I'm trying to understand how to use the select method in queries to return custom columns.
I'm running Rails 5.2, database is postgresql.
m = Message.all.select("messages.*, CASE WHEN id > 30 THEN TRUE ELSE FALSE END AS above_30")
returns only the messages table with all its columns. How do I get the above_30 column, preferable eager loaded?
The above_30 is there, but Rails will not define an access for it because there is no column by that name in your schema.
You can define one yourself, or use m[:above_30] to access the "raw" attributes of the object as returned from the query.

How to add attribute/property to each record/object in an array? Rails

I'm not sure if this is just a lacking of the Rails language, or if I am searching all the wrong things here on Stack Overflow, but I cannot find out how to add an attribute to each record in an array.
Here is an example of what I'm trying to do:
#news_stories.each do |individual_news_story|
#user_for_record = User.where(:id => individual_news_story[:user_id]).pluck('name', 'profile_image_url');
individual_news_story.attributes(:author_name) = #user_for_record[0][0]
individual_news_story.attributes(:author_avatar) = #user_for_record[0][1]
end
Any ideas?
If the NewsStory model (or whatever its name is) has a belongs_to relationship to User, then you don't have to do any of this. You can access the attributes of the associated User directly:
#news_stories.each do |news_story|
news_story.user.name # gives you the name of the associated user
news_story.user.profile_image_url # same for the avatar
end
To avoid an N+1 query, you can preload the associated user record for every news story at once by using includes in the NewsStory query:
NewsStory.includes(:user)... # rest of the query
If you do this, you won't need the #user_for_record query — Rails will do the heavy lifting for you, and you could even see a performance improvement, thanks to not issuing a separate pluck query for every single news story in the collection.
If you need to have those extra attributes there regardless:
You can select them as extra attributes in your NewsStory query:
NewsStory.
includes(:user).
joins(:user).
select([
NewsStory.arel_table[Arel.star],
User.arel_table[:name].as("author_name"),
User.arel_table[:profile_image_url].as("author_avatar"),
]).
where(...) # rest of the query
It looks like you're trying to cache the name and avatar of the user on the NewsStory model, in which case, what you want is this:
#news_stories.each do |individual_news_story|
user_for_record = User.find(individual_news_story.user_id)
individual_news_story.author_name = user_for_record.name
individual_news_story.author_avatar = user_for_record.profile_image_url
end
A couple of notes.
I've used find instead of where. find returns a single record identified by it's primary key (id); where returns an array of records. There are definitely more efficient ways to do this -- eager-loading, for one -- but since you're just starting out, I think it's more important to learn the basics before you dig into the advanced stuff to make things more performant.
I've gotten rid of the pluck call, because here again, you're just learning and pluck is a performance optimization useful when you're working with large amounts of data, and if that's what you're doing then activerecord has a batch api you should look into.
I've changed #user_for_record to user_for_record. The # denote instance variables in ruby. Instance variables are shared and accessible from any instance method in an instance of a class. In this case, all you need is a local variable.

Active record create query in multiple steps

I'm a bit confused by active record, it just seems to fire the query at any time you stop, ie.
#model.where( :store_id => #sid )
Which is fine, but what if I want to build a query like this:
query = #model.where( :store_id => #sid )
if(some_condition)
query.offset(50)
and then execute the query (not actually what I'm doing but a very simple example). Is there a way to put together the query in steps and then tell it to execute?
Actually, ActiveRecord will do exactly what you want. It's called lazy loading. You might be getting confused by the rails console, which calls .inspect behinds the scenes on the result of the line.
Check out this question: Lazy loading in Rails 3.2.6
This already works like you want it too.
where() returns an instance of ActiveRecord::Relation.
The relation won't execute it's database call until it needs to. The reason you might be experiencing otherwise is that you're testing it in the console, which prints the output of each statement (thus loading the relation). You can test whether a relation has been loaded via the loaded() method.
Try this on the console:
m = #model.where(:store_id => #sid); # the semicolon will silence the output
m.loaded? # nil
m # executes db call, will print out the contents of the relation
m.loaded? # true

AcitveRecord where method that matches just one record

Program.where(name: "xxyyzz123") will return a collection, even if there's just one record that matches which forces me to do ugly things like:
puts Program.where(name: "xxyyzz123").first.age
or
puts Program.where(name: "xxyyzz123")[0].age
When I know for sure only one record will match, is there a shorter way to grab a property from that one record?
ActiveRecord's dynamic attribute-based finders (find_by_x) allow you to select the first record that matches in your database. For example:
Program.find_by_name('xxyyzz123')
will return the first record with name = 'xxyyzz123'
Note that these finders are 'mildly deprecated' in Rails 4. Using
Program.find_by(name: 'xxyyzz123")
achieves the same thing and may make it easier when needing to update to the next version of Rails if they ever remove the former's functionality.
See ActiveRecord::Base in the API for more.
Yes, you will have to access that with Program.where(name: "xxyyzz123").first.age, however, in Rails 3, it is usually recommended to do that type of query with: Program.find_by_name('xxyyzz123').age.
Rails 4 deprecates the above syntax and recommends you to use the following syntax for that:
Program.find_by(name: 'xxyyzz123')
If you have multiple conditions, then simply : Program.find_by(name: 'xxyyzz123', lang: 'ruby')
Behind the scene, it does the same tomfoolery - where clause and returns first object.

stop meta_search doing sql to early

in the docs it's said:
MyObject.search()
returns an instance of MetaSearch::Builder (something like ActiveRecord::Relation). But in my case when I do this I get a collection of objects because the sql-query is send to the database.
I would like to have something like this:
search = MyObject.search() # no sql-query should be done here
count = search.count # count sql done
objects = search.all # select sql done - maybee with pagination
does anyone know how to stop Meta_search from doing the the query to early?
-> ok, something mysterious going on in my shell:
search = MyObject.search() # queries the database
search = MyObject.search(); 0 # stores a MetaSearch-Object in search
the console seems to call an extra method after each comand
If you're testing in irb, be aware that returned objects are inspected. In the case of a MetaSearch builder, this means the relation gets inspected. If you have a look at ActiveRecord's inspect method, in relation.rb, you'll see that it calls to_a, which executes the query and returns the results.

Resources