Как я могу отфильтровать свой запрос, чтобы показать случаи, когда у текущего пользователя нет тегов? - PullRequest
1 голос
/ 29 августа 2011

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

enter image description here

Контроллер

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

@brand = current_user.brands.includes(:taggings).where( [ "taggings.id IS NULL OR taggings.tagger_id != ?", current_user.id ] ).order("RANDOM()").first

Схема (включая мою модель соединения для хорошей меры)

create_table "brand_users", :force => true do |t|
t.integer  "brand_id"
t.integer  "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end

create_table "taggings", :force => true do |t|
t.integer  "tag_id"
t.integer  "taggable_id"
t.string   "taggable_type"
t.integer  "tagger_id"
t.string   "tagger_type"
t.string   "context"
t.datetime "created_at"
end

add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"

create_table "tags", :force => true do |t|
t.string "name"
end

end

Ответы [ 2 ]

2 голосов
/ 30 августа 2011

Так что, если вы используете гем act-as-taggable-on и имеете следующие модели:

class User < ActiveRecord::Base
  acts_as_tagger
  has_many :brand_users
  has_many :brands, :through => :brand_users
end

Таким образом, у вас также есть таблицы в вашей схеме:

create_table "users", :force => true  do |t|
  t.string "name"
end

create_table "brands", :force => true  do |t|
  t.string "name"
end

Тогда следующий SQL-запрос, как мы надеемся, должен делать то, что вы хотите (?):

SELECT brands.*
FROM brands
WHERE brands.id NOT IN (
    SELECT brands.id
    FROM brands
    INNER JOIN brand_users ON brand_users.brand_id = brands.id
    INNER JOIN taggings ON (taggings.tagger_id = brand_users.user_id AND taggings.tagger_type = 'User')
    WHERE brand_users.user_id = 1 AND taggings.taggable_id = brand_users.brand_id
)

Чтобы перевести это в Rails ORM, я не могу приблизиться без жесткого кодирования всего суб-выбора SQLстрока, что-то вроде:

class Brand < ActiveRecord::Base
  has_many :brand_users
  has_many :users, :through => :brand_users

  scope :has_not_been_tagged_by_user, lambda {|user| where("brands.id NOT IN (SELECT brands.id
    FROM brands
    INNER JOIN brand_users ON brand_users.brand_id = brands.id
    INNER JOIN taggings ON (taggings.tagger_id = brand_users.user_id AND taggings.tagger_type = 'User')
    WHERE brand_users.user_id = ? AND taggings.taggable_id = brand_users.brand_id)", user.id) }

end

(я знаю, что вы можете сделать это, а затем использовать ruby's .map (&: id) .join (',') , но если этоя думаю, что вы теряете много производительности, вынимая это из базы данных, конвертируя его в строку целых чисел и возвращая его обратно (насколько я понимаю).)

Затем в вашем контроллере Я думаю, что вы бы сделали что-то вроде:

@brand = current_user.brands.has_not_been_tagged_by_user(current_user)

В качестве отступления, я думаю, что на самом деле это затем выполнит SQL, как показано ниже (это правильно?):

SELECT brands.*
FROM users
INNER JOIN brand_users ON brand_users.user_id = users.id
INNER JOIN brands ON brands.id = brand_users.brand_id 
WHERE brands.id NOT IN (
    SELECT brands.id
    FROM brands
    INNER JOIN brand_users ON brand_users.brand_id = brands.id
    INNER JOIN taggings ON (taggings.tagger_id = brand_users.user_id AND taggings.tagger_type = 'User')
    WHERE brand_users.user_id = 1 AND taggings.taggable_id = brand_users.brand_id
) AND users.id = 1
0 голосов
/ 29 августа 2011

Насколько я знаю, нет SELECT * FROM x WHERE * IS NULL, поэтому процедура, вероятно, будет единственным способом. Просто создайте процедуру в своей БД и вызовите ее из кода, вам не нужно помещать SQL в код.

Пример такой процедуры можно увидеть здесь .

...