I need to make a simple API and add the following functionality:
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. I try to use Rack Reducer but this method only works on string or text types.
I know this should be simple, but I'm new in Rails.
Thanks for any help.
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
pg_search
gem is pretty good to make search in a Postgres database.
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.
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)
This is a really trivial task to accomplish without anything other than Rails if you actually setup your database properly first.
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 []
:
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. 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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.