如何过滤查询以显示当前用户没有标签的实例?

发布于 2024-12-01 17:41:54 字数 1068 浏览 0 评论 0原文

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

在此处输入图像描述

控制器

这是我的控制器代码,尽管它应该是工作正常,它目前返回所有品牌实例。

@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

I'm trying to show only brand instances which the current user has not tagged, even if other users have tagged the same brand already. Something like:

enter image description here

Controller

This is my controller code and even though it should be working, it currently returns all brand instances.

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

Schema (including my join model for good measure)

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

弱骨蛰伏 2024-12-08 17:41:54

因此,如果您使用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

So if you're using the acts-as-taggable-on gem and have the following models:

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

So you also have the tables in your schema like:

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

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

Then the following SQL query should hopefully do what you want (?):

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
)

To translate this into Rails ORM, I can't get any closer without hard coding the whole sub-select SQL string, something like:

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

(I know you could do this and then use ruby's .map(&:id).join(',') but if this is a large app I think you loose a lot of performance by taking this out of the database, converting it into a string of integers and feeding it back in (as I understand it).)

Then in your controller I think you'd do something like:

@brand = current_user.brands.has_not_been_tagged_by_user(current_user)

As an aside, I think this would actually then execute an SQL like below (is that right?):

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
失眠症患者 2024-12-08 17:41:54

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

您可以在此处查看此类过程的示例。

As far as I know there is no SELECT * FROM x WHERE * IS NULL so a procedure would probably be the only way. Just create a procedure in your DB and call it from the code, you don't have to put the SQL in the code.

You can see an example of such a procedure here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文