Как выдернуть id ассоциаций has_one?

class Post
  has_one :latest_comment, -> { order(created_at: :desc) }, class_name: 'Comment'
end

Я хочу сделать что-то вроде:

Post.joins(:latest_comment).pluck('latest_comment.id')

но это недопустимый синтаксис, и он не работает.

Post.joins(:latest_comment).pluck('comments.id')

Выше работает, но возвращает идентификаторы всех комментариев к сообщению, а не только последнего.

Этот запрос является обратным. Если вам нужны только идентификаторы комментариев, просто запустите запрос из модели комментариев и добавьте группу или DISTINCT ON столбец comments.post_id. Например, на Postgres вы можете сделать Comment.order(:post_id, created_at: :desc).pluck(Arel.sql('DISTINCT ON (post_id) id')). Я считаю, что в MySQL вы можете использовать что-то вроде Comment.group(:post_id).order(created_at: :desc).ids, так как он меньше заботится о таких вещах, как порядок и групповое предложение, соответствующее предложению select.

max 23.04.2022 08:18

Пожалуйста, добавьте ответ с Comment.order(:post_id, created_at: :desc).pluck(Arel.sql('DISTINCT ON (post_id) id')), чтобы я мог его принять.

Hubert Jakubiak 23.04.2022 10:33
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
0
2
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не смог найти хороший существующий ответ. это самый близкий.

Проблема не совсем pluck. Проблема в том, что присоединиться к has_one — это то же самое, что присоединиться к самому столу. В данном случае это эквивалентно Post.joins(:comments).

Не вдаваясь в пользовательский SQL или Arel, простое решение — просто загрузить записи:

Post.includes(:latest_comment)    # eager-loads latest_comments in subquery
    .find_each                    # optional: batches data to save memory
    .filter_map(&:latest_comment) # map to `latest_comment` and remove nils
    .map(&:id)                    # finally, get the ids

Это не так эффективно, как pluck, поскольку он извлекает каждый столбец в обеих таблицах и создает экземпляры всех экземпляров модели.

Вы можете немного повысить производительность, добавив select(:id) после Post, чтобы он извлекал только id для сообщений.

Ответ принят как подходящий

ActiveRecord::Assocations — это очень дырявая абстракция для соединений SQL, поэтому ваша ассоциация has_one :latest_comment на самом деле не будет возвращать ни одной строки в таблице соединений для каждой записи, если только вы не вызываете ее для экземпляра Post.

Вместо этого, когда вы бежите Post.joins(:latest_comment).pluck('comments.id'), вы получаете:

SELECT "comments"."id" 
FROM "posts" 
INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"

ActiveRecord на самом деле недостаточно умен, чтобы знать, что вы хотите получить уникальные значения из таблицы комментариев, и на самом деле он просто ведет себя как ассоциация has_many. В свою защиту это на самом деле не то, что даже реально сделать полиглотом.

Вместо этого вы можете выбрать строки из таблицы комментариев и получить разные значения:

Comment.order(:post_id, created_at: :desc)
       .pluck(Arel.sql('DISTINCT ON (post_id) id'))

DISTINCT ON специфичен для Postgres. Точный подход здесь будет варьироваться в зависимости от СУБД, и есть много других альтернатив, таких как боковые соединения, оконные функции и т. д., в зависимости от ваших требований к производительности.

Следует отметить, что это едва ли не самый производительный вариант, но, вероятно, он достаточно хорош. Если это становится узким местом, используйте боковое соединение или добавьте столбец posts.latest_comment_id.

max 23.04.2022 11:38

Другие вопросы по теме