-
-
Notifications
You must be signed in to change notification settings - Fork 67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Exact search on a post title #428
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,11 @@ def self.fuzzy_search(term, **cols) | |
|
||
def self.match_search(term, **cols) | ||
sanitized = sanitize_for_search term, **cols | ||
select(Arel.sql("`#{table_name}`.*, #{sanitized} AS search_score")).where(sanitized) | ||
|
||
mappedCols = sanitized.map { |val| "#{val} AS search_score_#{sanitized.find_index(val)}" }.join(', ') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ActiveSupport has a neat extension you can use to simplify this: mapped_cols = sanitized.map.with_index { |val, idx| "#{val} AS search_score_#{idx}" }.join(', ') |
||
whereClause = sanitized.map { |val| val.to_s }.join(' OR') | ||
Comment on lines
+12
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style: Ruby variables are named in |
||
|
||
select(Arel.sql("`#{table_name}`.*, #{mappedCols}")).where(whereClause).order('search_score_0 * 2 + search_score_1 * 1 DESC') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrap raw SQL statements in .order(Arel.sql('search_score_0 * 2 + search_score_1 * 1 DESC')) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does "* 1" do? Seems unnecessary - i.e., is that any different from just "search_score_0 * 2 + search_score_1 DESC"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably not - I just copied it out without really thinking about it :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, does nothing really. Was just experimenting on how we would determine search weights |
||
end | ||
|
||
def self.sanitize_name(name) | ||
|
@@ -26,13 +30,16 @@ def attributes_print | |
def self.sanitize_for_search(term, **cols) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original behaviour of this method was to take a search parameter and list of columns, and turn them into a search against a single fulltext index, i.e. sanitize_for_search 'query', posts: :body
# => "MATCH (`posts`.`body`) AGAINST ('query' IN BOOLEAN MODE)" Do I understand right that what you're trying to do is make this return multiple sanitize_for_search 'query', posts: [:body, :title]
# => [" MATCH `posts`.`body` AGAINST ('query' IN BOOLEAN MODE)", " MATCH `posts`.`title` AGAINST ('query' IN BOOLEAN MODE)"] If you only pass a single column, you just get the column's name, no MATCH clause at all: sanitize_for_search 'query', posts: :body
# => "`posts`.`body`" What's probably an easier way to do it (if I understand what you want correctly) is:
Something like this: cols = cols.map do |table_name, column_or_array|
if column_or_array.is_a?(Array)
column_or_array.map { |column_name| "#{sanitize_name table_name}.#{sanitize_name column_name}" }
else
"#{sanitize_name table_name}.#{sanitize_name column_or_array}"
end
end.flatten
cols.map do |sanitized_name|
ActiveRecord::Base.sanitize_sql(["MATCH (#{sanitized_name}) AGAINST (? IN BOOLEAN MODE)", term])
end There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yup exactly what I was trying to do. Ran your suggestion and it worked like a charm! Would there be a case where we pass in a single column? The methods are first being called here https://github.com/codidact/qpixel/blob/develop/app/models/post.rb#L59 and it looks like it will only ever look at the columns that we tell it. If there is, then the way I am trying to do the search weight would break (not that it was a good way to do it anyways 😛) since it will try to look for a column that doesn't exist. |
||
cols = cols.map do |k, v| | ||
if v.is_a?(Array) | ||
v.map { |vv| "#{sanitize_name k}.#{sanitize_name vv}" }.join(', ') | ||
v.map do |vv| | ||
#prob can do this just once at end of map | ||
ActiveRecord::Base.sanitize_sql([" MATCH #{sanitize_name k}.#{sanitize_name vv} AGAINST (? IN BOOLEAN MODE)", term]) | ||
end | ||
else | ||
"#{sanitize_name k}.#{sanitize_name v}" | ||
end | ||
end.join(', ') | ||
end | ||
|
||
ActiveRecord::Base.send(:sanitize_sql_array, ["MATCH (#{cols}) AGAINST (? IN BOOLEAN MODE)", term]) | ||
cols[0] | ||
end | ||
|
||
def self.sanitize_sql_in(ary) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class AddFulltextIndexToPostsTitle < ActiveRecord::Migration[5.2] | ||
def change | ||
add_index :posts, :title, type: :fulltext | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could do with updating to re-add a default for the new weighted search, and to change the
relevance
sort to the same weighted search. Not sure whether our implementation ofuser_sort
supports that viaArel.sql
, though, so that's probably not one for this PR - needs investigation.