Commit 9fb4d3ac authored by Changzheng Liu's avatar Changzheng Liu Committed by Matthias Käppler

Improve search filter by taking space in file path into account

parent d3f092da
---
title: Improve search filter by taking space in file path into account
merge_request: 52392
author:
type: fixed
...@@ -5,6 +5,9 @@ module Gitlab ...@@ -5,6 +5,9 @@ module Gitlab
class Query < SimpleDelegator class Query < SimpleDelegator
include EncodingHelper include EncodingHelper
QUOTES_REGEXP = %r{\A"|"\Z}.freeze
TOKEN_WITH_QUOTES_REGEXP = %r{\s(?=(?:[^"]|"[^"]*")*$)}.freeze
def initialize(query, filter_opts = {}, &block) def initialize(query, filter_opts = {}, &block)
@raw_query = query.dup @raw_query = query.dup
@filters = [] @filters = []
...@@ -35,22 +38,24 @@ module Gitlab ...@@ -35,22 +38,24 @@ module Gitlab
def extract_filters def extract_filters
fragments = [] fragments = []
query_tokens = parse_raw_query
filters = @filters.each_with_object([]) do |filter, parsed_filters| filters = @filters.each_with_object([]) do |filter, parsed_filters|
match = @raw_query.split.find { |part| part =~ /\A-?#{filter[:name]}:/ } match = query_tokens.find { |part| part =~ /\A-?#{filter[:name]}:/ }
next unless match next unless match
input = match.split(':')[1..-1].join input = match.split(':')[1..-1].join
next if input.empty? next if input.empty?
filter[:negated] = match.start_with?("-") filter[:negated] = match.start_with?("-")
filter[:value] = parse_filter(filter, input) filter[:value] = parse_filter(filter, input.gsub(QUOTES_REGEXP, ''))
filter[:regex_value] = Regexp.escape(filter[:value]).gsub('\*', '.*?') filter[:regex_value] = Regexp.escape(filter[:value]).gsub('\*', '.*?')
fragments << match fragments << match
parsed_filters << filter parsed_filters << filter
end end
query = (@raw_query.split - fragments).join(' ') query = (query_tokens - fragments).join(' ')
query = '*' if query.empty? query = '*' if query.empty?
[query, filters] [query, filters]
...@@ -61,6 +66,13 @@ module Gitlab ...@@ -61,6 +66,13 @@ module Gitlab
@filter_options[:encode_binary] ? encode_binary(result) : result @filter_options[:encode_binary] ? encode_binary(result) : result
end end
def parse_raw_query
# Positive lookahead for any non-quote char or even number of quotes
# for example '"search term" path:"foo bar.txt"' would break into
# ["search term", "path:\"foo bar.txt\""]
@raw_query.split(TOKEN_WITH_QUOTES_REGEXP).reject(&:empty?)
end
end end
end end
end end
...@@ -53,6 +53,14 @@ RSpec.describe Gitlab::FileFinder do ...@@ -53,6 +53,14 @@ RSpec.describe Gitlab::FileFinder do
end end
end end
context 'with white space in the path' do
it 'filters by path correctly' do
results = subject.find('directory path:"with space/README.md"')
expect(results.count).to eq(1)
end
end
it 'does not cause N+1 query' do it 'does not cause N+1 query' do
expect(Gitlab::GitalyClient).to receive(:call).at_most(10).times.and_call_original expect(Gitlab::GitalyClient).to receive(:call).at_most(10).times.and_call_original
......
...@@ -46,4 +46,22 @@ RSpec.describe Gitlab::Search::Query do ...@@ -46,4 +46,22 @@ RSpec.describe Gitlab::Search::Query do
expect(subject.filters).to all(include(negated: true)) expect(subject.filters).to all(include(negated: true))
end end
end end
context 'with filter value in quotes' do
let(:query) { '"foo bar" name:"my test script.txt"' }
it 'does not break the filter value in quotes' do
expect(subject.term).to eq('"foo bar"')
expect(subject.filters[0]).to include(name: :name, negated: false, value: "MY TEST SCRIPT.TXT")
end
end
context 'with extra white spaces between the query words' do
let(:query) { ' foo = bar name:"my test.txt"' }
it 'removes the extra whitespace between tokens' do
expect(subject.term).to eq('foo = bar')
expect(subject.filters[0]).to include(name: :name, negated: false, value: "MY TEST.TXT")
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment