【问题标题】:How can I filter my query to show instances where current user has no tags?如何过滤我的查询以显示当前用户没有标签的实例?
【发布时间】:2011-08-29 18:16:00
【问题描述】:

我试图仅显示当前用户未标记的品牌实例,即使其他用户已经标记了同一品牌。比如:

控制器

这是我的控制器代码,尽管它应该可以工作,但它当前会返回所有品牌实例。

@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

【问题讨论】:

  • 谢谢,虽然我不太愿意在控制器中使用纯 SQL。
  • 据我所知没有SELECT * FROM x WHERE * IS NULL,所以程序可能是唯一的方法。只需在您的数据库中创建一个过程并从代码中调用它,您不必将 SQL 放入代码中。
  • 是否愿意将其添加为答案?
  • 你看到这个答案了吗?:stackoverflow.com/questions/1314408/…

标签: sql ruby ruby-on-rails-3 tagging acts-as-taggable-on


【解决方案1】:

因此,如果您使用acts-as-taggable-on gem 并具有以下模型:

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​​ 的 .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

【讨论】:

    【解决方案2】:

    据我所知,没有SELECT * FROM x WHERE * IS NULL,所以程序可能是唯一的方法。只需在您的数据库中创建一个过程并从代码中调用它,您不必将 SQL 放入代码中。

    您可以查看此类过程的示例here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多