Reputation: 6455
I'm working on a project with a search function that is set up as follows:
if params[:search_term].present? && params[:search_term].length > 1
@candidates = @candidates.where("title like ?","%#{params[:search_term]}%")
end
The client has asked me to 'loosen up' the search - specifically the word order. At the moment if there is a candidate with a title of White bar stool
and one searches for White stool bar
, it returns no results.
Is there any way for me to perform a query where word order is ignored? Or would it be better for me to make new search term params with a different word order, do multiple searches, and combine the results?
Upvotes: 1
Views: 120
Reputation: 29598
You may consider using Arel
for this. Arel
is the underlying query assembler for rails/activerecord (so no new dependencies) and can be very useful when building complex queries because it offers far more depth than the high level ActiveRecord::QueryMethods
.
Arel
offers a large selection of predication matchers including in your case matches_any
and matches_all
. These methods take an Array
of String
s and split them into individual search conditions using Like
.
For Example to search for all candidates that contain any of the words searched for you can use:
class Candidate < ActiveRecord::Base
def self.search_for(term)
candidates = Candidate.arel_table
where(
candidates[:title].lower.matches_any(
term.split.map { |t| "%#{t.downcase}%" }
)
)
end
end
The end result of search_for
(given a search term of 'White stool bar') is:
SELECT [candidates].*
FROM [candidates]
WHERE (
LOWER([candidates].[title]) LIKE '%white%'
OR LOWER([candidates].[title]) LIKE '%stool%'
OR LOWER([candidates].[title]) LIKE '%bar%')
Which appears to be what you are looking for. If it must match all the terms you can instead use matches_all
which will result in:
SELECT [candidates].*
FROM [candidates]
WHERE (
LOWER([candidates].[title]) LIKE '%white%'
AND LOWER([candidates].[title]) LIKE '%stool%'
AND LOWER([candidates].[title]) LIKE '%bar%')
See Here for all the available Arel
predications.
This has added benefits of basic escaping to avoid things like SQL injection.
Upvotes: 3
Reputation: 33481
You can use the MySQL RLIKE
operator, to match with an specific pattern you can create with your sentence.
sentence = 'White stoll bar'
@candidates = @candidates.where('title RLIKE ?', "(#{sentence.tr(' ', '|')})")
Upvotes: 3