оптимизация запроса на выборку по has_many: с помощью ассоциации атрибутов - PullRequest
0 голосов
/ 03 апреля 2012

Я хочу найти все сообщения, помеченные тегами, которые передаются в массиве params.В сообщении есть много тегов через ассоциацию.

В настоящее время мой код выглядит следующим образом:

if params.has_key?(:tags)
  params[:tags].each do |tag|
    @tags = Array.new if @tags.nil?
    @tag = Tag.find_by_content(tag)
    @tags << @tag if @tag
  end
  @allposts = Post.followed_by(@user).select { |p| p.tags.size != 0 && (p.tags & @tags).size == p.tags.size }
else
  @allposts = Post.followed_by(@user)
end

Что я в основном делаю, так это нахожу фактические модели тегов в соответствии с массивом params и помещаю их вмассив, затем я запускаю запрос на выборку для всех постов, ищущих те же массивы тегов.

Есть ли лучший и более чистый способ сделать это?

1 Ответ

1 голос
/ 03 апреля 2012

Вы можете свернуть запрос Tag.find в один запрос к БД и добавить соответствующее условие where для ограничения количества возвращаемых сообщений:

finder = Post.followed_by(@user)
if params.has_key?(:tags)
  @tags = Tag.where(:content => params[:tags])
  finder = finder.with_tags(@tags)
end

@allposts = finder.all

в приложении / models / post.rb

scope :with_tags, lambda { |tags| joins(:tags).group('posts.id').where(:tags => { :id => tags.map { |t| t.id } } ).having("COUNT(*) = ?", tags.length) }

ОБНОВЛЕНИЕ

Вот что делает область with_tags:

  1. joins(:tags) Сначала мы присоединяемся к тегамстол к столу сообщений.Rails будет делать с внутренним объединением, когда вы используете синтаксис символа
  2. where(:tags => { :id => tags.map { |t| t.id } } ) Мы хотим отфильтровать теги, чтобы найти только те теги, которые были предоставлены.Поскольку мы предоставляем список объектов тегов, мы используем map для генерации массива идентификаторов.Этот массив затем используется в предложении where для создания запроса WHERE field IN (list) - хеш в синтаксисе хеша используется для обозначения таблицы, а затем столбца в таблице.
  3. group('posts.id') Итак, теперь у нас есть список сообщений с необходимыми тегами, однако, если имеется несколько тегов, мы будем иметь сообщения в списке несколько раз (по одному для каждого соответствующего тега), поэтому мы группируем поposts.id, чтобы у нас было только 1 строка, возвращаемая для каждого сообщения (также необходимо, чтобы мы могли выполнить подсчет на шаге 4)
  4. having("count(*) = ?", tags.length) Это последняя часть головоломки.Теперь, когда мы сгруппированы по посту, мы можем подсчитать количество подходящих тегов, связанных с этим постом.До тех пор, пока повторяющиеся теги не допускаются, тогда, если количество совпавших тегов (count(*)) совпадает с количеством тегов, с которыми мы искали (tags.length), тогда мы можем быть уверены, что запись имеет все теги, которые мы

Вы можете найти гораздо больше информации о различных методах запросов, доступных для моделей, прочитав Руководство по интерфейсу запросов Active Record

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...