如何动态链接 rails 中的 where 子句?
How do I dynamically chain where clauses in rails?
我有一个视频模型和一个标签模型。标签 HABTM 视频和视频 HABTM 标签。
class Tag < ApplicationRecord
has_and_belongs_to_many :videos
scope :with_tag, -> (id) { where(id: id) }
end
class Video < ApplicationRecord
has_and_belongs_to_many :tags
end
我想知道如何创建一个查询,其中来自用户的未知数量的标签。所以我可以说,"show me all the videos that are tagged with (123 AND 124 AND 125)"。最终目标是我不知道用户要传入多少标签,所以我需要知道如何创建范围链并最终能够将整个内容传递到 ransack 中以进行文本搜索结果视频的某些字段。我怎样才能做到这一点?
更新:我想知道如何使用 Rails 和 ActiveRecord 执行此操作。我知道如何使用 SQL 完成此操作。但是只用 SQL 完成它不会让我将结果关系传递给 ransack。
我创建了一些相关的 tables 来编写这个答案,并根据 table 拥有的数据测试查询。所以,我在这里获取帖子,它们都有标签 1 和 2。
输入SQL:
rails-test_development=# select id from posts;
id
----
1
2
3
(3 rows)
rails-test_development=# select id from tags;
id
----
2
3
4
5
6
(5 rows)
rails-test_development=# select post_id, tag_id from posts_tags;
post_id | tag_id
---------+--------
1 | 2
2 | 3
2 | 2
2 | 1
3 | 1
3 | 2
1 | 4
(7 rows)
rails-test_development=# WITH posts_tags_cte AS (
rails-test_development(# SELECT post_id, array_agg(tag_id) as tags
rails-test_development(# FROM posts_tags
rails-test_development(# WHERE tag_id in (1, 2)
rails-test_development(# GROUP BY post_id
rails-test_development(# )
rails-test_development-# SELECT posts.id FROM posts_tags_cte JOIN posts ON posts.id = posts_tags_cte.post_id
rails-test_development-# WHERE posts_tags_cte.tags @> array[1, 2]::int8[];
id
----
3
2
(2 rows)
现在 Rails:
rails-test$ rails c
Running via Spring preloader in process 7361
Loading development environment (Rails 5.2.1)
2.5.1 :001 > Post.by_tag([1,2]).first.attributes
Post Load (1.7ms) SELECT "posts".* FROM (WITH posts_tags_cte AS (
SELECT post_id, array_agg(tag_id) as tags
FROM posts_tags
WHERE tag_id in (1,2)
GROUP BY post_id
)
SELECT posts.* FROM posts_tags_cte JOIN posts ON posts.id = posts_tags_cte.post_id
WHERE posts_tags_cte.tags @> array[1,2]::int8[]) as posts
ORDER BY "posts"."id" ASC LIMIT [["LIMIT", 1]]
=> {"id"=>2, "name"=>"Postppp", "title"=>"Post", "content"=>"I don't know", "created_at"=>Sun, 09 Sep 2018 09:48:33 UTC +00:00, "updated_at"=>Sun, 09 Sep 2018 09:48:33 UTC +00:00, "month_and_year"=>Sun, 09 Sep 2018}
2.5.1 :002 > Post.by_tag([1,2]).last.attributes
Post Load (1.3ms) SELECT "posts".* FROM (WITH posts_tags_cte AS (
SELECT post_id, array_agg(tag_id) as tags
FROM posts_tags
WHERE tag_id in (1,2)
GROUP BY post_id
)
SELECT posts.* FROM posts_tags_cte JOIN posts ON posts.id = posts_tags_cte.post_id
WHERE posts_tags_cte.tags @> array[1,2]::int8[]) as posts
ORDER BY "posts"."id" DESC LIMIT [["LIMIT", 1]]
=> {"id"=>3, "name"=>"Post A", "title"=>"Post title", "content"=>"Lorem Ipsum is simply dummy text of the printing and typesetting industry.", "created_at"=>Sun, 09 Sep 2018 09:52:57 UTC +00:00, "updated_at"=>Sun, 09 Sep 2018 09:52:57 UTC +00:00, "month_and_year"=>Sun, 09 Sep 2018}
2.5.1 :003 >
并且我将范围定义为:
class Post < ApplicationRecord
has_and_belongs_to_many :tags
scope :by_tag, -> (tag_ids) {
sql = sanitize_sql_array [<<-SQL, tag_ids, tag_ids]
(WITH posts_tags_cte AS (
SELECT post_id, array_agg(tag_id) as tags
FROM posts_tags
WHERE tag_id in (?)
GROUP BY post_id
)
SELECT posts.* FROM posts_tags_cte JOIN posts ON posts.id = posts_tags_cte.post_id
WHERE posts_tags_cte.tags @> array[?]::int8[]) as posts
SQL
from(sql)
}
end
我得到了 this answer 的帮助。
我有一个视频模型和一个标签模型。标签 HABTM 视频和视频 HABTM 标签。
class Tag < ApplicationRecord
has_and_belongs_to_many :videos
scope :with_tag, -> (id) { where(id: id) }
end
class Video < ApplicationRecord
has_and_belongs_to_many :tags
end
我想知道如何创建一个查询,其中来自用户的未知数量的标签。所以我可以说,"show me all the videos that are tagged with (123 AND 124 AND 125)"。最终目标是我不知道用户要传入多少标签,所以我需要知道如何创建范围链并最终能够将整个内容传递到 ransack 中以进行文本搜索结果视频的某些字段。我怎样才能做到这一点?
更新:我想知道如何使用 Rails 和 ActiveRecord 执行此操作。我知道如何使用 SQL 完成此操作。但是只用 SQL 完成它不会让我将结果关系传递给 ransack。
我创建了一些相关的 tables 来编写这个答案,并根据 table 拥有的数据测试查询。所以,我在这里获取帖子,它们都有标签 1 和 2。
输入SQL:
rails-test_development=# select id from posts;
id
----
1
2
3
(3 rows)
rails-test_development=# select id from tags;
id
----
2
3
4
5
6
(5 rows)
rails-test_development=# select post_id, tag_id from posts_tags;
post_id | tag_id
---------+--------
1 | 2
2 | 3
2 | 2
2 | 1
3 | 1
3 | 2
1 | 4
(7 rows)
rails-test_development=# WITH posts_tags_cte AS (
rails-test_development(# SELECT post_id, array_agg(tag_id) as tags
rails-test_development(# FROM posts_tags
rails-test_development(# WHERE tag_id in (1, 2)
rails-test_development(# GROUP BY post_id
rails-test_development(# )
rails-test_development-# SELECT posts.id FROM posts_tags_cte JOIN posts ON posts.id = posts_tags_cte.post_id
rails-test_development-# WHERE posts_tags_cte.tags @> array[1, 2]::int8[];
id
----
3
2
(2 rows)
现在 Rails:
rails-test$ rails c
Running via Spring preloader in process 7361
Loading development environment (Rails 5.2.1)
2.5.1 :001 > Post.by_tag([1,2]).first.attributes
Post Load (1.7ms) SELECT "posts".* FROM (WITH posts_tags_cte AS (
SELECT post_id, array_agg(tag_id) as tags
FROM posts_tags
WHERE tag_id in (1,2)
GROUP BY post_id
)
SELECT posts.* FROM posts_tags_cte JOIN posts ON posts.id = posts_tags_cte.post_id
WHERE posts_tags_cte.tags @> array[1,2]::int8[]) as posts
ORDER BY "posts"."id" ASC LIMIT [["LIMIT", 1]]
=> {"id"=>2, "name"=>"Postppp", "title"=>"Post", "content"=>"I don't know", "created_at"=>Sun, 09 Sep 2018 09:48:33 UTC +00:00, "updated_at"=>Sun, 09 Sep 2018 09:48:33 UTC +00:00, "month_and_year"=>Sun, 09 Sep 2018}
2.5.1 :002 > Post.by_tag([1,2]).last.attributes
Post Load (1.3ms) SELECT "posts".* FROM (WITH posts_tags_cte AS (
SELECT post_id, array_agg(tag_id) as tags
FROM posts_tags
WHERE tag_id in (1,2)
GROUP BY post_id
)
SELECT posts.* FROM posts_tags_cte JOIN posts ON posts.id = posts_tags_cte.post_id
WHERE posts_tags_cte.tags @> array[1,2]::int8[]) as posts
ORDER BY "posts"."id" DESC LIMIT [["LIMIT", 1]]
=> {"id"=>3, "name"=>"Post A", "title"=>"Post title", "content"=>"Lorem Ipsum is simply dummy text of the printing and typesetting industry.", "created_at"=>Sun, 09 Sep 2018 09:52:57 UTC +00:00, "updated_at"=>Sun, 09 Sep 2018 09:52:57 UTC +00:00, "month_and_year"=>Sun, 09 Sep 2018}
2.5.1 :003 >
并且我将范围定义为:
class Post < ApplicationRecord
has_and_belongs_to_many :tags
scope :by_tag, -> (tag_ids) {
sql = sanitize_sql_array [<<-SQL, tag_ids, tag_ids]
(WITH posts_tags_cte AS (
SELECT post_id, array_agg(tag_id) as tags
FROM posts_tags
WHERE tag_id in (?)
GROUP BY post_id
)
SELECT posts.* FROM posts_tags_cte JOIN posts ON posts.id = posts_tags_cte.post_id
WHERE posts_tags_cte.tags @> array[?]::int8[]) as posts
SQL
from(sql)
}
end
我得到了 this answer 的帮助。