Rails 4 - Eager loading with multiple ON conditions - ruby-on-rails

I have an Article and each Article has exactly one User and either zero or one ArticleVote.
My code is:
#articles = Article
.eager_load(:user)
.eager_load(:article_vote)
.where(article_votes: { user_id: session[:user_id]})
.order(created_at: :desc)
Which produces the following SQL:
SELECT
`articles`.`article_id` AS t0_r0,
`articles`.`user_id` AS t0_r1,
`users`.`user_id` AS t1_r0,
`article_votes`.`article_vote_id` AS t2_r0,
`article_votes`.`article_id` AS t2_r1,
`article_votes`.`user_id` AS t2_r2
FROM `articles`
LEFT OUTER JOIN `users` ON `users`.`user_id` = `articles`.`user_id`
LEFT OUTER JOIN `article_votes` ON `article_votes`.`article_id` = `articles`.`article_id`
WHERE `article_votes`.`user_id` = 1
ORDER BY `articles`.`created_at` DESC
The problem with this query is if no vote exists (which sometimes it won't), then no records are returned. What I really need is this (note the where is gone and the condition is moved to the left join):
SELECT
`articles`.`article_id` AS t0_r0,
`articles`.`user_id` AS t0_r1,
`users`.`user_id` AS t1_r0,
`article_votes`.`article_vote_id` AS t2_r0,
`article_votes`.`article_id` AS t2_r1,
`article_votes`.`user_id` AS t2_r2
FROM `articles`
LEFT OUTER JOIN `users` ON `users`.`user_id` = `articles`.`user_id`
LEFT OUTER JOIN `article_votes` ON `article_votes`.`article_id` = `articles`.`article_id` and `article_votes`.`user_id` = 1
ORDER BY `articles`.`created_at` DESC
I saw in the documentation I can do something like:
has_one :article_vote, -> { where(user_id: 1) }
Which will put it in the left join vs the where, but this doesn't let me specify the user_id at query time, which makes it not viable.
Can this be done?

Related

Eager loading model instances using single SQL statement with Mobility table backend

Using Rails 7 and Mobility 1.2.9:
# config/initializers/mobility.rb
Mobility.configure do |config|
config.plugins do
backend :table
active_record
reader
writer
backend_reader
query
cache
dirty
presence
end
end
# app/models/content.rb
class Content < ApplicationRecord
extend Mobility
translates :slug, :title
end
How to find contents by slug, instantiate them into Content objects and query their translated attributes using one single SQL statement (for an unchanging locale)? The examples below with and without eager_loading run 2 SQL statements.
> content = Content.i18n.find_by(slug: "foo")
Content Load (0.7ms) SELECT "contents".* FROM "contents" INNER JOIN "content_translations" "content_translations_en" ON "content_translations_en"."content_id" = "contents"."id" AND "content_translations_en"."locale" = 'en' WHERE "content_translations_en"."slug" = 'foo' LIMIT $1 [["LIMIT", 1]]
> content.title
Content::Translation Load (0.3ms) SELECT "content_translations".* FROM "content_translations" WHERE "content_translations"."content_id" = $1 [["content_id", "..."]]
> content = Content.i18n.eager_load(:translations).find_by(slug: "foo")
SQL (0.6ms) SELECT DISTINCT "contents"."id" FROM "contents" LEFT OUTER JOIN "content_translations" ON "content_translations"."content_id" = "contents"."id" INNER JOIN "content_translations" "content_translations_en" ON "content_translations_en"."content_id" = "contents"."id" AND "content_translations_en"."locale" = 'en' WHERE "content_translations_en"."slug" = 'blog-1-en' LIMIT $1 [["LIMIT", 1]]
SQL (0.8ms) SELECT "contents"."id" AS t0_r0, "contents"."created_at" AS t0_r1, "contents"."updated_at" AS t0_r2, ..., "content_translations"."id" AS t1_r0, "content_translations"."locale" AS t1_r1, "content_translations"."created_at" AS t1_r2, "content_translations"."updated_at" AS t1_r3, "content_translations"."slug" AS t1_r4, "content_translations"."title" AS t1_r5, "content_translations"."content_id" AS t1_r6 FROM "contents" LEFT OUTER JOIN "content_translations" ON "content_translations"."content_id" = "contents"."id" INNER JOIN "content_translations" "content_translations_en" ON "content_translations_en"."content_id" = "contents"."id" AND "content_translations_en"."locale" = 'en' WHERE "content_translations_en"."slug" = 'blog-1-en' AND "contents"."id" = $1 [["id", "..."]]
> content.title
# no DB statement, but there were 2 statements at instantiation
NB: I do not want to pluck attributes but instead to create model instances.

Rails is breaking SQL query when modifying order

Context
We have a Rails app that is retrieving conversations with the following raw SQL query:
SELECT sub.*,
profiles.status AS interlocutor_status
FROM (
SELECT DISTINCT ON (conversations.id) conversations.id,
conversation_preferences.unread_counter,
left(messages.content, 50) AS last_message,
posts.id AS post_id,
messages.created_at AS last_activity_on,
categories.root_name AS site_name,
conversation_preferences.state,
COALESCE(NULLIF(post_owner, 1234567), NULLIF(post_applicant, 1234567)) AS interlocutor_id
FROM "conversations"
LEFT OUTER JOIN "conversation_preferences" ON "conversation_preferences"."conversation_id" = "conversations"."id"
LEFT OUTER JOIN "posts" ON "posts"."id" = "conversations"."post_id"
LEFT OUTER JOIN "categories" ON "categories"."id" = "posts"."category_id"
LEFT OUTER JOIN "messages" ON "messages"."conversation_id" = "conversations"."id"
WHERE (post_applicant = 1234567 OR post_owner = 1234567)
AND "conversation_preferences"."user_id" = 1234567
ORDER BY "conversations"."id" ASC, messages.created_at DESC
) sub
LEFT OUTER JOIN users ON interlocutor_id = users.id
LEFT OUTER JOIN profiles ON interlocutor_id = profiles.user_id
WHERE ("profiles"."status" != 'pending')
AND (last_activity_on >= '2021-01-19 04:40:22.881985')
AND (state = 'active')
ORDER BY profiles.status, sub.unread_counter DESC, sub.last_activity_on DESC
LIMIT 25
We generate this query using the following ActiveRecord code:
def fetch
distinct = Conversation.left_outer_joins(:preferences)
.left_outer_joins(post: :category)
.left_outer_joins(:messages)
.where('post_applicant = :id OR post_owner = :id', id: current_user.id)
.where(conversation_preferences: { user_id: current_user.id })
.select(
<<-SQL.squish
DISTINCT ON (conversations.id) conversations.id,
conversation_preferences.unread_counter,
left(messages.content, 50) AS last_message,
posts.id AS post_id,
messages.created_at AS last_activity_on,
categories.root_name AS site_name,
conversation_preferences.state,
COALESCE(NULLIF(post_owner, #{current_user.id}), NULLIF(post_applicant, #{current_user.id})) AS interlocutor_id
SQL
)
.order(:id, 'messages.created_at DESC')
Conversation.includes(post: :category)
.from(distinct, :sub)
.select('sub.*, profiles.status AS interlocutor_status')
.joins('LEFT OUTER JOIN users ON interlocutor_id = users.id')
.joins('LEFT OUTER JOIN profiles ON interlocutor_id = profiles.user_id')
.where.not('profiles.status' => :pending)
.order('profiles.status, sub.unread_counter DESC, sub.last_activity_on DESC')
end
Problem
We want to stop ordering by profiles.status. To do this, we naturally removed it from the last order statement:
order('sub.unread_counter DESC, sub.last_activity_on DESC')
That's the problem. Doing that is entirely breaking the generated query, that generate an error which is irrelevant here because we don't want the modified query (note how it is different from the 1st one):
SELECT sub.*,
profiles.status AS interlocutor_status,
"conversations"."id" AS t0_r0,
"conversations"."post_id" AS t0_r1,
"conversations"."post_owner" AS t0_r2,
"conversations"."post_applicant" AS t0_r3,
"conversations"."created_at" AS t0_r4,
"conversations"."updated_at" AS t0_r5,
"posts"."id" AS t1_r0,
"posts"."title" AS t1_r1,
"posts"."description" AS t1_r2,
"categories"."id" AS t2_r0,
"categories"."name" AS t2_r1
FROM (
SELECT DISTINCT ON (conversations.id) conversations.id,
conversation_preferences.unread_counter,
left(messages.content, 50) AS last_message,
posts.id AS post_id,
messages.created_at AS last_activity_on,
categories.root_name AS site_name,
conversation_preferences.state,
COALESCE(NULLIF(post_owner, 1234567), NULLIF(post_applicant, 1234567)) AS interlocutor_id
FROM "conversations"
LEFT OUTER JOIN "conversation_preferences" ON "conversation_preferences"."conversation_id" = "conversations"."id"
LEFT OUTER JOIN "posts" ON "posts"."id" = "conversations"."post_id"
LEFT OUTER JOIN "categories" ON "categories"."id" = "posts"."category_id"
LEFT OUTER JOIN "messages" ON "messages"."conversation_id" = "conversations"."id"
WHERE (post_applicant = 1234567 OR post_owner = 1234567)
AND "conversation_preferences"."user_id" = 1234567
ORDER BY "conversations"."id" ASC, messages.created_at DESC
) sub
LEFT OUTER JOIN "posts" ON "posts"."id" = "conversations"."post_id"
LEFT OUTER JOIN "categories" ON "categories"."id" = "posts"."category_id"
LEFT OUTER JOIN users ON interlocutor_id = users.id
LEFT OUTER JOIN profiles ON interlocutor_id = profiles.user_id
WHERE ("profiles"."status" != 'pending')
AND (last_activity_on >= '2021-01-19 05:04:06.084499')
AND (state = 'active')
ORDER BY sub.unread_counter DESC, sub.last_activity_on DESC
LIMIT 25
I know without a bit of context it'll be hard to help us but if someone knows why ActiveRecord is changing the query after trying to just remove profiles.status from the order statement, that would be awesome. Thanks in advance
NOTE: modifying the 1st raw SQL directly (from our postgres client) does works. The issue is not the first query, but maybe how ActiveRecord is handling it
Finally found a way to make it work using preload instead of includes. We wanted to avoid having seperate queries to load posts and categories but since performance is not affected by it, we don't mind it.
Here is how it look like:
Conversation.preload(post: :category)
.from(distinct, :sub)
.select('sub.*, profiles.status AS interlocutor_status')
.joins('LEFT OUTER JOIN users ON interlocutor_id = users.id')
.joins('LEFT OUTER JOIN profiles ON interlocutor_id = profiles.user_id')
.where.not('profiles.status' => :pending)
.order('sub.unread_counter DESC, sub.last_activity_on DESC')
Which generates 3 queries:
-- Query 1
SELECT sub.*, profiles.status AS interlocutor_status
FROM (
SELECT DISTINCT
....
-- Query 2
SELECT "posts".* FROM "posts" WHERE "posts"."id" IN (............)
-- Query 3
SELECT "categories".* FROM "categories" WHERE "categories"."id" IN (..........)
Thanks to everyone for the help in comments (max and Sebastian Palma)

How to reduce eager_load query overhead (ActiveRecord)?

I have the following model:
# address.rb
class Address < ActiveRecord::Base
has_many :orders_for_cleaning, class_name: 'Order',
foreign_key: :cleaning_address_id
has_many :orders_for_billing, class_name: 'Order',
foreign_key: :billing_address_id
has_many :purchases_for_shipping, class_name: 'Purchase',
foreign_key: :shipping_address_id
has_many :purchases_for_billing, class_name: 'Purchase',
foreign_key: :billing_address_id
end
and I want to fetch all addresses of a certain Customer through his orders a purchases using eager_load:
# customer.rb
def addresses
Address.eager_load(:orders_for_cleaning,
:orders_for_billing,
:purchases_for_shipping,
:purchases_for_billing)
.where('orders.customer_id = ?
OR orders_for_billings_addresses.customer_id = ?
OR purchases.customer_id = ?
OR purchases_for_billings_addresses.customer_id = ?',
id, id, id, id)
end
This request generates the following query:
SELECT DISTINCT "addresses"."id" AS t0_r0,
"addresses"."latitude" AS t0_r1,
"addresses"."longitude" AS t0_r2,
"addresses"."house" AS t0_r3,
"addresses"."street" AS t0_r4,
"addresses"."city" AS t0_r5,
"addresses"."zip" AS t0_r6,
"addresses"."state" AS t0_r7,
"addresses"."country" AS t0_r8,
"addresses"."created_at" AS t0_r9,
"addresses"."updated_at" AS t0_r10,
"addresses"."street2" AS t0_r11,
"addresses"."custom_latitude" AS t0_r12,
"addresses"."custom_longitude" AS t0_r13,
"addresses"."name" AS t0_r14,
"addresses"."company" AS t0_r15,
"addresses"."archived" AS t0_r16,
"orders"."id" AS t1_r0,
"orders"."customer_id" AS t1_r1,
"orders"."cleaning_address_id" AS t1_r2,
"orders"."billing_address_id" AS t1_r3,
"orders"."start_at" AS t1_r4,
"orders"."finish_at" AS t1_r5,
"orders"."planned_at" AS t1_r6,
"orders"."started_at" AS t1_r7,
"orders"."finished_at" AS t1_r8,
"orders"."payed_price" AS t1_r9,
"orders"."status" AS t1_r10,
"orders"."pin" AS t1_r11,
"orders"."comment" AS t1_r12,
"orders"."created_at" AS t1_r13,
"orders"."updated_at" AS t1_r14,
"orders"."name" AS t1_r15,
"orders"."company" AS t1_r16,
"orders"."phone" AS t1_r17,
"orders"."coupon_code" AS t1_r18,
"orders"."coupon_discount" AS t1_r19,
"orders"."location_id" AS t1_r20,
"orders"."location_discount" AS t1_r21,
"orders"."job_count" AS t1_r22,
"orders"."bonus" AS t1_r23,
"orders"."organization_id" AS t1_r24,
"orders"."bill_id" AS t1_r25,
"orders"."cleaning_count_discount" AS t1_r26,
"orders"."created_by" AS t1_r27,
"orders"."price_group_id" AS t1_r28,
"orders"."travel_charge" AS t1_r29,
"orders"."departed_at" AS t1_r30,
"orders"."arrived_at" AS t1_r31,
"orders"."request_feedback" AS t1_r32,
"orders"."feedback_link" AS t1_r33,
"orders"."feedback_requested_at" AS t1_r34,
"orders"."rating" AS t1_r35,
"orders"."feedback" AS t1_r36,
"orders"."profit_center_code" AS t1_r37,
"orders"."feedback_received_at" AS t1_r38,
"orders"."reorder_bonus" AS t1_r39,
"orders"."paid_to" AS t1_r40,
"orders"."cleaner_id" AS t1_r41,
"orders"."station_id" AS t1_r42,
"orders"."cleaner_comment" AS t1_r43,
"orders_for_billings_addresses"."id" AS t2_r0,
"orders_for_billings_addresses"."customer_id" AS t2_r1,
"orders_for_billings_addresses"."cleaning_address_id" AS t2_r2,
"orders_for_billings_addresses"."billing_address_id" AS t2_r3,
"orders_for_billings_addresses"."start_at" AS t2_r4,
"orders_for_billings_addresses"."finish_at" AS t2_r5,
"orders_for_billings_addresses"."planned_at" AS t2_r6,
"orders_for_billings_addresses"."started_at" AS t2_r7,
"orders_for_billings_addresses"."finished_at" AS t2_r8,
"orders_for_billings_addresses"."payed_price" AS t2_r9,
"orders_for_billings_addresses"."status" AS t2_r10,
"orders_for_billings_addresses"."pin" AS t2_r11,
"orders_for_billings_addresses"."comment" AS t2_r12,
"orders_for_billings_addresses"."created_at" AS t2_r13,
"orders_for_billings_addresses"."updated_at" AS t2_r14,
"orders_for_billings_addresses"."name" AS t2_r15,
"orders_for_billings_addresses"."company" AS t2_r16,
"orders_for_billings_addresses"."phone" AS t2_r17,
"orders_for_billings_addresses"."coupon_code" AS t2_r18,
"orders_for_billings_addresses"."coupon_discount" AS t2_r19,
"orders_for_billings_addresses"."location_id" AS t2_r20,
"orders_for_billings_addresses"."location_discount" AS t2_r21,
"orders_for_billings_addresses"."job_count" AS t2_r22,
"orders_for_billings_addresses"."bonus" AS t2_r23,
"orders_for_billings_addresses"."organization_id" AS t2_r24,
"orders_for_billings_addresses"."bill_id" AS t2_r25,
"orders_for_billings_addresses"."cleaning_count_discount" AS t2_r26,
"orders_for_billings_addresses"."created_by" AS t2_r27,
"orders_for_billings_addresses"."price_group_id" AS t2_r28,
"orders_for_billings_addresses"."travel_charge" AS t2_r29,
"orders_for_billings_addresses"."departed_at" AS t2_r30,
"orders_for_billings_addresses"."arrived_at" AS t2_r31,
"orders_for_billings_addresses"."request_feedback" AS t2_r32,
"orders_for_billings_addresses"."feedback_link" AS t2_r33,
"orders_for_billings_addresses"."feedback_requested_at" AS t2_r34,
"orders_for_billings_addresses"."rating" AS t2_r35,
"orders_for_billings_addresses"."feedback" AS t2_r36,
"orders_for_billings_addresses"."profit_center_code" AS t2_r37,
"orders_for_billings_addresses"."feedback_received_at" AS t2_r38,
"orders_for_billings_addresses"."reorder_bonus" AS t2_r39,
"orders_for_billings_addresses"."paid_to" AS t2_r40,
"orders_for_billings_addresses"."cleaner_id" AS t2_r41,
"orders_for_billings_addresses"."station_id" AS t2_r42,
"orders_for_billings_addresses"."cleaner_comment" AS t2_r43,
"purchases"."id" AS t3_r0,
"purchases"."product_price_group_id" AS t3_r1,
"purchases"."customer_id" AS t3_r2,
"purchases"."shipping_address_id" AS t3_r3,
"purchases"."billing_address_id" AS t3_r4,
"purchases"."created_by" AS t3_r5,
"purchases"."token" AS t3_r6,
"purchases"."shipping" AS t3_r7,
"purchases"."coupon_code" AS t3_r8,
"purchases"."coupon_discount" AS t3_r9,
"purchases"."comment" AS t3_r10,
"purchases"."status" AS t3_r11,
"purchases"."purchased_at" AS t3_r12,
"purchases"."created_at" AS t3_r13,
"purchases"."updated_at" AS t3_r14,
"purchases"."purchase_invoice_id" AS t3_r15,
"purchases"."payment" AS t3_r16,
"purchases"."pickup" AS t3_r17,
"purchases"."tracking_number" AS t3_r18,
"purchases"."paper_invoice_sent_at" AS t3_r19,
"purchases_for_billings_addresses"."id" AS t4_r0,
"purchases_for_billings_addresses"."product_price_group_id" AS t4_r1,
"purchases_for_billings_addresses"."customer_id" AS t4_r2,
"purchases_for_billings_addresses"."shipping_address_id" AS t4_r3,
"purchases_for_billings_addresses"."billing_address_id" AS t4_r4,
"purchases_for_billings_addresses"."created_by" AS t4_r5,
"purchases_for_billings_addresses"."token" AS t4_r6,
"purchases_for_billings_addresses"."shipping" AS t4_r7,
"purchases_for_billings_addresses"."coupon_code" AS t4_r8,
"purchases_for_billings_addresses"."coupon_discount" AS t4_r9,
"purchases_for_billings_addresses"."comment" AS t4_r10,
"purchases_for_billings_addresses"."status" AS t4_r11,
"purchases_for_billings_addresses"."purchased_at" AS t4_r12,
"purchases_for_billings_addresses"."created_at" AS t4_r13,
"purchases_for_billings_addresses"."updated_at" AS t4_r14,
"purchases_for_billings_addresses"."purchase_invoice_id" AS t4_r15,
"purchases_for_billings_addresses"."payment" AS t4_r16,
"purchases_for_billings_addresses"."pickup" AS t4_r17,
"purchases_for_billings_addresses"."tracking_number" AS t4_r18,
"purchases_for_billings_addresses"."paper_invoice_sent_at" AS t4_r19
FROM "addresses"
LEFT OUTER JOIN "orders"
ON "orders"."cleaning_address_id" = "addresses"."id"
LEFT OUTER JOIN "orders" "orders_for_billings_addresses"
ON "orders_for_billings_addresses"."billing_address_id" = "addresses"."id"
LEFT OUTER JOIN "purchases"
ON "purchases"."shipping_address_id" = "addresses"."id"
LEFT OUTER JOIN "purchases" "purchases_for_billings_addresses"
ON "purchases_for_billings_addresses"."billing_address_id" = "addresses"."id"
WHERE (orders.customer_id = 3282 OR orders_for_billings_addresses.customer_id = 3282
OR purchases.customer_id = 3282 OR purchases_for_billings_addresses.customer_id = 3282)
this query takes about 100ms on my dataset.
But I don't need to load all join model columns just their foreign keys. If I execute stripped SQL query:
SELECT addresses.*,
orders.cleaning_address_id,
orders_for_billings_addresses.billing_address_id,
purchases.shipping_address_id,
purchases_for_billings_addresses.billing_address_id
FROM addresses
LEFT OUTER JOIN orders
ON orders.cleaning_address_id = addresses.id
LEFT OUTER JOIN orders orders_for_billings_addresses
ON orders_for_billings_addresses.billing_address_id = addresses.id
LEFT OUTER JOIN purchases
ON purchases.shipping_address_id = addresses.id
LEFT OUTER JOIN purchases purchases_for_billings_addresses
ON purchases_for_billings_addresses.billing_address_id = addresses.id
WHERE (orders.customer_id = 3282
OR orders_for_billings_addresses.customer_id = 3282
OR purchases.customer_id = 3282
OR purchases_for_billings_addresses.customer_id = 3282)
It returns the same results in only 50ms.
The question: is there a way to enforce ActiveRecord to select only necessary fields with eager_load? Adding select('addresses.*, <fk_fields...>) doesn't help.
eager_load is not what you are looking for based on your description. eager_load will load all the associations immediately. why not try using includes, references and select instead e.g.
def addresses
Address.includes(:orders_for_cleaning,
:orders_for_billing,
:purchases_for_shipping,
:purchases_for_billing).
references(:orders_for_cleaning,
:orders_for_billing,
:purchases_for_shipping,
:purchases_for_billing).
where("orders.customer_id = :id
OR orders_for_billings_addresses.customer_id = :id
OR purchases.customer_id = :id
OR purchases_for_billings_addresses.customer_id = :id",
{id: id}).
select("addresses.*,
orders.cleaning_address_id as cleaning_address_id,
orders_for_billings_addresses.billing_address_id as order_billing_address_id,
purchases.shipping_address_id as purchases_shipping_address_id,
purchases_for_billings_addresses.billing_address_id as purchases_billing_address_id")
end
I believe this will produce the query you are looking for and add methods for retrieval of cleaning_address_id,order_billing_address_id,purchases_shipping_address_id, and purchases_billing_address_id.
I have not tested this so I hope it helps if it does not let me know and I will remove it.
Update
Since the above code is still eager loading according to the OP. (of which I am not sure why and since the relationships are complex debugging personally seems difficult) I am suggesting building the query long hand.
relationship_joins = <<-SQL
LEFT OUTER JOIN orders
ON orders.cleaning_address_id = addresses.id
LEFT OUTER JOIN orders orders_for_billings_addresses
ON orders_for_billings_addresses.billing_address_id = addresses.id
LEFT OUTER JOIN purchases
ON purchases.shipping_address_id = addresses.id
LEFT OUTER JOIN purchases purchases_for_billings_addresses
ON purchases_for_billings_addresses.billing_address_id = addresses.id
SQL
Address.joins(relationship_joins).
where("orders.customer_id = :id
OR orders_for_billings_addresses.customer_id = :id
OR purchases.customer_id = :id
OR purchases_for_billings_addresses.customer_id = :id",
{id: id}).
select("addresses.*,
orders.cleaning_address_id as cleaning_address_id,
orders_for_billings_addresses.billing_address_id as order_billing_address_id,
purchases.shipping_address_id as purchases_shipping_address_id,
purchases_for_billings_addresses.billing_address_id as purchases_billing_address_id")
That should create the query you are requesting. It is not as pretty but sometimes you have to sacrifice beauty for functionality.

How to access joined record from Rails LEFT OUTER JOIN

Howdy I found plenty of examples on how to use LEFT OUTER JOIN, but I can't seem to find a way to access what I have joined. Here is what I mean:
List.featured.
joins(
"LEFT OUTER JOIN follows ON (
follows.followable_id = lists.id AND
follows.followable_type = 'List' AND
follows.user_id = #{current_user.id})"
).map { |list|
list.follows # <-- returns all follows, not only the ones from current_user
...
In the example I get the follows (it seems) with the join, but then how can I access them? The follows relation will just give me all follows for that list it seems.
Or maybe my mind is fogged :) Thanks!
To eager load follows, you can call includes():
List.featured.includes(:follows).where(:follows => { :user_id => current_user.id })
It generates queries like this:
SELECT
"lists"."id" AS t0_r0,
"lists"."is_featured" AS t0_r1,
"lists"."created_at" AS t0_r2,
"lists"."updated_at" AS t0_r3,
"follows"."id" AS t1_r0,
"follows"."followable_id" AS t1_r1,
"follows"."followable_type" AS t1_r2,
"follows"."user_id" AS t1_r3,
"follows"."created_at" AS t1_r4,
"follows"."updated_at" AS t1_r5
FROM
"lists"
LEFT OUTER JOIN
"follows"
ON
"follows"."followable_id" = "lists"."id" AND
"follows"."followable_type" = 'List'
WHERE
"lists"."is_featured" = 't' AND
"follows"."user_id" = 1

Squeel query comparing 2 variables

Using squeel, I'm able to generate the following request
Payment.joins(:account => :preference).where({:account => {:preference => {:currency => :currency } } }).to_sql
=> SELECT "payments".* FROM "payments" INNER JOIN "accounts" ON "accounts"."id" = "payments"."account_id" INNER JOIN "preferences" ON "preferences"."account_id" = "accounts"."id" WHERE "preferences"."currency" = "preferences"."currency"
However, how to get? (CHANGE IN CAPITAL)
=> SELECT "payments".* FROM "payments" INNER JOIN "accounts" ON "accounts"."id" = "payments"."account_id" INNER JOIN "preferences" ON "preferences"."account_id" = "accounts"."id" WHERE "preferences"."currency" = "PAYMENTS"."currency"
If the solution work fine with meta_where as well that's even better ;-)
Thanks to IRC guys hron84, injekt and SIGe
Here's the solution :
Payment.joins(:account => :preference).where{account.preference.currency == ~currency }.to_sql

Resources