So this is what I have in my dev (SQLite) environment that works perfectly:
key = "%#{keywords}%"
listings = Listing.order(:headline)
listings = listings.includes(:neighborhood).where("headline like ? or neighborhoods.name like ?", key, key) if keywords.present?
But, when I try something similar on Heroku, I get a number of errors:
listings = listings.includes(:neighborhood).where("headline like ? or neighborhoods.name like ?", key, key)
ActiveRecord::ConfigurationError: Association named 'neighborhoods' was not found; perhaps you misspelled it?
I also tried the following & got the corresponding error:
> listings = Listing.includes(:neighborhood).where("headline like :keywords or neighborhood.name like :keywords", :keywords => "%#{keywords}%")
SQL (3.1ms) SELECT "listings"."id" AS t0_r0, "listings"."listing_category_id" AS t0_r1, "listings"."listing_type_id" AS t0_r2, "listings"."user_id" AS t0_r3, "listings"."boro_id" AS t0_r4, "listings"."neighborhood_id" AS t0_r5, "listings"."building_id" AS t0_r6, "listings"."term_id" AS t0_r7, "listings"."headline" AS t0_r8, "listings"."property_type_id" AS t0_r9, "listings"."unit_num" AS t0_r10, "listings"."common_charges" AS t0_r11, "listings"."taxes" AS t0_r12, "listings"."maintenance" AS t0_r13, "listings"."num_bedrooms" AS t0_r14, "listings"."num_bathrooms" AS t0_r15, "listings"."square_footage" AS t0_r16, "listings"."list_square_footage" AS t0_r17, "listings"."description" AS t0_r18, "listings"."condition_id" AS t0_r19, "listings"."pet_policy_id" AS t0_r20, "listings"."exposure_id" AS t0_r21, "listings"."floor_plan" AS t0_r22, "listings"."photo" AS t0_r23, "listings"."security" AS t0_r24, "listings"."coop_deductible" AS t0_r25, "listings"."flip_tax" AS t0_r26, "listings"."house_families" AS t0_r27, "listings"."house_extensions" AS t0_r28, "listings"."house_stories" AS t0_r29, "listings"."house_units" AS t0_r30, "listings"."created_at" AS t0_r31, "listings"."updated_at" AS t0_r32, "listings"."price" AS t0_r33, "neighborhoods"."id" AS t1_r0, "neighborhoods"."name" AS t1_r1, "neighborhoods"."created_at" AS t1_r2, "neighborhoods"."updated_at" AS t1_r3, "neighborhoods"."boro_id" AS t1_r4 FROM "listings" LEFT OUTER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%')
ActiveRecord::StatementInvalid: PG::Error: ERROR: missing FROM-clause entry for table "neighborhood"
LINE 1: ..._id" WHERE (headline like '%prospect heights%' or neighborho...
^
: SELECT "listings"."id" AS t0_r0, "listings"."listing_category_id" AS t0_r1, "listings"."listing_type_id" AS t0_r2, "listings"."user_id" AS t0_r3, "listings"."boro_id" AS t0_r4, "listings"."neighborhood_id" AS t0_r5, "listings"."building_id" AS t0_r6, "listings"."term_id" AS t0_r7, "listings"."headline" AS t0_r8, "listings"."property_type_id" AS t0_r9, "listings"."unit_num" AS t0_r10, "listings"."common_charges" AS t0_r11, "listings"."taxes" AS t0_r12, "listings"."maintenance" AS t0_r13, "listings"."num_bedrooms" AS t0_r14, "listings"."num_bathrooms" AS t0_r15, "listings"."square_footage" AS t0_r16, "listings"."list_square_footage" AS t0_r17, "listings"."description" AS t0_r18, "listings"."condition_id" AS t0_r19, "listings"."pet_policy_id" AS t0_r20, "listings"."exposure_id" AS t0_r21, "listings"."floor_plan" AS t0_r22, "listings"."photo" AS t0_r23, "listings"."security" AS t0_r24, "listings"."coop_deductible" AS t0_r25, "listings"."flip_tax" AS t0_r26, "listings"."house_families" AS t0_r27, "listings"."house_extensions" AS t0_r28, "listings"."house_stories" AS t0_r29, "listings"."house_units" AS t0_r30, "listings"."created_at" AS t0_r31, "listings"."updated_at" AS t0_r32, "listings"."price" AS t0_r33, "neighborhoods"."id" AS t1_r0, "neighborhoods"."name" AS t1_r1, "neighborhoods"."created_at" AS t1_r2, "neighborhoods"."updated_at" AS t1_r3, "neighborhoods"."boro_id" AS t1_r4 FROM "listings" LEFT OUTER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%')
I also tried and got the following error:
0> listings = Listing.joins(:neighborhood).where("headline like :keywords or neighborhood.name like :keywords", :keywords => "%#{keywords}%")
Listing Load (2.7ms) SELECT "listings".* FROM "listings" INNER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%')
ActiveRecord::StatementInvalid: PG::Error: ERROR: missing FROM-clause entry for table "neighborhood"
LINE 1: ..._id" WHERE (headline like '%prospect heights%' or neighborho...
^
: SELECT "listings".* FROM "listings" INNER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%')
Thoughts?
Thanks.
Edit 1:
A listing belongs_to neighborhood, and neighborhood has_many listings.
Edit 2:
Extracted one of the SQL statements and ran it via psql, this is the result:
SELECT "listings".* FROM "listings" INNER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id" WHERE (headline like '%prospect heights%' or neighborhood.name like '%prospect heights%');
ERROR: missing FROM-clause entry for table "neighborhood"
LINE 1: ..._id" WHERE (headline like '%prospect heights%' or neighborho...
Try the below:
listings = Listing.includes(:neighborhood).where("listings.headline like :keywords or neighborhoods.name like :keywords", :keywords => "%#{keywords}%")
I think it should call listings.headline instead of headline
SELECT "listings".* FROM "listings" INNER JOIN "neighborhoods"
ON "neighborhoods"."id" = "listings"."neighborhood_id"
WHERE (
headline like '%prospect heights%' #I mean here
or neighborhood.name like '%prospect heights%') #also here, should be neighborhoods.name
EDIT - do you know that there are some difference between SQLite and PostgreSQL? I faced a similar problem. I think you want ILIKE instead of LIKE:
listings = Listing.includes(:neighborhood).where("listings.headline ilike :keywords or neighborhoods.name ilike :keywords", :keywords => "%#{keywords}%")
Reformatted query:
SELECT "listings"."id" AS t0_r0,
"listings"."listing_category_id" AS t0_r1,
"listings"."listing_type_id" AS t0_r2,
"listings"."user_id" AS t0_r3,
"listings"."boro_id" AS t0_r4,
"listings"."neighborhood_id" AS t0_r5,
"listings"."building_id" AS t0_r6,
"listings"."term_id" AS t0_r7,
"listings"."headline" AS t0_r8,
"listings"."property_type_id" AS t0_r9,
"listings"."unit_num" AS t0_r10,
"listings"."common_charges" AS t0_r11,
"listings"."taxes" AS t0_r12,
"listings"."maintenance" AS t0_r13,
"listings"."num_bedrooms" AS t0_r14,
"listings"."num_bathrooms" AS t0_r15,
"listings"."square_footage" AS t0_r16,
"listings"."list_square_footage" AS t0_r17,
"listings"."description" AS t0_r18,
"listings"."condition_id" AS t0_r19,
"listings"."pet_policy_id" AS t0_r20,
"listings"."exposure_id" AS t0_r21,
"listings"."floor_plan" AS t0_r22,
"listings"."photo" AS t0_r23,
"listings"."security" AS t0_r24,
"listings"."coop_deductible" AS t0_r25,
"listings"."flip_tax" AS t0_r26,
"listings"."house_families" AS t0_r27,
"listings"."house_extensions" AS t0_r28,
"listings"."house_stories" AS t0_r29,
"listings"."house_units" AS t0_r30,
"listings"."created_at" AS t0_r31,
"listings"."updated_at" AS t0_r32,
"listings"."price" AS t0_r33,
"neighborhoods"."id" AS t1_r0,
"neighborhoods"."name" AS t1_r1,
"neighborhoods"."created_at" AS t1_r2,
"neighborhoods"."updated_at" AS t1_r3,
"neighborhoods"."boro_id" AS t1_r4
FROM "listings"
LEFT OUTER JOIN "neighborhoods" ON "neighborhoods"."id" = "listings"."neighborhood_id"
WHERE (headline LIKE '%prospect heights%'
OR neighborhood.name LIKE '%prospect heights%');
Problem seems obvious. You include table "neighborhoods", but later on are trying to use "neighborhood".
How to fix that in RoR - no idea, but perhaps it will be enough to point you in right direction.
The best way to diagnose this is to make sure your development and production environments match as closely as possible, and actually run PostgreSQL locally. Inevitably, you will encounter inconsistencies between SQLite and PostgreSQL that are difficult to debug, or will surprise you when you push your code.
If you're on a Mac, Heroku Postgres has a standalone application you can download and use with no configuration: http://postgresapp.com/
Related
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))"))
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)
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' })
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.
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.