[英]Rails API - How to build a query to filter an array of strings?
I need to make a simple API and add the following functionality:我需要制作一个简单的 API 并添加以下功能:
GET /tools?tag=node (EXAMPLE)
The Tool model has the following columns:工具模型具有以下列:
* title (string) * 标题(字符串)
* description (text) * 描述(文本)
* link (string) * 链接(字符串)
* tags (array of strings) * 标签(字符串数组)
I need to be able to search from localhost all the tools that have some tag in it.我需要能够从 localhost 搜索所有带有标签的工具。 I try to use Rack Reducer but this method only works on string or text types.我尝试使用Rack Reducer,但此方法仅适用于字符串或文本类型。
I know this should be simple, but I'm new in Rails.我知道这应该很简单,但我是 Rails 新手。
Thanks for any help.谢谢你的帮助。
schema.rb:架构.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 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
pg_search
gem is pretty good to make search in a Postgres database. pg_search
gem 非常适合在 Postgres 数据库中进行搜索。
Yet, in your case, I think your app has a bad design.但是,就您而言,我认为您的应用程序设计不佳。
Instead of storing tags in a field of the "tool" model, you should maybe create a new model called "tags" and create a has_and_belongs_to_many relationship between the tags model and the tool model.不是在“工具”模型的字段中存储标签,您应该创建一个名为“标签”的新模型,并在标签模型和工具模型之间创建 has_and_belongs_to_many 关系。
It will be much easier to find and delete tags.查找和删除标签会容易得多。
(Also anything rack related goes deeper into your app, it is middleware. I don't know Rack::reducer but I am sure you don't need it) (此外,任何与机架相关的内容都会深入到您的应用程序中,它是中间件。我不知道 Rack::reducer,但我确定您不需要它)
This is a really trivial task to accomplish without anything other than Rails if you actually setup your database properly first.如果您首先正确地设置了数据库,那么除了 Rails 之外,无需任何其他东西即可完成这项任务。
You want to create a normalized tags table and a join table that links tools and tags:您想创建一个规范化的标签表和一个链接工具和标签的连接表:
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
This lets you filter tools by joining the tags model:这使您可以通过加入标签模型来过滤工具:
# 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')
You can pass arrays in Rack by ending the parameter names with []
:您可以通过以[]
结束参数名称来在 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"]}
This how its done by the Rails form helpers when you create selects and checkboxes.这是当您创建选择和复选框时 Rails 表单助手如何完成的。 You could also use something like a comma seperated list and manually split the parameter.您还可以使用逗号分隔列表之类的内容并手动拆分参数。
def index
@tools = Tool.all
if params[:tags].any?
@tools = @tools.joins(:tags).where(tags: { name: params[:tags] })
end
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.