Ruby on Rails array not returning results - ruby-on-rails

Following were my query in controller:
#cref = Creference.where("lower(name) LIKE ?", "#{#city.downcase}")
if #cref.present?
cities_array = #cref.map {|con| "%#{con.countries}%" }
#cities_array return --> ["%["Kuching", "Kota Kinabalu"]%"]
#products.where("city ILIKE ANY ( array[?] )", cities_array) --> []
end
The product doesn't return any result despite there is city with above name in Product table. Thanks!!
SCHEMA
product:
create_table "products", force: :cascade do |t|
t.integer "user_id"
t.string "name"
t.integer "payment_type", limit: 2
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "product_category_id"
t.integer "location_id"
t.text "description"
t.text "highlight"
t.integer "price_cents"
t.integer "zip"
t.string "country"
t.string "state"
t.string "city"
t.string "address"
t.string "apt"
t.integer "refund_day"
t.string "currency"
t.integer "refund_percent"
t.integer "refundable", limit: 2, default: 0
t.integer "step", limit: 2, default: 0
t.integer "discount", default: 0
t.string "slug"
t.integer "status", limit: 2, default: 1
t.integer "verification", limit: 2, default: 0
end
creference:
create_table "creferences", force: :cascade do |t|
t.string "name"
t.string "countries", default: [], array: true
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

You can do something like that:
#cref = Creference.where("lower(name) LIKE ?", "#{#city.downcase}")
if #cref.present?
cities_array = #cref.collect {|con| con.countries }.flatten
#cities_array will return --> ["Kuching", "Kota Kinabalu"]
#products.where("city IN (?)", cities_array)
# the IN query will find the city names that are in the cities_array
# but in that case your product tables city name should be in lowercase
# as you are fetching lowercase city names from Creference
end
N.B. I've not tested the code above. But it should work. :)

First, check the value of cities_array to see what is returns.
Second, try to append to_sql at the end of the statement and to print it to see what the sql looks like:
puts #products.where("city ILIKE ANY ( array[?] )", cities_array).to_sql
Last but not least, Product.count to see that you actually have products in your table :).

Related

How to use AND condition in an ActiveRecord query

I currently have these three tables:
create_table "cocktail_ingredients", force: :cascade do |t|
t.integer "cocktail_id"
t.integer "ingredient_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["cocktail_id"], name: "index_cocktail_ingredients_on_cocktail_id"
t.index ["ingredient_id"], name: "index_cocktail_ingredients_on_ingredient_id"
create_table "cocktails", force: :cascade do |t|
t.string "title"
t.string "ingredients"
t.text "method"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
create_table "ingredients", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
I'm trying to do a seach that returns all the cocktails that contain ALL of the ingredients in my search.
So far i've managed to come up with this:
CocktailIngredient.where(ingredient_id: [1, 4]).map(&:cocktail).uniq
Which returns all the cocktails that include the ingredient_id of 1 or 4. I am trying to return only the cocktails that contain BOTH ingredients with the id 1 AND 4.
Any help would be much appreciated.
Here is a generalised version, if you have an ingredient_ids array:
cocktail_ingredients = [1, 4]
cocktail_ingredients = CocktailIngredient.where(ingredient_id: ingredient_ids).select(:cocktail_id)
cocktail_ingredients = cocktail_ingredients.group(:cocktail_id).having('COUNT(ingredient_id) >= ?', ingredient_ids.count)
cocktails = Cocktail.where(id: cocktail_ingredients.select(:cocktail_id))
Here is a tested scope for your Cocktail model (with Rspec and FactoryGirl). You'll just have to call Cocktail.with_ingredients([1,4]).
In cocktail.rb:
scope :with_ingredients, (lambda do |ingredient_ids|
cocktail_ingredients = CocktailIngredient.where(ingredient_id: ingredient_ids).select(:cocktail_id)
cocktail_ingredients = cocktail_ingredients.group(:cocktail_id).having('COUNT(ingredient_id) >= ?', ingredient_ids.count)
where(id: cocktail_ingredients.select(:cocktail_id))
end)
And cocktail_spec.rb: https://gist.github.com/ArnoHolo/54b9259fbaa067d7abbf04a73d94ec40
The best I found for now is:
Cocktail.joins(:cocktail_ingredients).where(cocktail_ingredients: { ingredient_id: 1, cocktail_id: CocktailIngredient.where(ingredient_id: 4).select(:cocktail_id) })
I don't think it's the best we can do, but it's MUCH MORE optimized then previous solution

Rails 4 where clause on nested joined tables

currently I have this query:
Mother.joins(camp: [{ sub_center: [{ health_center: :block }] }])
which essentially joins the mother table to the block table through the camp, sub_center, and health_center tables (I know it's a lot to take). I want to be able to do something like this:
Mother.joins(camp: [{ sub_center: [{ health_center: :block }] }]).where(sub_center_id: 1)
However, I get that sub_center_id is an undefined column PG::UndefinedColumn: ERROR: column mothers.sub_center_id does not exist
How do I get it to query camp.sub_center_id instead of mother.sub_center_id? In other words, how do I execute a where clause on nested joined tables?
Here's the schema for given tables:
create_table "mothers", force: :cascade do |t|
t.integer "camp_id"
t.integer "eligible_couple_id"
t.string "pcts_id"
t.integer "thumb_id"
t.string "survey_id"
t.string "necklace_id"
t.integer "necklace_replacement_number", default: 0
t.boolean "facility_referral_true", default: false
t.float "weight_at_las_pnc"
t.integer "created_by"
t.integer "updated_by"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "reg_id"
t.string "nfc_data"
t.datetime "date_of_registration"
t.integer "status", default: 1, null: false
t.datetime "deleted_at"
t.datetime "completed_at"
end
create_table "camps", force: :cascade do |t|
t.string "english_name"
t.string "local_name"
t.float "latitude"
t.float "longitude"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "created_by"
t.integer "updated_by"
t.integer "sub_center_id"
t.integer "week_of_the_month"
t.integer "status", default: 1
t.integer "day"
t.datetime "deleted_at"
end
create_table "health_centers", force: :cascade do |t|
t.integer "block_id"
t.string "english_name"
t.string "local_name"
t.boolean "phc_true", default: false
t.boolean "ipd_true", default: false
t.boolean "fru_true", default: false
t.boolean "always_open_true", default: false
t.string "phone_number"
t.integer "created_by"
t.integer "updated_by"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status", default: 1
t.datetime "deleted_at"
end
create_table "sub_centers", force: :cascade do |t|
t.integer "health_center_id"
t.string "english_name"
t.string "local_name"
t.boolean "phc_true", default: false
t.boolean "ipd_true", default: false
t.boolean "fru_true", default: false
t.boolean "always_open_true", default: false
t.string "phone_number"
t.float "latitude"
t.float "longitude"
t.integer "created_by"
t.integer "updated_by"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status", default: 1
t.datetime "deleted_at"
end
create_table "blocks", force: :cascade do |t|
t.string "english_name"
t.string "local_name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "created_by"
t.integer "updated_by"
t.integer "status", default: 1
t.datetime "deleted_at"
end
You can always use the string method to reference the column, instead of the symbol one.
Mother.
joins(camp: { sub_center: { health_center: :block } }).
where('camps.sub_center_id = ?', 1)
or with the symbol method
Mother.
joins(camp: { sub_center: { health_center: :block } }).
where(camps: { sub_center_id: 1 } )

Post Sorting By Vote Database

I have a page where users can add favorites. Right now the page sorts their posts into favorites but sorts them by .order(created_at: :desc) for the Post databases created_at field. How can I change this to use the Vote table and its created_at frield.
#posts = Post.where(id: current_user.find_voted_items.map(&:id)).order(created_at: :desc).paginate(:page => params[:page], :per_page => 36)
Here is the relevant schema
create_table "votes", force: :cascade do |t|
t.integer "votable_id"
t.string "votable_type"
t.integer "voter_id"
t.string "voter_type"
t.boolean "vote_flag"
t.string "vote_scope"
t.integer "vote_weight"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "posts", force: :cascade do |t|
t.string "soundcloud"
t.string "title"
t.string "artist"
t.string "audio"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "download"
t.integer "cached_votes_total", default: 0
t.integer "cached_votes_score", default: 0
t.integer "cached_votes_up", default: 0
t.integer "cached_votes_down", default: 0
t.integer "cached_weighted_score", default: 0
t.integer "cached_weighted_total", default: 0
t.float "cached_weighted_average", default: 0.0
end
Thank you
If you want to return the posts, it makes more sense to use the posts database. If you want to return all of the votes for one post using the vote database instead, you could try something like this:
#votes = Vote.where(votable_id: some_post.id).order('created_at DESC')
But I'm not sure I understand what you would like to return from the Vote database.

How do i check if my user is admin in rails?

User has many profiles such has admin and merchant etc in my rails db schema. Now how do i write code to check if current user is admin ?
More info : User table is separate and profile table is separate. They are linked in user model through has_and_belongs_to_many :profiles
Here is my schema :
create_table "profiles", force: true do |t|
t.string "name", null: false
t.string "description"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "profiles", ["name"], name: "index_profiles_on_name", unique: true, using: :btree
create_table "profiles_roles", id: false, force: true do |t|
t.integer "profile_id"
t.integer "role_id"
end
create_table "profiles_users", id: false, force: true do |t|
t.integer "profile_id"
t.integer "user_id"
end
create_table "role_types", force: true do |t|
t.string "name"
t.string "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "roles", force: true do |t|
t.string "name", null: false
t.string "description"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "role_type_id"
t.string "display_name"
end
add_index "roles", ["name"], name: "index_roles_on_name", unique: true, using: :btree
create_table "users", force: true do |t|
t.string "username", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "email", default: ""
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.string "first_name"
t.string "last_name"
t.string "msisdn"
t.string "city"
t.string "address"
t.integer "e_bills_count", default: 0
t.string "notification_url"
t.string "notification_params", default: [], array: true
t.boolean "notification_post", default: true
t.string "legal_info"
t.string "greeting_message"
t.boolean "email_notification", default: false
t.string "website"
t.string "logo"
t.string "signature"
t.string "additional_notification_params", default: ""
t.string "currency"
end
There is no single dogmatic 'rails-way' to do this. The simplest way is to add a boolean column 'is_admin'. But because this does not scale to well with other scenarios, this is often widened to a schema where there is an extra 'roles' table and a has_many relationship to the user model.
Thare are also gems for more sophisticated authorization models:
https://github.com/ryanb/cancan/wiki/Role-Based-Authorization
An example implementation could be:
self.profiles.map(&:name).include?('admin')

ActiveResource::ResourceNotFound: Failed. Response code = 404. Response message = Not Found

Hi I'm working on a rails project, I updated the development database of my project recently. I had sqlite, now I have Postgres.
I have this method for my Product model:
def self.update_products!
ec_products = ElemetalCapital::Product.all
transaction do
ec_products.each do |ec_product|
product = ElemetalCapitalProduct.where(id: ec_product.id).first_or_initialize
product.spot_id = ec_product.spot
product.goldtrex_markup ||= 1 # default to a 1% markup
product.description = ec_product.description
product.metal = ec_product.metal
product.weight = ec_product.weight
product.elemetal_capital_premium = ec_product.premiumBuy
product.save!
end
end
end
Before the Postgres update, the method was working properly. However, after the update I'm getting this error, how can I fix that problem:
[2] pry(main)> Product.update_products!
(0.5ms) BEGIN
ElemetalCapitalProduct Load (0.5ms) SELECT "products".* FROM "products" WHERE "products"."type" IN ('ElemetalCapitalProduct') AND "products"."id" = $1 ORDER BY "products"."id" ASC LIMIT 1 [["id", "GKILO-OPM"]]
(0.4ms) ROLLBACK
ActiveResource::ResourceNotFound: Failed. Response code = 404. Response message = Not Found.
from /Users/enriquesalceda/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activeresource-4.0.0/lib/active_resource/connection.rb:144:in `handle_response'
Something that is very strange on the is the "products"."id" = $1, it shouldn't be $1.
This app use the API of a supplier elemetal capital, which provides the info about their products, and prices, then after a few calculations we update the shopify database.
Just for the record:
This is my entire Product model:
class Product < ActiveRecord::Base
self.primary_key = :id
monetize :elemetal_capital_premium_cents, allow_nil: true
belongs_to :spot
def to_hash
instance_variables.each_with_object({}) do |var, hash|
hash[var.to_s.delete("#")] = instance_variable_get(var)
end
end
def metal_name
case metal
when "Ag" then "Silver"
when "Au" then "Gold"
when "Pd" then "Palladium"
when "Pt" then "Platinum"
end
end
def price
# return 1300 if spot.nil?
spot_price = spot.ask
ec_price = spot_price + elemetal_capital_premium
total_price = ec_price * weight
gt_price = total_price + (goldtrex_markup / 100 * total_price)
gt_price.exchange_to(:AUD)
end
def shopify_variant_data
{
barcode: id,
price: price.to_s,
weight: weight,
weight_unit: "oz",
grams: weight * 31.1034768
}
end
before_create :shopify_create
def shopify_create
data = {
title: "#{metal_name} - #{description}",
variants: [
shopify_variant_data
]
}
sp = ShopifyAPI::Product.create(data)
self.shopify_id = sp.id
end
before_update :shopify_update
def shopify_update
sp = ShopifyAPI::Product.find(shopify_id)
variant = sp.variants.first
shopify_variant_data.each do |k, v|
instance_variable_set("##{k.to_s}".to_sym, v)
end
variant.save!
end
def self.update_products!
ec_products = ElemetalCapital::Product.all
transaction do
ec_products.each do |ec_product|
product = ElemetalCapitalProduct.where(id: ec_product.id).first_or_initialize
product.spot_id = ec_product.spot
product.goldtrex_markup ||= # default to a 1% markup
product.description = ec_product.description
product.metal = ec_product.metal
product.weight = ec_product.weight
product.elemetal_capital_premium = ec_product.premiumBuy
product.save!
end
end
end
end
This is the schema:
ActiveRecord::Schema.define(version: 20150609085027) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "deliveries", force: :cascade do |t|
t.string "receiver"
t.datetime "delivery_day"
t.string "tracking_number"
t.text "delivery_notes"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "ticket_id"
end
create_table "elemetal_capital_trades", force: :cascade do |t|
t.string "location"
t.string "currency"
t.string "side"
t.string "elemetal_capital_product_id"
t.integer "quantity"
t.string "elemetal_capital_trade_id"
t.float "price_per_unit"
t.float "weight"
t.float "price_per_weight"
t.float "price_total"
t.string "time_stamp_origin"
t.string "metal"
t.float "spot"
t.integer "line_item_id"
end
add_index "elemetal_capital_trades", ["line_item_id"], name: "index_elemetal_capital_trades_on_line_item_id", using: :btree
create_table "employees", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "items", force: :cascade do |t|
t.integer "quantity"
t.string "item_description"
t.float "unit_price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "ticket_id"
end
create_table "line_items", force: :cascade do |t|
t.integer "quantity"
t.integer "shopify_line_item_id"
t.integer "order_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "shopify_product_id", limit: 8
end
add_index "line_items", ["order_id"], name: "index_line_items_on_order_id", using: :btree
create_table "orders", force: :cascade do |t|
t.integer "order_number"
t.integer "shopify_order_id"
t.integer "total"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "payments", force: :cascade do |t|
t.datetime "value_date"
t.integer "reference_number"
t.float "contract_rate", default: 0.0
t.string "trade_notes"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "ticket_id"
t.float "usd_payment", default: 0.0
end
create_table "products", id: false, force: :cascade do |t|
t.string "id", null: false
t.string "type", null: false
t.text "description", null: false
t.decimal "weight", null: false
t.string "metal", null: false
t.string "spot_id", null: false
t.integer "elemetal_capital_premium_cents"
t.decimal "goldtrex_markup", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "shopify_id", limit: 8, null: false
end
add_index "products", ["shopify_id"], name: "index_products_on_shopify_id", unique: true, using: :btree
add_index "products", ["spot_id"], name: "index_products_on_spot_id", using: :btree
create_table "spots", id: false, force: :cascade do |t|
t.string "id", null: false
t.integer "bid_cents", null: false
t.integer "ask_cents", null: false
end
create_table "tickets", force: :cascade do |t|
t.integer "goldtrex_employee"
t.string "ticket_number"
t.datetime "elemetal_capital_order_date"
t.string "trader"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "au"
t.float "au_spot_price"
t.boolean "ag"
t.float "ag_spot_price"
t.boolean "deposit"
t.float "deposit_amount"
end
end
Rails uses ORM, which hides all logic of working with DB into nice methods. Basically that means that if you change the DB – nothing will happen, app will continue to work as expected (should mention, this statement does not applicable in any case as DBs differ, but not in this case). If you get 404 – it means item is missing in the database, nothing wrong about that.
When you said you changed DB from sqlite to Postgres – had you migrated the data? Try run ElemetalCapitalProduct.count from the console to ensure it has anything. If it does, compare data you had in sqlite and the data you receive in Postgres.

Resources