Use fuzzy search with minimum length of 3 characters where appropriate
This commit is contained in:
parent
d4eea27531
commit
da42dfb3cf
|
@ -60,10 +60,7 @@ module Ci
|
|||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def self.search(query)
|
||||
t = arel_table
|
||||
pattern = to_pattern(query)
|
||||
|
||||
where(t[:token].matches(pattern).or(t[:description].matches(pattern)))
|
||||
fuzzy_search(query, [:token, :description])
|
||||
end
|
||||
|
||||
def self.contact_time_deadline
|
||||
|
|
|
@ -122,9 +122,7 @@ module Issuable
|
|||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def search(query)
|
||||
title = fuzzy_arel_match(:title, query)
|
||||
|
||||
where(title)
|
||||
fuzzy_search(query, [:title])
|
||||
end
|
||||
|
||||
# Searches for records with a matching title or description.
|
||||
|
@ -135,10 +133,7 @@ module Issuable
|
|||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def full_search(query)
|
||||
title = fuzzy_arel_match(:title, query)
|
||||
description = fuzzy_arel_match(:description, query)
|
||||
|
||||
where(title&.or(description))
|
||||
fuzzy_search(query, [:title, :description])
|
||||
end
|
||||
|
||||
def sort(method, excluded_labels: [])
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class Email < ActiveRecord::Base
|
||||
include Sortable
|
||||
include Gitlab::SQL::Pattern
|
||||
|
||||
belongs_to :user
|
||||
|
||||
|
|
|
@ -74,10 +74,7 @@ class Milestone < ActiveRecord::Base
|
|||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def search(query)
|
||||
t = arel_table
|
||||
pattern = to_pattern(query)
|
||||
|
||||
where(t[:title].matches(pattern).or(t[:description].matches(pattern)))
|
||||
fuzzy_search(query, [:title, :description])
|
||||
end
|
||||
|
||||
def filter_by_state(milestones, state)
|
||||
|
|
|
@ -87,10 +87,7 @@ class Namespace < ActiveRecord::Base
|
|||
#
|
||||
# Returns an ActiveRecord::Relation
|
||||
def search(query)
|
||||
t = arel_table
|
||||
pattern = to_pattern(query)
|
||||
|
||||
where(t[:name].matches(pattern).or(t[:path].matches(pattern)))
|
||||
fuzzy_search(query, [:name, :path])
|
||||
end
|
||||
|
||||
def clean_path(path)
|
||||
|
|
|
@ -170,7 +170,7 @@ class Note < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def search(query)
|
||||
where(arel_table[:note].matches(to_pattern(query)))
|
||||
fuzzy_search(query, [:note])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -425,17 +425,11 @@ class Project < ActiveRecord::Base
|
|||
#
|
||||
# query - The search query as a String.
|
||||
def search(query)
|
||||
pattern = to_pattern(query)
|
||||
|
||||
where(
|
||||
arel_table[:path].matches(pattern)
|
||||
.or(arel_table[:name].matches(pattern))
|
||||
.or(arel_table[:description].matches(pattern))
|
||||
)
|
||||
fuzzy_search(query, [:path, :name, :description])
|
||||
end
|
||||
|
||||
def search_by_title(query)
|
||||
non_archived.where(arel_table[:name].matches(to_pattern(query)))
|
||||
non_archived.fuzzy_search(query, [:name])
|
||||
end
|
||||
|
||||
def visibility_levels
|
||||
|
|
|
@ -136,10 +136,7 @@ class Snippet < ActiveRecord::Base
|
|||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def search(query)
|
||||
t = arel_table
|
||||
pattern = to_pattern(query)
|
||||
|
||||
where(t[:title].matches(pattern).or(t[:file_name].matches(pattern)))
|
||||
fuzzy_search(query, [:title, :file_name])
|
||||
end
|
||||
|
||||
# Searches for snippets with matching content.
|
||||
|
@ -150,7 +147,7 @@ class Snippet < ActiveRecord::Base
|
|||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def search_code(query)
|
||||
where(arel_table[:content].matches(to_pattern(query)))
|
||||
fuzzy_search(query, [:content])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -313,9 +313,6 @@ class User < ActiveRecord::Base
|
|||
#
|
||||
# Returns an ActiveRecord::Relation.
|
||||
def search(query)
|
||||
table = arel_table
|
||||
pattern = User.to_pattern(query)
|
||||
|
||||
order = <<~SQL
|
||||
CASE
|
||||
WHEN users.name = %{query} THEN 0
|
||||
|
@ -325,11 +322,8 @@ class User < ActiveRecord::Base
|
|||
END
|
||||
SQL
|
||||
|
||||
where(
|
||||
table[:name].matches(pattern)
|
||||
.or(table[:email].matches(pattern))
|
||||
.or(table[:username].matches(pattern))
|
||||
).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
|
||||
fuzzy_search(query, [:name, :email, :username])
|
||||
.reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
|
||||
end
|
||||
|
||||
# searches user by given pattern
|
||||
|
@ -337,16 +331,16 @@ class User < ActiveRecord::Base
|
|||
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
|
||||
|
||||
def search_with_secondary_emails(query)
|
||||
table = arel_table
|
||||
email_table = Email.arel_table
|
||||
pattern = to_pattern(query)
|
||||
matched_by_emails_user_ids = email_table.project(email_table[:user_id]).where(email_table[:email].matches(pattern))
|
||||
matched_by_emails_user_ids = email_table
|
||||
.project(email_table[:user_id])
|
||||
.where(Email.fuzzy_arel_match(:email, query))
|
||||
|
||||
where(
|
||||
table[:name].matches(pattern)
|
||||
.or(table[:email].matches(pattern))
|
||||
.or(table[:username].matches(pattern))
|
||||
.or(table[:id].in(matched_by_emails_user_ids))
|
||||
fuzzy_arel_match(:name, query)
|
||||
.or(fuzzy_arel_match(:email, query))
|
||||
.or(fuzzy_arel_match(:username, query))
|
||||
.or(arel_table[:id].in(matched_by_emails_user_ids))
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use fuzzy search with minimum length of 3 characters where appropriate
|
||||
merge_request:
|
||||
author:
|
||||
type: performance
|
|
@ -7,6 +7,12 @@ module Gitlab
|
|||
REGEX_QUOTED_WORD = /(?<=\A| )"[^"]+"(?= |\z)/
|
||||
|
||||
class_methods do
|
||||
def fuzzy_search(query, columns)
|
||||
matches = columns.map { |col| fuzzy_arel_match(col, query) }.compact.reduce(:or)
|
||||
|
||||
where(matches)
|
||||
end
|
||||
|
||||
def to_pattern(query)
|
||||
if partial_matching?(query)
|
||||
"%#{sanitize_sql_like(query)}%"
|
||||
|
|
|
@ -473,7 +473,7 @@ describe Ci::Runner do
|
|||
end
|
||||
|
||||
describe '.search' do
|
||||
let(:runner) { create(:ci_runner, token: '123abc') }
|
||||
let(:runner) { create(:ci_runner, token: '123abc', description: 'test runner') }
|
||||
|
||||
it 'returns runners with a matching token' do
|
||||
expect(described_class.search(runner.token)).to eq([runner])
|
||||
|
|
|
@ -67,6 +67,7 @@ describe Issuable do
|
|||
|
||||
describe ".search" do
|
||||
let!(:searchable_issue) { create(:issue, title: "Searchable awesome issue") }
|
||||
let!(:searchable_issue2) { create(:issue, title: 'Aw') }
|
||||
|
||||
it 'returns issues with a matching title' do
|
||||
expect(issuable_class.search(searchable_issue.title))
|
||||
|
@ -86,8 +87,8 @@ describe Issuable do
|
|||
expect(issuable_class.search('searchable issue')).to eq([searchable_issue])
|
||||
end
|
||||
|
||||
it 'returns all issues with a query shorter than 3 chars' do
|
||||
expect(issuable_class.search('zz')).to eq(issuable_class.all)
|
||||
it 'returns issues with a matching title for a query shorter than 3 chars' do
|
||||
expect(issuable_class.search(searchable_issue2.title.downcase)).to eq([searchable_issue2])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -95,6 +96,7 @@ describe Issuable do
|
|||
let!(:searchable_issue) do
|
||||
create(:issue, title: "Searchable awesome issue", description: 'Many cute kittens')
|
||||
end
|
||||
let!(:searchable_issue2) { create(:issue, title: "Aw", description: "Cu") }
|
||||
|
||||
it 'returns issues with a matching title' do
|
||||
expect(issuable_class.full_search(searchable_issue.title))
|
||||
|
@ -133,8 +135,8 @@ describe Issuable do
|
|||
expect(issuable_class.full_search('many kittens')).to eq([searchable_issue])
|
||||
end
|
||||
|
||||
it 'returns all issues with a query shorter than 3 chars' do
|
||||
expect(issuable_class.search('zz')).to eq(issuable_class.all)
|
||||
it 'returns issues with a matching description for a query shorter than 3 chars' do
|
||||
expect(issuable_class.full_search(searchable_issue2.description.downcase)).to eq([searchable_issue2])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ describe Snippet do
|
|||
end
|
||||
|
||||
describe '.search' do
|
||||
let(:snippet) { create(:snippet) }
|
||||
let(:snippet) { create(:snippet, title: 'test snippet') }
|
||||
|
||||
it 'returns snippets with a matching title' do
|
||||
expect(described_class.search(snippet.title)).to eq([snippet])
|
||||
|
|
Loading…
Reference in New Issue