【问题标题】:Rails API - How to build a query to filter an array of strings?Rails API - 如何构建查询以过滤字符串数组?
【发布时间】:2019-12-02 22:30:16
【问题描述】:

我需要制作一个简单的 API 并添加以下功能:

GET /tools?tag=node (EXAMPLE)

工具模型具有以下列:
* 标题(字符串)
* 描述(文字)
* 链接(字符串)
* 标签(字符串数组)

我需要能够从 localhost 中搜索所有带有标签的工具。 我尝试使用Rack Reducer,但此方法仅适用于字符串或文本类型。

我知道这应该很简单,但我是 Rails 的新手。

感谢您的帮助。

schema.rb:

ActiveRecord::Schema.define(version: 2019_11_29_170156) do

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "tools", force: :cascade do |t|
    t.string "title"
    t.string "link"
    t.text "description"
    t.text "tags", default: [], array: true
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end

tools_controller.rb

class ToolsController < ApplicationController

  ToolReducer = Rack::Reducer.new(
    Tool.all,
    # ->(tags:) { select { |item| item[:tags].match(/#{tags}/i) } }
    ->(tags:) { where("tags @> ?", "%#{tags}%") }
  )

  def index
    # @tools = Tool.where(query_params)
    @tools = ToolReducer.apply(params)
    # @tools = Tool.all
    render json: @tools
  end

  def new
    @tool = Tool.new
  end

  def show
    @tool = Tool.find(params[:id])
    render json: @tool
  end

  def create
    @tool = Tool.new(tool_params)
    if @tool.save
      render json: @tool
      redirect_to @tool
    end
  end

  def destroy
    @tool = Tool.find(params[:id])
    @tool.destroy
  end

  private

    def tool_params
      params.require(:tool).permit(:title, :link, :description, :tags)
    end
end

【问题讨论】:

  • 如果你的数据库是 Postgres pg_search gem 非常有用

标签: ruby-on-rails postgresql api ruby-on-rails-5


【解决方案1】:

pg_search gem 非常适合在 Postgres 数据库中进行搜索。

但是,就您而言,我认为您的应用设计不佳。

与其将标签存储在“工具”模型的字段中,不如创建一个名为“标签”的新模型,并在标签模型和工具模型之间创建 has_and_belongs_to_many 关系。

查找和删除标签会容易得多。

(还有任何与机架相关的东西都会深入到您的应用程序中,它是中间件。我不知道 Rack::reducer 但我相信您不需要它)

【讨论】:

    【解决方案2】:

    如果您实际上首先正确设置了数据库,那么除了 Rails 之外没有其他任何东西可以完成这项非常简单的任务。

    您想要创建一个规范化的标签表和一个链接工具和标签的连接表:

    class Tool < ApplicationRecord
      has_many :taggings
      has_many :tags, through: :taggings
    end
    
    # rails g model tag name:string:uniq
    class Tag < ApplicationRecord
      validates_uniqueness_of :name
      has_many :taggings
      has_many :tools, through: :taggings
    end
    
    # rails g model tagging tool:belongs_to tag:belongs_to
    class Tagging < ApplicationRecord
      belongs_to :tool
      belongs_to :tag
    end
    

    这让您可以通过加入标签模型来过滤工具:

    # has at least one of the tags
    Tool.joins(:tags)
        .where(tags: { name: ["foo", "bar", "baz"] })
    
    # has all the tags
    Tool.joins(:tags)
        .where(tags: { name: ["foo", "bar", "baz"] })
        .group('tools.id')
        .having('count(*) = 3')
    

    您可以在 Rack 中通过以 [] 结尾的参数名称来传递数组:

    Started GET "/tools?tags[]=foo&tags[]=bar" for ::1 at 2019-12-02 23:49:37 +0100
       (0.4ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
    Processing by ToolsController#index as HTML
      Parameters: {"tags"=>["foo", "bar"]}
    

    当您创建选择和复选框时,Rails 表单助手是如何完成的。您还可以使用逗号分隔列表之类的东西并手动拆分参数。

    def index
      @tools = Tool.all
      if params[:tags].any?
        @tools = @tools.joins(:tags).where(tags: { name: params[:tags] })
      end
    end
    

    【讨论】:

      猜你喜欢
      • 2013-08-15
      • 1970-01-01
      • 2012-01-17
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 2021-06-05
      • 2016-01-11
      • 2021-09-24
      相关资源
      最近更新 更多