Input custom value from parameters to Rails SQL Query - ruby-on-rails

I have written so code to query lists of users who go jogging at a certain time and in certain locations, but I am getting the following error: ActiveRecord::StatementInvalid: SQLite3::SQLException: near "'jogging'"
Does this mean I cannot write a string into that variable? Are there any solutions to this?
users = User.where('ids in (:user_ids)', user_ids:
Activity.where('title :title AND created_at >= :created_at AND location_id: location_id',
{title: 'jogging', created_at: Time.zone.now.beginning_of_day, location_id:
Location.where('id in id', id: user.activities.where.not(location_id: nil)
.order('created_at DESC').first)}))

You can simplified your query this way
location_id = Location.where(
id: user.activities
.where.not(location_id: nil)
.order('created_at DESC').first
)
user_ids = Activity.where("title = ? AND created_at > ? AND location_id = ?",
"jogging", Time.zone.now.beginning_of_day, location_id)
users = User.where(id: user_ids)
But If you want to keep query one liner. You can use this
User.where('id IN(:user_ids)',
user_ids: Activity.where('title = :title AND created_at >= :created_at AND location_id = :location_id', title: 'jogging', created_at: Time.zone.now.beginning_of_day,
location_id: Location.where('id IN(:id)', id: user.activities.where.not(location_id: nil)
.order('created_at DESC').first).ids)
)
Note: I am assuming that you have user object to use above one liner query
Hope this will help you.

I think you forgot to add an = in the query:
Your query:
Activity.where('title :title ...
What you want:
Activity.where('title = :title ...
And if you don't need an operator like > or <, you can also use:
Activity.where(title: title)
If you then need to chain it, it's pretty simple:
Activity.where(title: title).where('foo < ?', 100)

Related

Pull Activerecord join table information through controller query

I have a query that generated a list of product data; I am working to dig in a bit deeper and extract an attribute from a join table (ProductSelections). Both RFIDTag and Products have many of the Product Selections model (i.e. the schema table contains both an rfid_tag_id and a product_id). Since I'm working from the product list, I'm deriving my query from here:
#original query (works as needed):
#warehoused_products = Product.includes(:rfid_tags).joins(:rfid_tags).where("rfid_tags.location_type = 'Warehouse' AND rfid_tags.location_id = ?", params[:id])
#Attempt at getting product selection data:
#product_histories = #warehoused_products.joins("INNER JOIN product_selections ON :product_selections.product_id = products.id").includes(:product_selections)
This raises a fairly large postgreSQL error when I check it:
raise #product_histories.inspect:
PG::SyntaxError: ERROR: syntax error at or near ":" LINE 1: ...eted_at" IS NULL INNER JOIN product_selections ON :product_s...
On adjusting the query:
raise #warehoused_products.includes(:product_selections).joins(:product_selections).where("product_selections.product_id = ?", params[:product_id]).first.inspect
I get a a nil result - error-free, and likely cleaner, but not quite there.
A Product Selection object looks like so, for reference:
#<ProductSelection id: 269, user_id: 2, listing_id: 11, product_id: 35, created_at: "2016-07-14 15:38:11", updated_at: "2016-08-08 21:10:13", rfid_tag_id: 575, part_id: nil, use_time: 2173274, account_id: 1, deleted_at: nil>
How can I resolve the query so that I can pull the associated table object?
#warehoused_products = Product.includes(:rfid_tags).where("rfid_tags.location_type = ? AND rfid_tags.location_id = ?", 'Warehouse', params[:id])
#product_histories = #warehoused_products.includes("product_selections").where("product_selections.product_id = ?", params[:product_id]).first.inspect

how to filter array with date range?

array = [#<Product id: 206, product: "first product", created_at: "2018-05-28 09:50:26", updated_at: "2018-05-28 09:50:26">, #<Product id: 207, product: "second product" ,created_at: "2018-05-28 09:50:46", updated_at: "2018-05-28 09:50:46"]
params[:from_date] = "2018-04-28 09:50:26"
params[:to_date] = "2018-05-28 09:50:46"
I'm filtering the above array with the following params (params[:from_date], params[:to_date]) using the below select statement.
array.select { |product| product.created_at >= params[:from_date] && product.created_at <= params[:to_date] }
I think there are more efficient methods than the above.
Are there any other methods to tackle this issue in a more efficient way?
Have you thought of improving the SQL query? It looks like your array contains the Products that are result from a query like Product.all. Maybe you can change that to something like:
Product.where("created_at >= :from_date AND created_at <= :to_date",
{from_date: params[:from_date], to_date: params[:to_date]})
I think that is more efficient approach because you will improve your app performance. And from my point of view the code looks more understandable this way.

Ruby - Extract value of a particular key from array of hashes

I have an array of hashes - #profiles which has data as:
[{:user_id=>5, :full_name=>"Emily Spot"},{:user_id=>7, :full_name=>"Kevin Walls"}]
I want to get full_name of say user_id = 7? I'm doing the following: but it's throwing an error that expression #profiles.find{|h| h[':user_id'] == current_user.id} is nil.
name = #profiles.find{ |h| h[':user_id'] == current_user.id }[':full_name']
if I use select instead of find then error is - no implicit conversion of String into Integer.
How do I search through the array of hashes?
UPDATE:
After #Eric's answer, I restructured my job model & view actions:
def full_names
profile_arr||= []
profile_arr = self.applications.pluck(:user_id)
#profiles = Profile.where(:user_id => profile_arr).select([:user_id, :first_name, :last_name]).map {|e| {user_id: e.user_id, full_name: e.full_name} }
#full_names = #profiles.each_with_object({}) do |profile, names|
names[profile[:user_id]] = profile[:full_name]
end
end
In the view....,
p #current_job.full_names[current_user.id]
#profiles is an array of hashes, with symbols as keys, whereas what you use is String objects.
So ':user_id' is a string, and you want symbol: :user_id:
#profiles.find{ |h| h[:user_id] == current_user.id }
I want to get full_name of say user_id == 7
#profiles.find { |hash| hash[:user_id] == 7 }.fetch(:full_name, nil)
Note, I used Hash#fetch for case, when there is no hash with value 7 at key :user_id.
As you've noticed, it's not very convenient to extract the name of user_id 7. You could modify your data structure a bit :
#profiles = [{:user_id=>5, :full_name=>"Emily Spot"},
{:user_id=>7, :full_name=>"Kevin Walls"}]
#full_names = #profiles.each_with_object({}) do |profile, names|
names[profile[:user_id]] = profile[:full_name]
end
p #full_names
# {5=>"Emily Spot", 7=>"Kevin Walls"}
p #full_names[7]
# "Kevin Walls"
p #full_names[6]
# nil
You didn't lose any information but name look-up is now much faster, easier and more robust.
Suggesting, to create a new hash that can make things simpler
Eg:
results = {}
profiles = [
{user_id: 5, full_name: "Emily Spot"},
{user_id: 7, full_name: "Kevin Walls"}
]
profiles.each do |details|
results[details[:user_id]] = details[:full_name]
end
Now, results will have:
{5: "Emily Spot", 7: "Kevin Walls"}
So, if you need to get full_name of say user_id = 7, simply do:
results[7] # will give "Kevin Walls"

Rails find_by_name on another table

I have Genders and based on Gender name create category and subcategories.
m = Gender.create(:gender => 'masculine')
c = Category.find_by_name("T-shirt", gender: m )
c.subcategories.create(:name => "Necklace" )
and so on.
While the answer of Amit Sharma works I'd suggest have several improvements for it.
Use the new Hash syntax:
gender = Gender.create!(gender: 'masculine')
Use find_by instead of where/first
category = Category.find_by(gender: gender, name: 'T-Shirt')
Use the bang variants when not checking the return value
category.subcategories.create!(name: 'Necklace')
Use if/present? instead of unless/blank?
if category.present?
category.subcategories.create!(name: 'Necklace')
end
(this is just a matter of taste. But my brain seems to have troubles parsing unless expressions:-))
Use find_or_initialize_by/find_or_create_by!
If you want to find the category OR create it if it does not exist, use find_or_initialize/find_or_create_by!() so you can avoid the nil check:
category = Category.find_or_create_by!(gender: gender, name: 'T-Shirt')
So in total i'd write it like:
gender = Gender.create!(gender: 'masculine')
category = Category.find_or_create_by!(gender: gender, name: 'T-Shirt')
category.subcategories.create!(name: 'Necklace')
You can try this.
m = Gender.create(:gender => 'masculine')
c = Category.where(name: "T-shirt", gender: m.gender ).first
c.subcategories.create(name: "Necklace" )
Please note above code will raise an exception if no category found with given condition, so to avoid that you can use following.
m = Gender.create(:gender => 'masculine')
c = Category.where(name: "T-shirt", gender: m.gender).try(:first)
unless c.blank?
c.subcategories.create(name: "Necklace" )
end

Count the records where a field doesn't equal value X

I'm trying to perform a count on results where a field is not equal to a specific value, but it always fails to returns results.
For instance, assuming Company and Products have a one to many relationship, I might get the following array back from ActiveRecord by querying for Company.find(63).products (which would be the SQL equivalent of SELECT "products".* FROM "products" WHERE "products"."company_id" = 63;):
<Product id: 1, company_id: 63, foo_id: 1>,
<Product id: 2, company_id: 63, foo_id: 3>,
<Product id: 3, company_id: 63, foo_id: nil>,
<Product id: 4, company_id: 63, foo_id: nil>
However, if I try to extend the above query to count everything but the first record with the following:
Company.find(63).products.where("foo_id != ?", 1).count
Which in SQL is:
SELECT COUNT(*) FROM "products" WHERE "products"."company_id" = 63 AND (foo_id != 1)
I always seem to get 1 back as a result when I expect to see 3. Why is this happening and how can I get it to count correctly?
If it is like you say you should get 1 instead of 0. What you are looking for is DISTINCT FROM as = and != on null types both return unknown as the result.
SELECT COUNT(*) FROM
products
WHERE products.company_id = 63 AND (foo_id IS DISTINCT FROM 1)
http://sqlfiddle.com/#!1/8b7a0/3
And as for further information. DISTINCT FROM is PostgreSQL exclusive so the standard version of doing this would be:
SELECT COUNT(*) FROM
products
WHERE products.company_id = 63 AND (foo_id<>1 OR foo_id IS NULL)
have you tried using <> instaed of !=
<>
Company.find(63).products.where("foo_id != ?", 1).count
should be
Company.find(63).products.where("foo_id <> ?", 1).count
but you should take it one step further:
class Product
def self.not_foo(foo_id)
where("foo_id <> ?", foo_id)
end
end
now
Company.find(63).products.not_foo(1).count

Resources