-
-
Notifications
You must be signed in to change notification settings - Fork 16
Full‐Text Search
Sometimes
LIKEis not enough.
Full-text searching is always a cool thing to do. However, it involves functions and operations that are generally cumbersome to write using Arel. With this feature, we can now easily set up our tables, have a proper scope, order by rank, get the rank if necessary, and it even supports dynamic language. PostgreSQL Docs
First, it is good to set up a generated column with the cached vector of the records. Although not mandatory, it makes the whole implementation more rounded. Rails do provide what it calls virtual columns, so this is built on top of it.
create_table :courses do |t|
t.string :title, null: false
t.text :description
t.search_vector :search_vector, columns: [:title, :description]
t.timestamps
endThis operation will create a proper virtual column named search_vector with the following function:
SETWEIGHT(TO_TSVECTOR('english', COALESCE(title, '')), 'A') ||
SETWEIGHT(TO_TSVECTOR('english', COALESCE(content, '')), 'B')-
columns: It supports a single column (:title), an array of columns ([:title, :description]), or a Hash of column and weight ({ title: 'A', description: 'A' }). -
index: If giventrue, it will set the column up with thefull_text_search.default_index_type. -
language: This is theregconfigused for theto_tsvectorfunction. It can be a plain string of the dictionary name, or a symbol to reference another column for dynamic language support (see example below). Defaults tofull_text_search.default_language.
# Example for a dynamic language
create_table :courses do |t|
t.string :title, null: false
t.text :description
t.search_language :lang, null: false, default: 'english'
t.column :lang, type: :regconfig, null: false, default: 'english' # Same as above
t.search_vector :search_vector, language: :lang, columns: [:title, :description]
t.timestamps
endThen we need to enable this feature on the model, which will give a single scope .full_text_search capable of handling everything.
class Course < ApplicationRecord
torque_search_for :search_vector
end-
prefix: It allows adding a prefix to the scope name (eg,prefix: :mainwill produce.main_full_text_search). -
suffix: It allows adding a suffix to the scope name (eg,suffix: :mainwill produce.full_text_search_main). -
language: This value affects the language of the input search term. It supports aStringas a plain value, or aSymbolthat refers to an attribute or a public class method. -
order: This sets the default value for ordering the result. It can be eithertrueor:ascforASCor:descforDESC. -
with_rank: This sets the default value for retrieving the result of theRANKfunction as an accessible attribute of the records. It can betrueto add the"rank"attribute, or the name of the alias. -
use_mode: It sets the default mode of parsing the provided value. Values can be::default,:plain,:phrase, or:web, respectively. Defaults to::phrase.
class Course < ApplicationRecord
torque_search_for :search_vector, language: :lang # Support for dynamic language on querying
endWith the scope now set up, we can call it with several different options to find what we want.
Course.full_text_search('Ruby')
# WHERE "courses"."search_vector" @@ PHRASETO_TSQUERY('english', 'Ruby')-
order: Indicates if the records should be sorted by theRANKoperation. It can be eithertrueor:ascforASCor:descforDESC. -
rank: If provided, it will add an extra column to the result set with the value of the calculated rank. It can betrue, which gives a.rankattribute, or the alias of the attribute. -
language: The language to be used for the search. It supports aStringas a plain value, or aSymbolthat refers to an attribute or a public class method. -
mode: It switches between using the functionsTO_TSQUERY,PLAINTO_TSQUERY,PHRASETO_TSQUERY, andWEBSEARCH_TO_TSQUERYon the provided value. Values can be::default,:plain,:phrase, or:web, respectively. Defaults to::phrase. See more.
Course.full_text_search('Ruby', order: :desc, rank: :rank_result, language: :lang, mode: :web)Produces:
SELECT "courses".*, TS_RANK("courses"."search_vector", WEBSEARCH_TO_TSQUERY(lang, 'Ruby')) AS rank_result
FROM "courses" WHERE "courses"."search_vector" @@ WEBSEARCH_TO_TSQUERY(lang, 'Ruby')
ORDER BY TS_RANK("courses"."search_vector", WEBSEARCH_TO_TSQUERY(lang, 'Ruby')) DESCFuture versions will attempt to move the
tsquerytransformations intoFROM.
Can't find what you're looking for? Add an issue to the issue tracker.