ActiveRecord includes query - ruby-on-rails

Products has Variants.
I'm trying to fetch Variants that has cost_currency: "USD"
A Variant may look like this:
Spree::Variant id: 22, cost_currency: "USD">
Spree::Product.includes('variants') returns a lot of Variants, but if I try to filter only the ones that has cost_currency = true it fails.
This is what I did:
2.2.3 :077 > Spree::Product.includes('variants')
.where( variants: { cost_currency: "USD" })
But that throws an ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: variants.cost_currency error, which I don't understand why.
Full stack trace:
SQL (0.5ms) SELECT "spree_products"."id" AS t0_r0, "spree_products"."name" AS t0_r1, "spree_products"."description"
AS t0_r2, "spree_products"."available_on" AS t0_r3, "spree_pro
ducts"."deleted_at" AS t0_r4, "spree_products"."slug" AS t0_r5, "spree_products"."meta_description" AS t0_r6, "spree_products"."meta_keywords" AS t0_r7, "spree_products"."tax_catego
ry_id" AS t0_r8, "spree_products"."shipping_category_id" AS t0_r9, "spree_products"."created_at" AS t0_r10, "spree_products"."updated_at" AS t0_r11, "spree_products"."promotionable"
AS t0_r12, "spree_products"."meta_title" AS t0_r13, "spree_variants"."id" AS t1_r0, "spree_variants"."sku" AS t1_r1, "spree_variants"."weight" AS t1_r2, "spree_variants"."height" A
S t1_r3, "spree_variants"."width" AS t1_r4, "spree_variants"."depth" AS t1_r5, "spree_variants"."deleted_at" AS t1_r6, "spree_variants"."is_master" AS t1_r7, "spree_variants"."produ
ct_id" AS t1_r8, "spree_variants"."cost_price" AS t1_r9, "spree_variants"."position" AS t1_r10, "spree_variants"."cost_currency" AS t1_r11, "spree_variants"."track_inventory" AS t1_
r12, "spree_variants"."tax_category_id" AS t1_r13, "spree_variants"."updated_at" AS t1_r14 FROM "spree_products" LEFT OUTER JOIN "spree_variants" ON "spree_variants"."product_id" =
"spree_products"."id" AND "spree_variants"."is_master" = ? AND "spree_variants"."deleted_at" IS NULL WHERE "spree_products"."deleted_at" IS NULL AND "variants"."cost_currency" = ?
[["is_master", "f"], ["cost_currency", "USD"]]
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: variants.cost_currency: SELECT "spree_products"."id" AS t0_r0, "spree_products"."name" AS t0_r1, "spree_produc
ts"."description" AS t0_r2, "spree_products"."available_on" AS t0_r3, "spree_products"."deleted_at" AS t0_r4, "spree_products"."slug" AS t0_r5, "spree_products"."meta_description" A
S t0_r6, "spree_products"."meta_keywords" AS t0_r7, "spree_products"."tax_category_id" AS t0_r8, "spree_products"."shipping_category_id" AS t0_r9, "spree_products"."created_at" AS t
0_r10, "spree_products"."updated_at" AS t0_r11, "spree_products"."promotionable" AS t0_r12, "spree_products"."meta_title" AS t0_r13, "spree_variants"."id" AS t1_r0, "spree_variants"
."sku" AS t1_r1, "spree_variants"."weight" AS t1_r2, "spree_variants"."height" AS t1_r3, "spree_variants"."width" AS t1_r4, "spree_variants"."depth" AS t1_r5, "spree_variants"."dele
ted_at" AS t1_r6, "spree_variants"."is_master" AS t1_r7, "spree_variants"."product_id" AS t1_r8, "spree_variants"."cost_price" AS t1_r9, "spree_variants"."position" AS t1_r10, "spre
e_variants"."cost_currency" AS t1_r11, "spree_variants"."track_inventory" AS t1_r12, "spree_variants"."tax_category_id" AS t1_r13, "spree_variants"."updated_at" AS t1_r14 FROM "spre
e_products" LEFT OUTER JOIN "spree_variants" ON "spree_variants"."product_id" = "spree_products"."id" AND "spree_variants"."is_master" = ? AND "spree_variants"."deleted_at" IS NULL
WHERE "spree_products"."deleted_at" IS NULL AND "variants"."cost_currency" = ?

Please try doing
Spree::Product.includes(:variants).where('spree_variants.cost_currency = "USD"')
or
Spree::Product.includes('variants').where('spree_variants.cost_currency = "USD" ' )
or
Spree::Product.includes('variants').where(spree_variants: { cost_currency: 'USD' })

Related

Rails ERROR: 'missing FROM-clause entry' when order through associated model

In my Rails 5 and Ruby 2.4 app I've got two models CaregiverAuthorization and Registrant:
# caregiver_authorization
class CaregiverAuthorization < ApplicationRecord
belongs_to :registrant
scope :pending, (-> { where(status: [statuses[:pending], statuses[:pending_renewal]]) })
end
# registrant
class Registrant < User
has_many :caregiver_authorizations,
(-> { order('created_at asc') })
def full_name
[first_name, last_name].join(' ')
end
end
Now I want to have all pending caregiver_authorizations ordered by registrant.full_name. Pretty simple, so I did:
CaregiverAuthorization.includes(:registrant).order("registrants.full_name")
Surprisingly I got below error:
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "registrants"
LINE 1: ...id" AND "users"."type" IN ('Registrant') ORDER BY registrant...
^
What did I missed?
[Edit]
Full error message:
SQL (0.8ms) SELECT "caregiver_authorizations"."id" AS t0_r0, "caregiver_authorizations"."registrant_id" AS t0_r1, "caregiver_authorizations"."reviewer_id" AS t0_r2, "caregiver_authorizations"."reviewed_at" AS t0_r3, "caregiver_authorizations"."status" AS t0_r4, "caregiver_authorizations"."created_at" AS t0_r5, "caregiver_authorizations"."updated_at" AS t0_r6, "users"."id" AS t1_r0, "users"."external_system_id" AS t1_r1, "users"."external_system_type" AS t1_r2, "users"."type" AS t1_r3, "users"."login_id" AS t1_r4, "users"."first_name" AS t1_r5, "users"."last_name" AS t1_r6, "users"."status" AS t1_r7, "users"."minor" AS t1_r8, "users"."created_at" AS t1_r9, "users"."updated_at" AS t1_r10, "users"."paper_entry" AS t1_r11, "users"."date_of_birth" AS t1_r12, "users"."caregiver_limit_override" AS t1_r13, "users"."patient_limit_override" AS t1_r14 FROM "caregiver_authorizations" LEFT OUTER JOIN "users" ON "users"."id" = "caregiver_authorizations"."registrant_id" AND "users"."type" IN ('Registrant') ORDER BY registrants.full_name
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "registrants"
LINE 1: ...id" AND "users"."type" IN ('Registrant') ORDER BY registrant...
^
: SELECT "caregiver_authorizations"."id" AS t0_r0, "caregiver_authorizations"."registrant_id" AS t0_r1, "caregiver_authorizations"."reviewer_id" AS t0_r2, "caregiver_authorizations"."reviewed_at" AS t0_r3, "caregiver_authorizations"."status" AS t0_r4, "caregiver_authorizations"."created_at" AS t0_r5, "caregiver_authorizations"."updated_at" AS t0_r6, "users"."id" AS t1_r0, "users"."external_system_id" AS t1_r1, "users"."external_system_type" AS t1_r2, "users"."type" AS t1_r3, "users"."login_id" AS t1_r4, "users"."first_name" AS t1_r5, "users"."last_name" AS t1_r6, "users"."status" AS t1_r7, "users"."minor" AS t1_r8, "users"."created_at" AS t1_r9, "users"."updated_at" AS t1_r10, "users"."paper_entry" AS t1_r11, "users"."date_of_birth" AS t1_r12, "users"."caregiver_limit_override" AS t1_r13, "users"."patient_limit_override" AS t1_r14 FROM "caregiver_authorizations" LEFT OUTER JOIN "users" ON "users"."id" = "caregiver_authorizations"."registrant_id" AND "users"."type" IN ('Registrant') ORDER BY registrants.full_name
Maybe this will be helpfully:
2.4.5 :065 > CaregiverAuthorization.includes(:registrant).order("registrants.full_name").to_sql
=> "SELECT \"caregiver_authorizations\".\"id\" AS t0_r0, \"caregiver_authorizations\".\"registrant_id\" AS t0_r1, \"caregiver_authorizations\".\"reviewer_id\" AS t0_r2, \"caregiver_authorizations\".\"reviewed_at\" AS t0_r3, \"caregiver_authorizations\".\"status\" AS t0_r4, \"caregiver_authorizations\".\"created_at\" AS t0_r5, \"caregiver_authorizations\".\"updated_at\" AS t0_r6, \"users\".\"id\" AS t1_r0, \"users\".\"external_system_id\" AS t1_r1, \"users\".\"external_system_type\" AS t1_r2, \"users\".\"type\" AS t1_r3, \"users\".\"login_id\" AS t1_r4, \"users\".\"first_name\" AS t1_r5, \"users\".\"last_name\" AS t1_r6, \"users\".\"status\" AS t1_r7, \"users\".\"minor\" AS t1_r8, \"users\".\"created_at\" AS t1_r9, \"users\".\"updated_at\" AS t1_r10, \"users\".\"paper_entry\" AS t1_r11, \"users\".\"date_of_birth\" AS t1_r12, \"users\".\"caregiver_limit_override\" AS t1_r13, \"users\".\"patient_limit_override\" AS t1_r14 FROM \"caregiver_authorizations\" LEFT OUTER JOIN \"users\" ON \"users\".\"id\" = \"caregiver_authorizations\".\"registrant_id\" AND \"users\".\"type\" IN ('Registrant') ORDER BY registrants.full_name"
Please note that includes does not necessarily join tables in the database query. It is built to avoid N+1 queries and it might happen that it simply runs two queries (one for the caregiver_authorizations and another for the associated registrants.
When you really need both tables in one query because you need to filter or order by them then you need to use joins. Additionally, the table is not named registrants but users because Registrant is a subclass of User. And you cannot order by full_name because full_name is not a database query but a method in your model. To order by full_name you need to simulate the behavior of that method in SQL.
I guess the following should work for you:
CaregiverAuthorization
.pending
.joins(:registrant)
.order(Arel.sql("LOWER(CONCAT(users.first_name, ' ', users.last_name))"))

How can I write an .includes() plus .where() on both top model and associated model in Rails4?

I didn't have any idea what was going wrong with this query:
#customers =
Customer.includes(:phone_numbers, :emails)
.select("first_name, last_name, zip_code, customers.id, street1, street2")
.where(:merchant_id => environment)
.where("phone_numbers.number ~* :key",
:key => key, :digits => digits)
It was working fine in Rails3.
I stumbled onto some documentation here that helped me resolve it in Rails4, but I'd like to know if there is a better way to write this query.
It produces the following (this is gnarly...):
"SELECT first_name, last_name, zip_code, customers.id, street1, street2, "customers"."id" AS t0_r0, "customers"."account_id" AS t0_r1, "customers"."first_name" AS t0_r2, "customers"."last_name" AS t0_r3, "customers"."street1" AS t0_r4, "customers"."street2" AS t0_r5, "customers"."city_id" AS t0_r6, "customers"."city" AS t0_r7, "customers"."state" AS t0_r8, "customers"."state_id" AS t0_r9, "customers"."zip_code" AS t0_r10, "customers"."country" AS t0_r11, "customers"."municipality" AS t0_r12, "customers"."latitude" AS t0_r13, "customers"."longitude" AS t0_r14, "customers"."location" AS t0_r15, "customers"."customer_type" AS t0_r16, "customers"."notes" AS t0_r17, "customers"."birthday" AS t0_r18, "customers"."merchant_id" AS t0_r19, "customers"."gmaps" AS t0_r20, "customers"."marketing" AS t0_r21, "customers"."communication_method" AS t0_r22, "customers"."account_status" AS t0_r23, "customers"."account_type" AS t0_r24, "customers"."created_at" AS t0_r25, "customers"."updated_at" AS t0_r26, "customers"."api_customer_id" AS t0_r27, "customers"."api_updated_at" AS t0_r28, "phone_numbers"."id" AS t1_r0, "phone_numbers"."customer_id" AS t1_r1, "phone_numbers"."account_id" AS t1_r2, "phone_numbers"."contact_id" AS t1_r3, "phone_numbers"."number" AS t1_r4, "phone_numbers"."country_code" AS t1_r5, "phone_numbers"."area_code" AS t1_r6, "phone_numbers"."extension" AS t1_r7, "phone_numbers"."created_at" AS t1_r8, "phone_numbers"."updated_at" AS t1_r9, "emails"."id" AS t2_r0, "emails"."customer_id" AS t2_r1, "emails"."account_id" AS t2_r2, "emails"."contact_id" AS t2_r3, "emails"."address" AS t2_r4, "emails"."created_at" AS t2_r5, "emails"."updated_at" AS t2_r6
FROM "customers"
LEFT OUTER JOIN "phone_numbers" ON "phone_numbers"."customer_id" = "customers"."id"
LEFT OUTER JOIN "emails" ON "emails"."customer_id" = "customers"."id"
WHERE "customers"."merchant_id" = 29 AND (phone_numbers.number ~* '424298')"
Since you aren't selecting any of the fields in phone_numbers or emails I think you want to use join not includes. Tricky bit is to make the LEFT OUTER work.
http://apidock.com/rails/v4.2.1/ActiveRecord/QueryMethods/joins
Something like this maybe.
Customer.
select("first_name, last_name, zip_code, customers.id, street1, street2").
joins("LEFT OUTER JOIN phone_numbers ON phone_numbers.customer_id = customers.id").
joins("LEFT OUTER JOIN emails ON emails.customer_id = customers.id").
where(:merchant_id => environment).
where("phone_numbers.number ~* :key", :key => key, :digits => digits)

Why is this Ruby Squeel (PostgreSQL) call returning nil, when it's clearly not?

I have a search page that allows a user to search for athletes based on criteria such as GPA and sport stats. When only one search filter is used, the PostgreSQL returns the correct answer. When more than one search filter is used, it will work ONLY if the same stat is being used in both search filters. For instance, if I do a search of "GPA > 3" and "GPA == 4" it will return the corresponding athlete. If I do "GPA > 3" and "ACT > 20" (both of which are true on the same athlete), it returns nothing (empty array).
For reference throughout this page, here is what #conditions looks like (it's transformed into an array in the code rather than a hash so the loop can iterate, but you get the gist):
{
"0"=>{"category"=>"1", "stat_type_id"=>"22", "predicate"=>"Greater Than",
"q"=>"2", "type"=>"stat"},
"1"=>{"category"=>"1", "stat_type_id"=>"2",
"predicate"=>"Greater Than", "q"=>"10", "type"=>"stat"}
}
Below is the code (Squeel syntax):
def query
relation = Athlete.listed.for_sport( self.sport )
return relation if #conditions.blank?
#conditions = #conditions.map { |k, v| v } if #conditions.is_a?(Hash)
debugger
#conditions.each do |condition|
case condition[:type]
when "stat"
if !condition[:q].eql?("")
relation = case condition[:predicate]
when /less than/i
relation.joins{stats}.where{(stats.stat_type_id.eq condition[:stat_type_id]) & (stats.value.lt (StatType.where(:id => condition[:stat_type_id]).first.display_as_decimal == true ? condition[:q].to_f.to_s : condition[:q].to_i.to_s))}
when /greater than/i
relation.joins{stats}.where{(stats.stat_type_id.eq condition[:stat_type_id]) & (stats.value.gt (StatType.where(:id => condition[:stat_type_id]).first.display_as_decimal == true ? condition[:q].to_f.to_s : condition[:q].to_i.to_s))}
when /equal to/i
relation.joins{stats}.where{(stats.stat_type_id.eq condition[:stat_type_id]) & (stats.value.eq (StatType.where(:id => condition[:stat_type_id]).first.display_as_decimal == true ? condition[:q].to_f.to_s : condition[:q].to_i.to_s))}
when /not equal to/i
relation.joins{stats}.where{(stats.stat_type_id.eq condition[:stat_type_id]) & (stats.value.not_eq (StatType.where(:id => condition[:stat_type_id]).first.display_as_decimal == true ? condition[:q].to_f.to_s : condition[:q].to_i.to_s))}
end
end
when "academic"
relation
when "social"
relation
end
end
relation
end
I have included below two PostgreSQL calls grabbed from ruby-debug (I'm using Squeel, a Ruby wrapper for PostgreSQL). If you scroll down in both calls, you'll see that the first and second calls are identical, but the second has an extra AND (and follows the same format as the first, should work fine).
Call 1 (one parameter):
SELECT "users"."id" AS t0_r0,
"users"."name" AS t0_r1,
"users"."first_name" AS t0_r2,
"users"."last_name" AS t0_r3,
"users"."facebook_id" AS t0_r4,
"users"."access_token" AS t0_r5,
"users"."birthday" AS t0_r6,
"users"."gender" AS t0_r7,
"users"."device_token" AS t0_r8,
"users"."current_city" AS t0_r9,
"users"."hometown" AS t0_r10,
"users"."relationship_status" AS t0_r11,
"users"."avatar_file_name" AS t0_r12,
"users"."avatar_content_type" AS t0_r13,
"users"."avatar_file_size" AS t0_r14,
"users"."avatar_updated_at" AS t0_r15,
"users"."email" AS t0_r16,
"users"."encrypted_password" AS t0_r17,
"users"."reset_password_token" AS t0_r18,
"users"."reset_password_sent_at" AS t0_r19,
"users"."remember_created_at" AS t0_r20,
"users"."sign_in_count" AS t0_r21,
"users"."current_sign_in_at" AS t0_r22,
"users"."last_sign_in_at" AS t0_r23,
"users"."current_sign_in_ip" AS t0_r24,
"users"."last_sign_in_ip" AS t0_r25,
"users"."authentication_token" AS t0_r26,
"users"."created_at" AS t0_r27,
"users"."updated_at" AS t0_r28,
"users"."time_zone" AS t0_r29,
"users"."username" AS t0_r30,
"users"."middle_name" AS t0_r31,
"users"."primary_sport_id" AS t0_r32,
"users"."state" AS t0_r33,
"users"."high_school" AS t0_r34,
"users"."phone" AS t0_r35,
"users"."user_type_id" AS t0_r36,
"users"."agreed_to_terms_and_conditions" AS t0_r37,
"users"."agreed_to_information_integrity" AS t0_r38,
"users"."agreed_to_content_responsibility" AS t0_r39,
"users"."account_type_id" AS t0_r40,
"users"."social_score" AS t0_r41,
"users"."recruit_year" AS t0_r42,
"users"."college_major" AS t0_r43,
"users"."secondary_sport_id" AS t0_r44,
"users"."handedness" AS t0_r45,
"users"."checkdin_id" AS t0_r46,
"users"."allow_twitter_sync" AS t0_r47,
"users"."allow_facebook_sync" AS t0_r48,
"users"."head_coach" AS t0_r49,
"users"."head_coach_email" AS t0_r50,
"users"."has_recruited_others" AS t0_r51,
"users"."customer_id" AS t0_r52,
"users"."last_4_digits" AS t0_r53,
"users"."removed_from_listing" AS t0_r54,
"users"."slug" AS t0_r55,
"users"."college_minor" AS t0_r56,
"users"."type" AS t0_r57,
"users"."billing_expiration_month" AS t0_r58,
"users"."billing_expiration_year" AS t0_r59,
"users"."billing_name" AS t0_r60,
"users"."billing_address" AS t0_r61,
"users"."billing_city" AS t0_r62,
"users"."billing_state" AS t0_r63,
"users"."billing_zip" AS t0_r64,
"users"."college_id" AS t0_r65,
"users"."agreed_to_age_requirements" AS t0_r66,
"users"."primary_sport_primary_position_id" AS t0_r67,
"users"."secondary_sport_primary_position_id" AS t0_r68,
"users"."recruit_status_id" AS t0_r69,
"users"."active_flag" AS t0_r70,
"users"."public" AS t0_r71,
"invitations"."id" AS t1_r0,
"invitations"."sender_id" AS t1_r1,
"invitations"."role_id" AS t1_r2,
"invitations"."to" AS t1_r3,
"invitations"."accepted_at" AS t1_r4,
"invitations"."token" AS t1_r5,
"invitations"."created_at" AS t1_r6,
"invitations"."updated_at" AS t1_r7,
"invitations"."current_state" AS t1_r8,
"invitations"."requester_id" AS t1_r9,
"invitations"."user_type" AS t1_r10,
"invitations"."type" AS t1_r11
FROM "users"
INNER JOIN "stats"
ON "stats"."athlete_id" = "users"."id"
LEFT OUTER JOIN "invitations"
ON "invitations"."requester_id" = "users"."id"
WHERE "users"."removed_from_listing" = 'f'
AND "users"."type" IN ( 'Athlete' )
AND (( "invitations"."current_state" IN ( 'accepted' )
OR "invitations"."current_state" IS NULL ))
AND ( users.primary_sport_id = 13
OR users.secondary_sport_id = 13 )
AND (( "stats"."stat_type_id" = 22
AND "stats"."value" > '2.0' ))
The result is an Athlete.
Call 2 (two parameters):
The second call however, returns [], but should be the same athlete...
SELECT "users"."id" AS t0_r0,
"users"."name" AS t0_r1,
"users"."first_name" AS t0_r2,
"users"."last_name" AS t0_r3,
"users"."facebook_id" AS t0_r4,
"users"."access_token" AS t0_r5,
"users"."birthday" AS t0_r6,
"users"."gender" AS t0_r7,
"users"."device_token" AS t0_r8,
"users"."current_city" AS t0_r9,
"users"."hometown" AS t0_r10,
"users"."relationship_status" AS t0_r11,
"users"."avatar_file_name" AS t0_r12,
"users"."avatar_content_type" AS t0_r13,
"users"."avatar_file_size" AS t0_r14,
"users"."avatar_updated_at" AS t0_r15,
"users"."email" AS t0_r16,
"users"."encrypted_password" AS t0_r17,
"users"."reset_password_token" AS t0_r18,
"users"."reset_password_sent_at" AS t0_r19,
"users"."remember_created_at" AS t0_r20,
"users"."sign_in_count" AS t0_r21,
"users"."current_sign_in_at" AS t0_r22,
"users"."last_sign_in_at" AS t0_r23,
"users"."current_sign_in_ip" AS t0_r24,
"users"."last_sign_in_ip" AS t0_r25,
"users"."authentication_token" AS t0_r26,
"users"."created_at" AS t0_r27,
"users"."updated_at" AS t0_r28,
"users"."time_zone" AS t0_r29,
"users"."username" AS t0_r30,
"users"."middle_name" AS t0_r31,
"users"."primary_sport_id" AS t0_r32,
"users"."state" AS t0_r33,
"users"."high_school" AS t0_r34,
"users"."phone" AS t0_r35,
"users"."user_type_id" AS t0_r36,
"users"."agreed_to_terms_and_conditions" AS t0_r37,
"users"."agreed_to_information_integrity" AS t0_r38,
"users"."agreed_to_content_responsibility" AS t0_r39,
"users"."account_type_id" AS t0_r40,
"users"."social_score" AS t0_r41,
"users"."recruit_year" AS t0_r42,
"users"."college_major" AS t0_r43,
"users"."secondary_sport_id" AS t0_r44,
"users"."handedness" AS t0_r45,
"users"."checkdin_id" AS t0_r46,
"users"."allow_twitter_sync" AS t0_r47,
"users"."allow_facebook_sync" AS t0_r48,
"users"."head_coach" AS t0_r49,
"users"."head_coach_email" AS t0_r50,
"users"."has_recruited_others" AS t0_r51,
"users"."customer_id" AS t0_r52,
"users"."last_4_digits" AS t0_r53,
"users"."removed_from_listing" AS t0_r54,
"users"."slug" AS t0_r55,
"users"."college_minor" AS t0_r56,
"users"."type" AS t0_r57,
"users"."billing_expiration_month" AS t0_r58,
"users"."billing_expiration_year" AS t0_r59,
"users"."billing_name" AS t0_r60,
"users"."billing_address" AS t0_r61,
"users"."billing_city" AS t0_r62,
"users"."billing_state" AS t0_r63,
"users"."billing_zip" AS t0_r64,
"users"."college_id" AS t0_r65,
"users"."agreed_to_age_requirements" AS t0_r66,
"users"."primary_sport_primary_position_id" AS t0_r67,
"users"."secondary_sport_primary_position_id" AS t0_r68,
"users"."recruit_status_id" AS t0_r69,
"users"."active_flag" AS t0_r70,
"users"."public" AS t0_r71,
"invitations"."id" AS t1_r0,
"invitations"."sender_id" AS t1_r1,
"invitations"."role_id" AS t1_r2,
"invitations"."to" AS t1_r3,
"invitations"."accepted_at" AS t1_r4,
"invitations"."token" AS t1_r5,
"invitations"."created_at" AS t1_r6,
"invitations"."updated_at" AS t1_r7,
"invitations"."current_state" AS t1_r8,
"invitations"."requester_id" AS t1_r9,
"invitations"."user_type" AS t1_r10,
"invitations"."type" AS t1_r11
FROM "users"
INNER JOIN "stats"
ON "stats"."athlete_id" = "users"."id"
LEFT OUTER JOIN "invitations"
ON "invitations"."requester_id" = "users"."id"
WHERE "users"."removed_from_listing" = 'f'
AND "users"."type" IN ( 'Athlete' )
AND (( "invitations"."current_state" IN ( 'accepted' )
OR "invitations"."current_state" IS NULL ))
AND ( users.primary_sport_id = 13
OR users.secondary_sport_id = 13 )
AND (( "stats"."stat_type_id" = 22
AND "stats"."value" > '2.0' ))
AND (( "stats"."stat_type_id" = 2
AND "stats"."value" > '10' ))
Any idea why this isn't working? Bumfuddled!
:::::UPDATE:::::
Fixed it by doing the following, and its still pretty fast:
def query
athletes = Athlete.listed.for_sport( self.sport )
relation = athletes
return relation if #conditions.blank?
#conditions = #conditions.map { |k, v| v } if #conditions.is_a?(Hash)
#conditions.each do |condition|
case condition[:type]
when "stat"
if !condition[:q].eql?("")
relation = case condition[:predicate]
when /less than/i
athletes.joins{stats}.where{(stats.stat_type_id.eq condition[:stat_type_id]) & (stats.value.lt (StatType.where(:id => condition[:stat_type_id]).first.display_as_decimal == true ? condition[:q].to_f.to_s : condition[:q].to_i.to_s))}
when /greater than/i
athletes.joins{stats}.where{(stats.stat_type_id.eq condition[:stat_type_id]) & (stats.value.gt (StatType.where(:id => condition[:stat_type_id]).first.display_as_decimal == true ? condition[:q].to_f.to_s : condition[:q].to_i.to_s))}
when /equal to/i
athletes.joins{stats}.where{(stats.stat_type_id.eq condition[:stat_type_id]) & (stats.value.eq (StatType.where(:id => condition[:stat_type_id]).first.display_as_decimal == true ? condition[:q].to_f.to_s : condition[:q].to_i.to_s))}
when /not equal to/i
athletes.joins{stats}.where{(stats.stat_type_id.eq condition[:stat_type_id]) & (stats.value.not_eq (StatType.where(:id => condition[:stat_type_id]).first.display_as_decimal == true ? condition[:q].to_f.to_s : condition[:q].to_i.to_s))}
end
end
when "academic"
relation
when "social"
relation
end
end
relation
end
Your second query has a WHERE clause that ends like this:
AND (( "stats"."stat_type_id" = 22
AND "stats"."value" > '2.0' ))
AND (( "stats"."stat_type_id" = 2
AND "stats"."value" > '10' ))
stat_type_id can't be equal to 2 and 22 at the same time. You'll need a second join to stats table like:
INNER JOIN "stats"
ON "stats"."athlete_id" = "users"."id"
INNER JOIN "stats" AS s2
ON "s2"."athlete_id" = "users"."id"
(...)
AND (( "stats"."stat_type_id" = 22
AND "stats"."value" > '2.0' ))
AND (( "s2"."stat_type_id" = 2
AND "s2"."value" > '10' ))
If you don't want to do joins dynamicaly the alternative is a subquery like:
SELECT athlete_id
FROM stats
WHERE
( "stats"."stat_type_id" = 22
AND "stats"."value" > '2.0' )
OR ( "s2"."stat_type_id" = 2
AND "s2"."value" > '10' )
GROUP BY athlete_id
HAVING COUNT(*)=<the number of parameters you want to filter by>
This will return all athletes that meet the criteria so join that with the rest of your tables.

How to understand logic for advanced query in Ruby on Rails?

I have a user model
class User < ActiveRecord::Base
has_many :messages_received, :class_name=>"Message", :foreign_key=>'receiving_id', :conditions => {:message_type => "incoming"}
has_many :messages_sent, :class_name=>"Message", :foreign_key=>'sender_id', :conditions => {:message_type => "outgoing"}
end
Now I want to fetch all the users who havn't sent any messages in last 15 days so that I can notify them. I tried this but it's not working.
has_many :last_7_days_inactive_users, :class_name=>"Message", :foreign_key=>'receiving_id', :conditions =>{:messages_sent => ("created_at < ? " ,Time.now - 7.days)}
How can I amend this query?
Edit
User.last_7_days_inactive_users
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'users.messages_sent' in 'where clause': SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`email` AS t0_r2, `users`.`phone_number` AS t0_r3, `users`.`active` AS t0_r4, `users`.`created_at` AS t0_r5, `users`.`updated_at` AS t0_r6, `users`.`groups_count` AS t0_r7, `users`.`daily_sms_count` AS t0_r8, `users`.`new_user_sms_count` AS t0_r9, `users`.`source_type` AS t0_r10, `users`.`messages_count` AS t0_r11, `users`.`salt` AS t0_r12, `users`.`hashed_password` AS t0_r13, `users`.`verified_email` AS t0_r14, `users`.`email_verification_code` AS t0_r15, `users`.`auto_generated_password` AS t0_r16, `users`.`forgot_password_code_short` AS t0_r17, `users`.`forgot_password_code_long` AS t0_r18, `messages`.`id` AS t1_r0, `messages`.`body` AS t1_r1, `messages`.`sender_number` AS t1_r2, `messages`.`time_at` AS t1_r3, `messages`.`operator` AS t1_r4, `messages`.`circle` AS t1_r5, `messages`.`receiving_number` AS t1_r6, `messages`.`sender_id` AS t1_r7, `messages`.`system_phone_number_id` AS t1_r8, `messages`.`created_at` AS t1_r9, `messages`.`updated_at` AS t1_r10, `messages`.`message_type` AS t1_r11, `messages`.`parent_id` AS t1_r12, `messages`.`receiving_id` AS t1_r13, `messages`.`group_id` AS t1_r14, `messages`.`mp_hash` AS t1_r15, `messages`.`junk` AS t1_r16, `messages`.`command` AS t1_r17, `messages`.`message_size` AS t1_r18 FROM `users` LEFT OUTER JOIN `messages` ON `messages`.`sender_id` = `users`.`id` AND `messages`.`message_type` = 'incoming' WHERE `users`.`messages_sent` = 'messages_sent.created_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 7 DAY)'
It's very important to note that unless you use a Proc, your time will be evaluated once and once only and will represent a time locked in from the instant the class is loaded. In production mode this will persist for the life-time of the process which could be days if not months. This is probably not what you want.
Instead you may have better luck with declaring a scope on User and fetching them this way:
named_scope :last_7_days_inactive_users,
:include => :messages_sent,
:conditions => "messages.created_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 7 DAY)"
For instance:
inactive_users = User.last_7_days_inactive_users.all
Edit: Updated the conditions expression to be direct, not a hash.

Can't Join a model when using belongs_to

class Exercise < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :exercises
end
Why can't I find all Exercises there were created by users with the role of "admin"?
Exercise.first :include => [:user], :conditions => ['user.role = "admin"']
The error shoes that the users table is not being joined.
Exercise.first :include => [:user], :conditions => ['user.role = "admin"']
Exercise Load Including Associations (0.0ms) Mysql::Error: Unknown column 'user.role' in 'where clause': SELECT `exercises`.`id` AS t0_r0, `exercises`.`title` AS t0_r1, `exercises`.`description` AS t0_r2, `exercises`.`fitness_type` AS t0_r3, `exercises`.`trackables` AS t0_r4, `exercises`.`user_id` AS t0_r5, `exercises`.`created_at` AS t0_r6, `exercises`.`updated_at` AS t0_r7, `users`.`id` AS t1_r0, `users`.`created_at` AS t1_r1, `users`.`updated_at` AS t1_r2, `users`.`first_name` AS t1_r3, `users`.`last_name` AS t1_r4, `users`.`email` AS t1_r5, `users`.`crypted_password` AS t1_r6, `users`.`password_salt` AS t1_r7, `users`.`persistence_token` AS t1_r8, `users`.`single_access_token` AS t1_r9, `users`.`perishable_token` AS t1_r10, `users`.`login_count` AS t1_r11, `users`.`failed_login_count` AS t1_r12, `users`.`last_request_at` AS t1_r13, `users`.`current_login_at` AS t1_r14, `users`.`last_login_at` AS t1_r15, `users`.`current_login_ip` AS t1_r16, `users`.`last_login_ip` AS t1_r17, `users`.`role` AS t1_r18, `users`.`avatar_file_name` AS t1_r19, `users`.`avatar_content_type` AS t1_r20, `users`.`avatar_file_size` AS t1_r21, `users`.`avatar_updated_at` AS t1_r22 FROM `exercises` LEFT OUTER JOIN `users` ON `users`.id = `exercises`.user_id WHERE (user.role = "admin") LIMIT 1
ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'user.role' in 'where clause': SELECT `exercises`.`id` AS t0_r0, `exercises`.`title` AS t0_r1, `exercises`.`description` AS t0_r2, `exercises`.`fitness_type` AS t0_r3, `exercises`.`trackables` AS t0_r4, `exercises`.`user_id` AS t0_r5, `exercises`.`created_at` AS t0_r6, `exercises`.`updated_at` AS t0_r7, `users`.`id` AS t1_r0, `users`.`created_at` AS t1_r1, `users`.`updated_at` AS t1_r2, `users`.`first_name` AS t1_r3, `users`.`last_name` AS t1_r4, `users`.`email` AS t1_r5, `users`.`crypted_password` AS t1_r6, `users`.`password_salt` AS t1_r7, `users`.`persistence_token` AS t1_r8, `users`.`single_access_token` AS t1_r9, `users`.`perishable_token` AS t1_r10, `users`.`login_count` AS t1_r11, `users`.`failed_login_count` AS t1_r12, `users`.`last_request_at` AS t1_r13, `users`.`current_login_at` AS t1_r14, `users`.`last_login_at` AS t1_r15, `users`.`current_login_ip` AS t1_r16, `users`.`last_login_ip` AS t1_r17, `users`.`role` AS t1_r18, `users`.`avatar_file_name` AS t1_r19, `users`.`avatar_content_type` AS t1_r20, `users`.`avatar_file_size` AS t1_r21, `users`.`avatar_updated_at` AS t1_r22 FROM `exercises` LEFT OUTER JOIN `users` ON `users`.id = `exercises`.user_id WHERE (user.role = "admin") LIMIT 1
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/abstract_adapter.rb:219:in `log'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/mysql_adapter.rb:323:in `execute'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/mysql_adapter.rb:608:in `select'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all_without_query_cache'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/abstract/query_cache.rb:62:in `select_all'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1617:in `select_all_rows'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1395:in `find_with_associations'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1393:in `catch'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1393:in `find_with_associations'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1546:in `find_every'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1505:in `find_initial'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:613:in `find'
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:623:in `first'
from (irb):7
>>
I know I can do this in a 2 step process by first finding all admin users then find exercises by "user_id" but want to make this into a named_scope.
I am pretty sure it is
Exercise.first :include => [:user], :conditions => ['users.role = "admin"']
instead of
Exercise.first :include => [:user], :conditions => ['user.role = "admin"']
Your condition should have users as plural: users.role

Resources