代码之家  ›  专栏  ›  技术社区  ›  Eric Lubow

如何在rails中动态链接where子句?

  •  1
  • Eric Lubow  · 技术社区  · 6 年前

    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
    

    我想知道如何创建一个查询,其中未知数量的标签将来自用户。所以我可以说,“给我看所有标有(123、124和125)的视频”。最终的目标是我不知道用户将要传递多少个标签,所以我需要知道如何创建一个范围链,并最终能够将整个内容传递到ransack中,以便对生成的视频的某些字段进行文本搜索。我怎样才能做到这一点?

    更新:我想知道如何使用Rails和ActiveRecord来实现这一点。我知道如何用SQL实现这一点。但是,仅用SQL来实现它将不允许我将结果关系传递到ransack。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Arup Rakshit    6 年前

    我创建了一些相关的表来编写这个答案,并根据表中的数据测试查询。所以,我在这里获取的职位,其中有标签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 $1  [["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 $1  [["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 .