Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
gitlab-ce
Commits
59e53938
Commit
59e53938
authored
Aug 23, 2017
by
Hiroyuki Sato
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fuzzy search issuable title or description
parent
d6e956d3
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
181 additions
and
15 deletions
+181
-15
app/models/concerns/issuable.rb
app/models/concerns/issuable.rb
+7
-4
lib/gitlab/sql/pattern.rb
lib/gitlab/sql/pattern.rb
+23
-0
spec/lib/gitlab/sql/pattern_spec.rb
spec/lib/gitlab/sql/pattern_spec.rb
+120
-0
spec/models/concerns/issuable_spec.rb
spec/models/concerns/issuable_spec.rb
+31
-11
No files found.
app/models/concerns/issuable.rb
View file @
59e53938
...
@@ -6,6 +6,7 @@
...
@@ -6,6 +6,7 @@
#
#
module
Issuable
module
Issuable
extend
ActiveSupport
::
Concern
extend
ActiveSupport
::
Concern
include
Gitlab
::
SQL
::
Pattern
include
CacheMarkdownField
include
CacheMarkdownField
include
Participable
include
Participable
include
Mentionable
include
Mentionable
...
@@ -122,7 +123,9 @@ module Issuable
...
@@ -122,7 +123,9 @@ module Issuable
#
#
# Returns an ActiveRecord::Relation.
# Returns an ActiveRecord::Relation.
def
search
(
query
)
def
search
(
query
)
where
(
arel_table
[
:title
].
matches
(
"%
#{
query
}
%"
))
title
=
to_fuzzy_arel
(
:title
,
query
)
where
(
title
)
end
end
# Searches for records with a matching title or description.
# Searches for records with a matching title or description.
...
@@ -133,10 +136,10 @@ module Issuable
...
@@ -133,10 +136,10 @@ module Issuable
#
#
# Returns an ActiveRecord::Relation.
# Returns an ActiveRecord::Relation.
def
full_search
(
query
)
def
full_search
(
query
)
t
=
arel_table
t
itle
=
to_fuzzy_arel
(
:title
,
query
)
pattern
=
"%
#{
query
}
%"
description
=
to_fuzzy_arel
(
:description
,
query
)
where
(
t
[
:title
].
matches
(
pattern
).
or
(
t
[
:description
].
matches
(
pattern
)
))
where
(
t
itle
&
.
or
(
description
))
end
end
def
sort
(
method
,
excluded_labels:
[])
def
sort
(
method
,
excluded_labels:
[])
...
...
lib/gitlab/sql/pattern.rb
View file @
59e53938
...
@@ -4,6 +4,7 @@ module Gitlab
...
@@ -4,6 +4,7 @@ module Gitlab
extend
ActiveSupport
::
Concern
extend
ActiveSupport
::
Concern
MIN_CHARS_FOR_PARTIAL_MATCHING
=
3
MIN_CHARS_FOR_PARTIAL_MATCHING
=
3
REGEX_QUOTED_WORD
=
/(?<=^| )"[^"]+"(?= |$)/
class_methods
do
class_methods
do
def
to_pattern
(
query
)
def
to_pattern
(
query
)
...
@@ -17,6 +18,28 @@ module Gitlab
...
@@ -17,6 +18,28 @@ module Gitlab
def
partial_matching?
(
query
)
def
partial_matching?
(
query
)
query
.
length
>=
MIN_CHARS_FOR_PARTIAL_MATCHING
query
.
length
>=
MIN_CHARS_FOR_PARTIAL_MATCHING
end
end
def
to_fuzzy_arel
(
column
,
query
)
words
=
select_fuzzy_words
(
query
)
matches
=
words
.
map
{
|
word
|
arel_table
[
column
].
matches
(
to_pattern
(
word
))
}
matches
.
reduce
{
|
result
,
match
|
result
.
and
(
match
)
}
end
def
select_fuzzy_words
(
query
)
quoted_words
=
query
.
scan
(
REGEX_QUOTED_WORD
)
query
=
quoted_words
.
reduce
(
query
)
{
|
q
,
quoted_word
|
q
.
sub
(
quoted_word
,
''
)
}
words
=
query
.
split
(
/\s+/
)
quoted_words
.
map!
{
|
quoted_word
|
quoted_word
[
1
..-
2
]
}
words
.
concat
(
quoted_words
)
words
.
select
{
|
word
|
partial_matching?
(
word
)
}
end
end
end
end
end
end
end
...
...
spec/lib/gitlab/sql/pattern_spec.rb
View file @
59e53938
...
@@ -52,4 +52,124 @@ describe Gitlab::SQL::Pattern do
...
@@ -52,4 +52,124 @@ describe Gitlab::SQL::Pattern do
end
end
end
end
end
end
describe
'.select_fuzzy_words'
do
subject
(
:select_fuzzy_words
)
{
Issue
.
select_fuzzy_words
(
query
)
}
context
'with a word equal to 3 chars'
do
let
(
:query
)
{
'foo'
}
it
'returns array cotaining a word'
do
expect
(
select_fuzzy_words
).
to
match_array
([
'foo'
])
end
end
context
'with a word shorter than 3 chars'
do
let
(
:query
)
{
'fo'
}
it
'returns empty array'
do
expect
(
select_fuzzy_words
).
to
match_array
([])
end
end
context
'with two words both equal to 3 chars'
do
let
(
:query
)
{
'foo baz'
}
it
'returns array containing two words'
do
expect
(
select_fuzzy_words
).
to
match_array
(
%w[foo baz]
)
end
end
context
'with two words divided by two speces both equal to 3 chars'
do
let
(
:query
)
{
'foo baz'
}
it
'returns array containing two words'
do
expect
(
select_fuzzy_words
).
to
match_array
(
%w[foo baz]
)
end
end
context
'with two words equal to 3 chars and shorter than 3 chars'
do
let
(
:query
)
{
'foo ba'
}
it
'returns array containing a word'
do
expect
(
select_fuzzy_words
).
to
match_array
([
'foo'
])
end
end
context
'with a multi-word surrounded by double quote'
do
let
(
:query
)
{
'"really bar"'
}
it
'returns array containing a multi-word'
do
expect
(
select_fuzzy_words
).
to
match_array
([
'really bar'
])
end
end
context
'with a multi-word surrounded by double quote and two words'
do
let
(
:query
)
{
'foo "really bar" baz'
}
it
'returns array containing a multi-word and tow words'
do
expect
(
select_fuzzy_words
).
to
match_array
([
'foo'
,
'really bar'
,
'baz'
])
end
end
context
'with a multi-word surrounded by double quote missing a spece before the first double quote'
do
let
(
:query
)
{
'foo"really bar"'
}
it
'returns array containing two words with double quote'
do
expect
(
select_fuzzy_words
).
to
match_array
([
'foo"really'
,
'bar"'
])
end
end
context
'with a multi-word surrounded by double quote missing a spece after the second double quote'
do
let
(
:query
)
{
'"really bar"baz'
}
it
'returns array containing two words with double quote'
do
expect
(
select_fuzzy_words
).
to
match_array
([
'"really'
,
'bar"baz'
])
end
end
context
'with two multi-word surrounded by double quote and two words'
do
let
(
:query
)
{
'foo "really bar" baz "awesome feature"'
}
it
'returns array containing two multi-words and tow words'
do
expect
(
select_fuzzy_words
).
to
match_array
([
'foo'
,
'really bar'
,
'baz'
,
'awesome feature'
])
end
end
end
describe
'.to_fuzzy_arel'
do
subject
(
:to_fuzzy_arel
)
{
Issue
.
to_fuzzy_arel
(
:title
,
query
)
}
context
'with a word equal to 3 chars'
do
let
(
:query
)
{
'foo'
}
it
'returns a single ILIKE condition'
do
expect
(
to_fuzzy_arel
.
to_sql
).
to
eq
(
%("issues"."title" ILIKE '%foo%')
)
end
end
context
'with a word shorter than 3 chars'
do
let
(
:query
)
{
'fo'
}
it
'returns nil'
do
expect
(
to_fuzzy_arel
).
to
be_nil
end
end
context
'with two words both equal to 3 chars'
do
let
(
:query
)
{
'foo baz'
}
it
'returns a joining ILIKE condition using a AND'
do
expect
(
to_fuzzy_arel
.
to_sql
).
to
eq
(
%("issues"."title" ILIKE '%foo%' AND "issues"."title" ILIKE '%baz%')
)
end
end
context
'with a multi-word surrounded by double quote and two words'
do
let
(
:query
)
{
'foo "really bar" baz'
}
it
'returns a joining ILIKE condition using a AND'
do
expect
(
to_fuzzy_arel
.
to_sql
).
to
eq
(
%("issues"."title" ILIKE '%foo%' AND "issues"."title" ILIKE '%baz%' AND "issues"."title" ILIKE '%really bar%')
)
end
end
end
end
end
spec/models/concerns/issuable_spec.rb
View file @
59e53938
...
@@ -66,56 +66,76 @@ describe Issuable do
...
@@ -66,56 +66,76 @@ describe Issuable do
end
end
describe
".search"
do
describe
".search"
do
let!
(
:searchable_issue
)
{
create
(
:issue
,
title:
"Searchable issue"
)
}
let!
(
:searchable_issue
)
{
create
(
:issue
,
title:
"Searchable
awesome
issue"
)
}
it
'returns
not
es with a matching title'
do
it
'returns
issu
es with a matching title'
do
expect
(
issuable_class
.
search
(
searchable_issue
.
title
))
expect
(
issuable_class
.
search
(
searchable_issue
.
title
))
.
to
eq
([
searchable_issue
])
.
to
eq
([
searchable_issue
])
end
end
it
'returns
not
es with a partially matching title'
do
it
'returns
issu
es with a partially matching title'
do
expect
(
issuable_class
.
search
(
'able'
)).
to
eq
([
searchable_issue
])
expect
(
issuable_class
.
search
(
'able'
)).
to
eq
([
searchable_issue
])
end
end
it
'returns
not
es with a matching title regardless of the casing'
do
it
'returns
issu
es with a matching title regardless of the casing'
do
expect
(
issuable_class
.
search
(
searchable_issue
.
title
.
upcase
))
expect
(
issuable_class
.
search
(
searchable_issue
.
title
.
upcase
))
.
to
eq
([
searchable_issue
])
.
to
eq
([
searchable_issue
])
end
end
it
'returns issues with a fuzzy matching title'
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
)
end
end
end
describe
".full_search"
do
describe
".full_search"
do
let!
(
:searchable_issue
)
do
let!
(
:searchable_issue
)
do
create
(
:issue
,
title:
"Searchable
issue"
,
description:
'
kittens'
)
create
(
:issue
,
title:
"Searchable
awesome issue"
,
description:
'Many cute
kittens'
)
end
end
it
'returns
not
es with a matching title'
do
it
'returns
issu
es with a matching title'
do
expect
(
issuable_class
.
full_search
(
searchable_issue
.
title
))
expect
(
issuable_class
.
full_search
(
searchable_issue
.
title
))
.
to
eq
([
searchable_issue
])
.
to
eq
([
searchable_issue
])
end
end
it
'returns
not
es with a partially matching title'
do
it
'returns
issu
es with a partially matching title'
do
expect
(
issuable_class
.
full_search
(
'able'
)).
to
eq
([
searchable_issue
])
expect
(
issuable_class
.
full_search
(
'able'
)).
to
eq
([
searchable_issue
])
end
end
it
'returns
not
es with a matching title regardless of the casing'
do
it
'returns
issu
es with a matching title regardless of the casing'
do
expect
(
issuable_class
.
full_search
(
searchable_issue
.
title
.
upcase
))
expect
(
issuable_class
.
full_search
(
searchable_issue
.
title
.
upcase
))
.
to
eq
([
searchable_issue
])
.
to
eq
([
searchable_issue
])
end
end
it
'returns notes with a matching description'
do
it
'returns issues with a fuzzy matching title'
do
expect
(
issuable_class
.
full_search
(
'searchable issue'
)).
to
eq
([
searchable_issue
])
end
it
'returns issues with a matching description'
do
expect
(
issuable_class
.
full_search
(
searchable_issue
.
description
))
expect
(
issuable_class
.
full_search
(
searchable_issue
.
description
))
.
to
eq
([
searchable_issue
])
.
to
eq
([
searchable_issue
])
end
end
it
'returns
not
es with a partially matching description'
do
it
'returns
issu
es with a partially matching description'
do
expect
(
issuable_class
.
full_search
(
searchable_issue
.
description
))
expect
(
issuable_class
.
full_search
(
searchable_issue
.
description
))
.
to
eq
([
searchable_issue
])
.
to
eq
([
searchable_issue
])
end
end
it
'returns
not
es with a matching description regardless of the casing'
do
it
'returns
issu
es with a matching description regardless of the casing'
do
expect
(
issuable_class
.
full_search
(
searchable_issue
.
description
.
upcase
))
expect
(
issuable_class
.
full_search
(
searchable_issue
.
description
.
upcase
))
.
to
eq
([
searchable_issue
])
.
to
eq
([
searchable_issue
])
end
end
it
'returns issues with a fuzzy matching description'
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
)
end
end
end
describe
'.to_ability_name'
do
describe
'.to_ability_name'
do
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment