How can use .where for an composite attribute? - ruby-on-rails

I am trying to execute a search by differents filters, color, skin and cover
def self.search_by_filter(params)
search_cover = params[:cover]
Article.where('cover[:value] = :search_cover', search_cover: search_cover )
end
the collection is an attribute composite like this:
{
skin:12,
color:'#382DFA',
cover: {"type":"LH","value":"A123"}
}
this is the error:
#<ActiveRecord::PreparedStatementInvalid: missing value for :value in cover[:value]

Related

Query on Params

I want to get records based on params received. Sometimes I receive 2 params or sometimes 3. I have written a code to get results when all 3 params received but when I receive only 1 param am getting 0 results.
Example
Office.where(state: params[:state], type: params[:type]).where("name like ?","%#{params[:name]}%")
When i get values like
{state: "KA", type: "private", name: "google"}
But when I get the only {name: "google"} I get no records
I have tried with condition
If params[:name].present? && params[:state].present? && params[:type].present?
query
elsif condition
query
end
Let me know how can I solve this or any better way
You can do something like this
In controller
filter_params = params.slice(:state, :type, :name)
Office.filter(filter_params)
In Office model
scope :state, -> (state) { where(state: state) }
scope :type, -> (type) { where(type: type) }
scope :name, -> (name) { where("name LIKE ?", "%#{name}%") }
def self.filter(filter_params)
results = where(nil)
filter_params.each do |key, value|
results = results.public_send(key, value) if value.present?
end
results
end
PS: It runs a single query irrespective of the number of params!
Hope that helps!
If a parameter is missing, it will probably be blank. If you pass them all in this will result in clauses like type = ''. For example, if only name is passed in you'll get something like...
where name like '%google%' and type = '' and state = ''
You need to strip out the blank fields. There are various ways to do this. Since you have a special case, the name clause, one good way to handle this is to build the query piece by piece.
query = Office.all
query = query.where(state: params[:state]) if params[:state].present?
query = query.where(type: params[:type]) if params[:type].present?
query = query.where("name like ?","%#{params[:name]}%") if params[:name].present?
The query does not execute until you fetch the values from query.
If there's a lot of simple parameters you can make a Hash and remove the pairs with a blank value.
qparams = {
state: params[:state],
type: params[:type]
}.select { |k,v|
v.present?
}
query = Office.where(qparams)
query = query.where("name like ?","%#{params[:name]}%") if params[:name].present?
Or use the handy compact_blank gem.
using CompactBlank
qparams = {
state: params[:state],
type: params[:type]
}.compact_blank
query = Office.where(qparams)
query = query.where("name like ?","%#{params[:name]}%") if params[:name].present?

How to select the count of joins as metadata for the main model using activerecord

I have two tables that are something like:
users
id
name
active
items
id
user_id
color
Using Rails, I want to select the active users along with the number of items that are red or blue.
Something like:
User.where(active: true).joins(:items).where(items: {color: ['red', 'blue']}).count(:items)
I want the result to be an array of Users where the Users have an annotated number of items.
So it could end up like users = activerecord query, users.first.name == 'John', users.first.items_count == 3
What would you do?
Considering the color filter, I'd just do the count in ruby.
class User
scope :active, -> { where(active: true) }
def items_of_color(colors)
items.select{ |i| i.color.in?(colors) }
end
end
in the controller
#users = User.active.preload(items)
and then in the view, count the red and blue
user.items_of_color(['red', 'blue']).size
But, if RED and BLUE are special and commonly referenced, you can do this...
class User
...
has_many :red_and_blue_items, where -> ({color: ["red", "blue"]}), class_name: "Item"
end
And then, preload like so
#users = User.active.preload(:red_and_blue_items)
In the view
#users.first.red_and_blue_items.size
I don't say it is the solution, but this following statement
Item
.joins(:user)
.group(:user_id)
.where(users: { active: true }, color: ['red', 'blue'])
.count
return a list of user_id with its associated item count:
{
user_id_1 => count_1,
user_id_2 => count_2,
user_id_3 => count_3,
...
}

How can I pass a hash for my where clause?

I am listing products and I want to be able to pass a hash as my where clause so I can do something like:
filter = {}
filter[:category_id] = #category.id
filter[:is_active] = true
#products = Products.where(filter)
Is it possible to do this somehow?
I also need to add something like this in my where clause:
WHERE price > 100
How could I add that to a filter?
The reason I want to do this is because in the UI I will have a set of optional filters, so then I will use if clauses in my controller to set each filter.
You can pass a hash to where exactly like you did:
filter = {
category_id: #category_id,
is_active: true
}
#products = Product.where(filter)
Using a hash only works for equality (e.g. category_id = 123), so you can't put something like price > 100 in there. To add that criteria, just add another where to the chain:
#product = Product.where(filter).where('price > 100')
Or...
#product = Product.where(filter)
if params[:min_price]
#product = #product.where('price > ?', min_price)
end
You could have a bit of fun with scopes: write a scope that's actually a mini predicate builder, sanitizing and pattern-matching strings, and delegating to the standard predicate builder for other scalar types. E.g.
# app/models/concerns/searchable.rb
module Searchable
extend ActiveSupport::Concern
included do
scope :search, ->(params) {
params.inject(self) do |rel, (key, value)|
next rel if value.blank?
case value
when String
rel.where arel_table[key].matches '%%%s%%' % sanitize_sql_like(value)
when Range, Numeric, TrueClass, FalseClass
rel.where key => value
else
raise ArgumentError, "unacceptable search type"
end
end
}
end
end
# app/models/product.rb
class Product < ApplicationRecord
include Searchable
then you can
filter = { name: 'cheese', description: 'aged', age: 42.. }
Product.search(filter) #=> SELECT "products".* FROM products WHERE "products"."name" ILIKE '%cheese%' AND "products"."description" ILIKE '%aged%' AND "products"."age" >= 42

How to mimic asp.net get set in rails

I am trying to mimic asp.net get{} set{} in rails, here is what i tried in my controller:
def get_segment=(segment)
if params[:s] != nil
segment = params[:s]
else
segment = "personal"
end
end
Then i am trying to access it like this:
#something = get_segment
But it always returns as nil.
How can i do this?
Thanks
Why are you using get segment=(segment)?
look like what you are wanting to do is test params[:s], so the = is uncessary, as is the segment parameter.
def get_segment
if params[:s] != nil
params[:s]
else
"personal"
end
end
I think this would give you what you want.
If you just want to mimic get{} set{} in C#, the property Segment
private string _segment;
public string Segment {
get { return _segment; }
set { _segment = value; }
}
is written as followed in Ruby:
# get
def segment
#segment
end
# set
def segment=(value)
#segment = value
end
# if you don't have additional logic, you can just write
attr_accessor :segment
Then you can use some_instance.segment to retrieve the value and some_instance.segment = some_value to modify the value.
According to your code sample above, you want to fetch s parameter with a default value if it doesn't exist. You should define a getter, not in the setter form as you have provided.
def get_segment # or just "segment"
params[:s] || "personal"
end

Parsing an array of objects by attributes in rails 3.2

Is it possible to parse an array of objects to select them by attribute? I have a situation where I need to display all objects of a model grouped by an attribute on the index page. What I had been doing in my controller is this...
#xx_controller.rb
#group1 = City.where(:population => 'big')
#group2 = City.where(:population => 'medium')
#group3 = City.where(:population => 'small')
But I'd prefer to do something like this in the controller...
#cities = City.all
And in my view something along the lines of a query, rather than prepackaged instance variables -
#cities.where....
Any thoughts?
If you don't mind loading everything at once from the database, you can do:
#cities = City.all.group_by(&:population)
Which returns a hash whose keys are the possible values for the population attribute.
Then, on your view, you can access the cities on each 'group' by doing #cities['small'], #cities['medium'] and so on.
Do you mean something like this?
#cities = City.all
small_cities = #cities.select { |city| city.population == 'small' }
medium_cities = #cities.select { |city| city.population == 'medium' }
big_cities = #cities.select { | city| city.population == 'big' }

Resources