I want to get all records of matching wiht query, I get multiple params and save them in query like that
query = Hash.new
query[:user_id] = params["user_id"] if query["user_id"]
query[:vehicle] = params["vehicle_id"] if query["vehicle_id"]
trips = Trip.where(query)
I also want to add params['created_at] if these params are present but no idea how to do it
You could add a scope to your Trip model.
scope :created_after, ->(time) { where("created_at > ?", time) if time }
Then chain it like so:
Trip.created_after(params["created_at"]).where(query)
Not the most elegant, but it will work. If "created_at" is not in the params, it will be nil and will just return all trips.
Just for fun and riffing off the answer provided by Paul Byrne and this Q&A, perhaps something like:
params.slice(:user_id, :vehicle_id).compact.
each_with_object(Trip.created_after(params[:created_at])) do |(key, value), scope|
scope.merge(Trip.send(key, value))
end
Related
I want to create a scope, I want to get all Orders in which has passed more than 24hrs since the last time they were updated.
I want to get all those Orders for then loop in them:
Order.more_than_24_updated.find_each do |order|
#do some stuff with each order
end
I do not know how to add 24hrs to the updated_at attribute of orders in the scope:
scope :more_than_24_updated, -> { where(status: "pago_pendiente").where('#{Time.now} >= ?', updated_at + 24.hours) }
Well I know the above does not work, but well... how to do this?
You can try something like this:
scope :more_than_24_updated, -> {where('status = ? and updated_at <= ?', :pago_pendiente, 24.hours.ago)}
You can try this one
scope :more_than_24_updated, -> { where(status: "pago_pendiente").where("NOW() - '24 hours'::INTERVAL >= updated_at") }
I want to grab those Blogs where its title.present? == true. (Blogs have a string attribute of title).
class Blog < ActiveRecord::Base
scope :with_present_titles, -> {where("#{title.present?} == ?", true)} #doesn't work
end
#blogs = Blog.with_present_titles #doesn't work
Not sure what I'm doing wrong here.
To return all records with some values in title column, you would update your scope with the following query:
scope :with_present_titles, -> { where("title is not null and title != ''") }
This returns all records with some value in title column leaving any null and empty titles.
.present? being a method provided by rails, you cannot simply use these methods in your DB queries.
Depending on your rails version you can also use where.not (introduced in rails 4) E.g.
scope :with_present_titles, ->{where.not(title: [nil,'']) }
#Blog.with_present_titles.to_sql
#=> "SELECT [blogs].*
# FROM [blogs]
# WHERE (NOT (([blogs].[title] = '' OR [blogs].[title] IS NULL)))"
Simply write the following scope.
scope :with_present_titles, -> { where('title IS NOT ? AND TITLE != ?', nil, '') }
I want to join my named scopes as I generate them with an array.
how wouldI do that , i can't join named scopes, is there a better way to do this?
scope :search, ->(attrs_for_search,params) do
if attrs_for_search.present?
params_to_search_on = params.keys & attrs_for_search
params_to_search_on.collect{|x| where("#{x} ILIKE ?", "%#{params[x]}%") }
else
none
end
end
Contact.search(%w[email],{'email => 'jason'})
I think that you can create a scope and use the 'send' method to join the scopes.
scope :search, ->(field, value) do
where("#{field} LIKE ?", "%#{value}%")
end
def self.multi_search(params)
result = nil
params_to_search_on = params.keys
params_to_search_on.each do |k|
if result.nil?
result = Message.send(:search, k, params[k])
else
result = result.send(:search, k, params[k])
end
end
result
end
Hopes this help you.
You can't chain scopes when you return an array.
You can:
Try returning a relation:
results = params_to_search_on.collect{|x| where("#{x} ILIKE ?", "%#{params[x]}%") }
where(id: results.flatten.map(&:id))
I have not tested this, but I think you will need flatten because results is an array of relations [rel_1,_rel_2]
Then you can use/chain/attach-a-bunch-of scopes like this:
Contact.search(attrs, params).other_scope.another_scope(with_params)
You may want to read about full text search, to expand this topic, I would need to know the DB you are using.
I have a scope that look like this:
scope :attr_similar_to, -> (attr, strings) { where("#{attr} similar to ?", "%(#{strings})%") }
The way I use this scope is that iterate through a number of attributes and for each attribute I send the attribute into the scope along with a set of multiple strings in the 'strings' variable, which gets sent into this scope as "foo|bar|etc". So for each attribute, I check if the attribute value is similar to any of the strings.
The loop where I use this scope looks like this:
results = Model.none
attributes.each do |attr|
results += attr_similar_to(attr, strings)
end
The problem is that if I have two attributes, "name" and "age", and only "name" matches on "foo", it will still fetch me all those records matching with "foo". I want to be able to chain these scope calls with something like an "and" operator between them so that for example both "name" and "age" must get a match each in order to fetch me any records.
Am I perhaps trying to do this in a wrong or inefficient way?
results = Model.all # Model.scoped in rails 3
attributes.each do |attr|
results = results.attr_similar_to(attr, strings)
end
Or more concise:
results = attributes.inject(Model.all) do |result, attr|
result.attr_similar_to(attr, strings)
end
I have an ActiveRecord relation of a user's previous "votes"...
#previous_votes = current_user.votes
I need to filter these down to votes only on the current "challenge", so Ruby's select method seemed like the best way to do that...
#previous_votes = current_user.votes.select { |v| v.entry.challenge_id == Entry.find(params[:entry_id]).challenge_id }
But I also need to update the attributes of these records, and the select method turns my relation into an array which can't be updated or saved!
#previous_votes.update_all :ignore => false
# ...
# undefined method `update_all' for #<Array:0x007fed7949a0c0>
How can I filter down my relation like the select method is doing, but not lose the ability to update/save it the items with ActiveRecord?
Poking around the Google it seems like named_scope's appear in all the answers for similar questions, but I can't figure out it they can specifically accomplish what I'm after.
The problem is that select is not an SQL method. It fetches all records and filters them on the Ruby side. Here is a simplified example:
votes = Vote.scoped
votes.select{ |v| v.active? }
# SQL: select * from votes
# Ruby: all.select{ |v| v.active? }
Since update_all is an SQL method you can't use it on a Ruby array. You can stick to performing all operations in Ruby or move some (all) of them into SQL.
votes = Vote.scoped
votes.select{ |v| v.active? }
# N SQL operations (N - number of votes)
votes.each{ |vote| vote.update_attribute :ignore, false }
# or in 1 SQL operation
Vote.where(id: votes.map(&:id)).update_all(ignore: false)
If you don't actually use fetched votes it would be faster to perform the whole select & update on SQL side:
Vote.where(active: true).update_all(ignore: false)
While the previous examples work fine with your select, this one requires you to rewrite it in terms of SQL. If you have set up all relationships in Rails models you can do it roughly like this:
entry = Entry.find(params[:entry_id])
current_user.votes.joins(:challenges).merge(entry.challenge.votes)
# requires following associations:
# Challenge.has_many :votes
# User.has_many :votes
# Vote.has_many :challenges
And Rails will construct the appropriate SQL for you. But you can always fall back to writing the SQL by hand if something doesn't work.
Use collection_select instead of select. collection_select is specifically built on top of select to return ActiveRecord objects and not an array of strings like you get with select.
#previous_votes = current_user.votes.collection_select { |v| v.entry.challenge_id == Entry.find(params[:entry_id]).challenge_id }
This should return #previous_votes as an array of objects
EDIT: Updating this post with another suggested way to return those AR objects in an array
#previous_votes = current_user.votes.collect {|v| records.detect { v.entry.challenge_id == Entry.find(params[:entry_id]).challenge_id}}
A nice approach this is to use scopes. In your case, you can set this up the scope as follows:
class Vote < ActiveRecord::Base
scope :for_challenge, lambda do |challenge_id|
joins(:entry).where("entry.challenge_id = ?", challenge_id)
end
end
Then your code for getting current votes will look like:
challenge_id = Entry.find(params[:entry_id]).challenge_id
#previous_votes = current_user.votes.for_challenge(challenge_id)
I believe you can do something like:
#entry = Entry.find(params[:entry_id])
#previous_votes = Vote.joins(:entry).where(entries: { id: #entry.id, challenge_id: #entry.challenge_id })