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
Boxiang Sun
gitlab-ce
Commits
7357209f
Commit
7357209f
authored
Jun 06, 2018
by
Mario de la Ossa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement filtering by filename on code search
parent
60b102be
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
186 additions
and
6 deletions
+186
-6
changelogs/unreleased/ce-5024-filename-search.yml
changelogs/unreleased/ce-5024-filename-search.yml
+5
-0
doc/api/search.md
doc/api/search.md
+9
-0
lib/gitlab/file_finder.rb
lib/gitlab/file_finder.rb
+12
-5
lib/gitlab/search/parsed_query.rb
lib/gitlab/search/parsed_query.rb
+23
-0
lib/gitlab/search/query.rb
lib/gitlab/search/query.rb
+55
-0
spec/lib/gitlab/file_finder_spec.rb
spec/lib/gitlab/file_finder_spec.rb
+19
-1
spec/lib/gitlab/search/query_spec.rb
spec/lib/gitlab/search/query_spec.rb
+39
-0
spec/requests/api/search_spec.rb
spec/requests/api/search_spec.rb
+24
-0
No files found.
changelogs/unreleased/ce-5024-filename-search.yml
0 → 100644
View file @
7357209f
---
title
:
Add filename filtering to code search
merge_request
:
19509
author
:
type
:
added
doc/api/search.md
View file @
7357209f
...
...
@@ -776,6 +776,15 @@ Example response:
### Scope: blobs
Filters are available for this scope:
-
filename
-
path
-
extension
to use a filter simply include it in your query like so:
`a query filename:some_name*`
.
You may use wildcards (
`*`
) to use glob matching.
```
bash
curl
--request
GET
--header
"PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK"
https://gitlab.example.com/api/v4/projects/6/search?scope
=
blobs&search
=
installation
```
...
...
lib/gitlab/file_finder.rb
View file @
7357209f
...
...
@@ -14,14 +14,21 @@ module Gitlab
end
def
find
(
query
)
by_content
=
find_by_content
(
query
)
query
=
Gitlab
::
Search
::
Query
.
new
(
query
)
do
filter
:filename
,
matcher:
->
(
filter
,
blob
)
{
blob
.
filename
=~
/
#{
filter
[
:regex_value
]
}
$/i
}
filter
:path
,
matcher:
->
(
filter
,
blob
)
{
blob
.
filename
=~
/
#{
filter
[
:regex_value
]
}
/i
}
filter
:extension
,
matcher:
->
(
filter
,
blob
)
{
blob
.
filename
=~
/\.
#{
filter
[
:regex_value
]
}
$/i
}
end
by_content
=
find_by_content
(
query
.
term
)
already_found
=
Set
.
new
(
by_content
.
map
(
&
:filename
))
by_filename
=
find_by_filename
(
query
,
except:
already_found
)
by_filename
=
find_by_filename
(
query
.
term
,
except:
already_found
)
(
by_content
+
by_filename
)
files
=
(
by_content
+
by_filename
)
.
sort_by
(
&
:filename
)
.
map
{
|
blob
|
[
blob
.
filename
,
blob
]
}
query
.
filter_results
(
files
).
map
{
|
blob
|
[
blob
.
filename
,
blob
]
}
end
private
...
...
lib/gitlab/search/parsed_query.rb
0 → 100644
View file @
7357209f
module
Gitlab
module
Search
class
ParsedQuery
attr_reader
:term
,
:filters
def
initialize
(
term
,
filters
)
@term
=
term
@filters
=
filters
end
def
filter_results
(
results
)
filters
=
@filters
.
reject
{
|
filter
|
filter
[
:matcher
].
nil?
}
return
unless
filters
results
.
select
do
|
result
|
filters
.
all?
do
|
filter
|
filter
[
:matcher
].
call
(
filter
,
result
)
end
end
end
end
end
end
lib/gitlab/search/query.rb
0 → 100644
View file @
7357209f
module
Gitlab
module
Search
class
Query
<
SimpleDelegator
def
initialize
(
query
,
filter_opts
=
{},
&
block
)
@raw_query
=
query
.
dup
@filters
=
[]
@filter_options
=
{
default_parser: :downcase
.
to_proc
}.
merge
(
filter_opts
)
self
.
instance_eval
(
&
block
)
if
block_given?
@query
=
Gitlab
::
Search
::
ParsedQuery
.
new
(
*
extract_filters
)
# set the ParsedQuery as our default delegator thanks to SimpleDelegator
super
(
@query
)
end
private
def
filter
(
name
,
**
attributes
)
filter
=
{
parser:
@filter_options
[
:default_parser
],
name:
name
}.
merge
(
attributes
)
@filters
<<
filter
end
def
filter_options
(
**
options
)
@filter_options
.
merge!
(
options
)
end
def
extract_filters
fragments
=
[]
filters
=
@filters
.
each_with_object
([])
do
|
filter
,
parsed_filters
|
match
=
@raw_query
.
split
.
find
{
|
part
|
part
=~
/\A
#{
filter
[
:name
]
}
:/
}
next
unless
match
input
=
match
.
split
(
':'
)[
1
..-
1
].
join
next
if
input
.
empty?
filter
[
:value
]
=
parse_filter
(
filter
,
input
)
filter
[
:regex_value
]
=
Regexp
.
escape
(
filter
[
:value
]).
gsub
(
'\*'
,
'.*?'
)
fragments
<<
match
parsed_filters
<<
filter
end
query
=
(
@raw_query
.
split
-
fragments
).
join
(
' '
)
[
query
,
filters
]
end
def
parse_filter
(
filter
,
input
)
filter
[
:parser
].
call
(
input
)
end
end
end
end
spec/lib/gitlab/file_finder_spec.rb
View file @
7357209f
...
...
@@ -3,11 +3,29 @@ require 'spec_helper'
describe
Gitlab
::
FileFinder
do
describe
'#find'
do
let
(
:project
)
{
create
(
:project
,
:public
,
:repository
)
}
subject
{
described_class
.
new
(
project
,
project
.
default_branch
)
}
it_behaves_like
'file finder'
do
subject
{
described_class
.
new
(
project
,
project
.
default_branch
)
}
let
(
:expected_file_by_name
)
{
'files/images/wm.svg'
}
let
(
:expected_file_by_content
)
{
'CHANGELOG'
}
end
it
'filters by name'
do
results
=
subject
.
find
(
'files filename:wm.svg'
)
expect
(
results
.
count
).
to
eq
(
1
)
end
it
'filters by path'
do
results
=
subject
.
find
(
'white path:images'
)
expect
(
results
.
count
).
to
eq
(
1
)
end
it
'filters by extension'
do
results
=
subject
.
find
(
'files extension:svg'
)
expect
(
results
.
count
).
to
eq
(
1
)
end
end
end
spec/lib/gitlab/search/query_spec.rb
0 → 100644
View file @
7357209f
require
'spec_helper'
describe
Gitlab
::
Search
::
Query
do
let
(
:query
)
{
'base filter:wow anotherfilter:noway name:maybe other:mmm leftover'
}
let
(
:subject
)
do
described_class
.
new
(
query
)
do
filter
:filter
filter
:name
,
parser: :upcase
.
to_proc
filter
:other
end
end
it
{
expect
(
described_class
).
to
be
<
SimpleDelegator
}
it
'leaves undefined filters in the main query'
do
expect
(
subject
.
term
).
to
eq
(
'base anotherfilter:noway leftover'
)
end
it
'parses filters'
do
expect
(
subject
.
filters
.
count
).
to
eq
(
3
)
expect
(
subject
.
filters
.
map
{
|
f
|
f
[
:value
]
}).
to
match_array
(
%w[wow MAYBE mmm]
)
end
context
'with an empty filter'
do
let
(
:query
)
{
'some bar name: baz'
}
it
'ignores empty filters'
do
expect
(
subject
.
term
).
to
eq
(
'some bar name: baz'
)
end
end
context
'with a pipe'
do
let
(
:query
)
{
'base | nofilter'
}
it
'does not escape the pipe'
do
expect
(
subject
.
term
).
to
eq
(
query
)
end
end
end
spec/requests/api/search_spec.rb
View file @
7357209f
...
...
@@ -312,6 +312,30 @@ describe API::Search do
end
it_behaves_like
'response is correct'
,
schema:
'public_api/v4/blobs'
,
size:
2
context
'filters'
do
it
'by filename'
do
get
api
(
"/projects/
#{
repo_project
.
id
}
/search"
,
user
),
scope:
'blobs'
,
search:
'mon filename:PROCESS.md'
expect
(
response
).
to
have_gitlab_http_status
(
200
)
expect
(
json_response
.
size
).
to
eq
(
2
)
expect
(
json_response
.
first
[
'filename'
]).
to
eq
(
'PROCESS.md'
)
end
it
'by path'
do
get
api
(
"/projects/
#{
repo_project
.
id
}
/search"
,
user
),
scope:
'blobs'
,
search:
'mon path:markdown'
expect
(
response
).
to
have_gitlab_http_status
(
200
)
expect
(
json_response
.
size
).
to
eq
(
8
)
end
it
'by extension'
do
get
api
(
"/projects/
#{
repo_project
.
id
}
/search"
,
user
),
scope:
'blobs'
,
search:
'mon extension:md'
expect
(
response
).
to
have_gitlab_http_status
(
200
)
expect
(
json_response
.
size
).
to
eq
(
11
)
end
end
end
end
end
...
...
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