Ruby, query active model dynamically [duplicate] - ruby-on-rails

This question already has an answer here:
rails dynamic where sql query
(1 answer)
Closed 5 years ago.
I have an Order model in my ruby on rails application which has several attributes. I want to query those orders according to parameters given by users such as itemCount, totalPrice and created date.
If one of those attributes is given, I mean it's not nil, I need to query model by using it.
I can do something like:
if params[:totalPrice] and params[:itemCount] and params[:created_date]
#product = Product.where(totalPrice: params[:totalPrice],itemCount: params[:itemCount],created_date: params[:created_date])
elsif params[:totalPrice] and params[:itemCount]
#product = Product.where(totalPrice: params[:totalPrice],itemCount: params[:itemCount])
elsif params[:totalPrice] and params[:created_date]
#product = Product.where(totalPrice: params[:totalPrice],created_date: params[:created_date])
elsif params[:itemCount] and params[:created_date]
#product = Product.where(itemCount: params[:itemCount],created_date: params[:created_date])
elseif ....(and continues)
However, I can't be sure about that. Maybe there is there a "Ruby" way to achieve that problem.
What is the best practice to do that,
Thanks.

You can chain the different scopes with ActiveRecord:
#products = Product.all
#products = #products.where(totalPrice: params[:totalPrice]) if params[:totalPrice]
#products = #products.where(itemCount: params[:itemCount]) if params[:itemCount]
#products = #products.where(created_date: params[:created_date]) if params[:created_date]
# etc...
And this can easily be dynamic (but white-listed):
filterable_columns = %i(itemCount totalPrice created_date)
#products = Product.all
filterable_columns.each do |column|
#products = #products.where(column => params[column]) if params[column]
end

Related

Problem with selecting elements with the same params

What i do wrong? I want to return every products which pass condition:
def show
products = Product.select{|x| x[:category_id] == params[:id]}
render json: products
end
When i write
def show
products = Product.select{|x| x[:category_id] == 1}
render json: products
end
it works why the first example doesn't work?
I am pretty sure that there is mismatch in data type.
1=='1' #will be always false
1==1 #will be true
'1'=='1' #will be true as well
And also check for nil value from params[:id]
Please make sure to change as follows
def show
products = Product.select{|x| x.category_id == params[:id].to_i}
render json: products
end
OR
The best solution as suggested by #Eyeslandic is to use .where as it will not check for mismatch in data type. And also you don't have to take care of nil value from params[:id].
You should really be using a where to stop sql from loading all your products.
#products = Product.where('category_is = ?', params[:id])
The being said, if you are sticking to rails restful conventions, the fact you have a param called :id that is the category_id suggests you are on the category controller. So maybe consider changing your logic to:
#category = Category.includes(:products).find(params[:id])
you can then access products via
#category.products
or if your not interested in the category too much maybe
#products = Category.includes(:products).find(params[:id])&.products

Rails: outputting data where record has more votes

Right now I have this
def index
#trips = Trip.all
end
And I'm outputting data like so:
- #trips.order('created_at desc').first(4).each do |trip|
- trip.trip_images.first(1).each do |image|
= trip.title_name.titleize
However, I have a votable table (from acts_as_votable gem) associated to trips. I was wondering if I can only output trips where trips have a certain amount of votes?
I can get the votes like this:
- #trips.order('created_at desc').first(4).each do |trip|
= trip.get_likes.size #this is where I can get the likes
- trip.trip_images.first(1).each do |image|
= trip.title_name.titleize
EDIT
If I do this instead:
def index
#votes = ActsAsVotable::Vote.where(votable_type: 'Trip').group(:votable_id).count
#trips = Trip.where(#votes)
end
#votes gives me something like this:
{195=>1, 106=>1, 120=>1, 227=>1, 247=>1, 264=>1, 410=>1}
How do I get it where trip will only get the ids?
EDIT 2
I think I figured it out...
def index
#votes = ActsAsVotable::Vote.where(votable_type: 'Trip').group(:votable_id).count
#trips = Trip.where(id: #votes.keys)
end
I got some kind of output. Is there a better way?
Yesterday I answered similar question.
This is how you could get the id(s) of trip with certain amount of votes (you can use =, >, <= and so on):
trip_ids = ActsAsVotable::Vote
.where(votable_type: 'Trip')
.group(:votable_id)
.having('count(votable_id) > 1') #any number of votes
.pluck(:votable_id)
.uniq
Trip.where(id: trip_ids)
Have you considered making this a method in your Trip model?
Something like,
def popular_trip_images
Trip.select(:trip_images).where("likes > ?", 200)
end
Then use it something like,
...
trip.popular_trip_images.each do |image|
...
Edit:
However, I have a votable table (from acts_as_votable gem) associated to trips. I was wondering if I can only output trips where trips have a certain amount of votes?
Sorry, missed this part. The gem has a find_liked_items method but don't see offhand how to set something like liked > 400 etc.
I've been trying to work through the comments, but right now, I've gotten this code to work:
def index
#votes = ActsAsVotable::Vote.where(votable_type: 'Trip').group(:votable_id).count
#votes = #votes.select {|k,v| v > 1}
#trips = Trip.where(id: #votes.keys)
end
If someone else can come up with a better solution! I'll mark as correct.

How to handle additional param in controller

I'm learning Rails and I have now a good knowledge about controllers.
By the way I always have some issues and I don't know whats the best way to solve them.
One of these is the search: I have a search in my website and I must reorder results for relevance and date.
My search controller
def show
#query = params[:query]
#contents = Content.published.search_by_text(#query).page(params[:page]).per(12)
end
This is the default search. I have to implement also the "data order" search and I think to do something like this:
def show
#query = params[:query]
#contents = Content.published.search_by_text(#query).page(params[:page]).per(12)
if params[:order]
#contents = Content.published.search_by_text(#query).reorder("created_at DESC").page(params[:page]).per(12)
end
end
Is there a better way to obtain my result?
Fortunately, rails allows us to chain calls when using Active Record (Rails' ORM)
Here is a possibility :
def show
#query = params[:query]
#contents = Content.published.search_by_text(#query)
#contents = #contents.reorder("created_at DESC") if params[:order]
#contents = #contents.page(params[:page]).per(12)
end

Rails 3 ActiveRecord nested associations select query

I'm trying to make an API for a quiz admin I have made.
I have a Quiz model which has_many Questions and has_many Results
The Question model also has_many Answers
I want the url /data/quiz/7 to return all the Questions+Answers and Results to the Quiz with id=7
Here is the method for this I have at the moment.
def quiz
#quiz = Quiz.find(params[:id])
#questions = #quiz.questions.select('id, content') # returns only selected fields
#results = #quiz.results.select('id, content, points_limit') # returns only selected fields
#questions.each do |question|
question['answers'] = question.answers.select('id, content, points') #returns whole object
end
#return = Hash.new
#return['questions'] = #questions
#return['results'] = #results
respond_to do |format|
format.json { render json: #return }
format.xml { render xml: #return }
end
end
Everything works EXCEPT the answers are returning the FULL answer object including created-at, id, updated-at etc etc and all I want are the fields selected in the query like I have.
Why is the .select working for #questions and #results but not the associated #answers?
No matter what I try it seems to ignore the select statement for the answers loop and always return the full object.
--
In console... I know doing the same thing
question.answers.select('id, content, points')
returns exactly what I'm after. So it's something to do with the way I'm putting it into the Array/Hash I'm guessing but still can't work it out.
I think if you try something like this it will work:
#answers = Answer.select([:id, :content, :points])
.where(question_id: #questions.pluck(:id))
This is the same as:
SELECT id, content, points
FROM answers
WHERE question_id in (<question_ids array>)

Rails Given an array of objects from a db, is there a way to get an item in the array w/o having to rehit the db?

Given:
#votes (user_id, option_id)
If do: #project.votes I get all the votes for that project.
If I then want to see what the current user voted for I have to do:
Votes.where(:user_id => current_user.id).first
This is a record that's already in the #project votes query. Is there a way I can find the record in that first query w/o having to rehit the db?
Thanks
You can just use the regular ruby Enumerable#select method:
#votes = project.votes.all
# ... other code ...
current_user_votes = #votes.select{ |v| v.user_id == current_user.id }
This will return an array of all the user's votes for the project. If the user is only allowed one vote, and you want a single value, not an array, just use .first like so:
#votes = project.votes.all
# ... other code ...
current_user_vote = #votes.select{ |v| v.user_id == current_user.id }.first

Resources