diff options
Diffstat (limited to 'lib/gitlab/file_finder.rb')
-rw-r--r-- | lib/gitlab/file_finder.rb | 109 |
1 files changed, 80 insertions, 29 deletions
diff --git a/lib/gitlab/file_finder.rb b/lib/gitlab/file_finder.rb index b4db3f93c9c..f59ca45e656 100644 --- a/lib/gitlab/file_finder.rb +++ b/lib/gitlab/file_finder.rb @@ -4,64 +4,115 @@ # the result is joined and sorted by file name module Gitlab class FileFinder - BATCH_SIZE = 100 + class BaseMatch + attr_reader :match - attr_reader :project, :ref + def initialize(match) + @match = match + end + end + + class ContentMatch < BaseMatch + FILENAME_REGEXP = /\A(?<ref>[^:]*):(?<filename>[^\x00]*)\x00/.freeze + + def filename + # FIXME - use const, match may not be found + # FIXME - check if utf8 is needed + @filename ||= match.match(FILENAME_REGEXP)[:filename] + end + + def found_blob(project, ref) + Gitlab::ProjectSearchResults.parse_search_result(match, project) + end + end + + class FilenameMatch < BaseMatch + attr_accessor :blob, :ref + + def filename + match + end + + def found_blob(project, ref) + Gitlab::SearchResults::FoundBlob.new( + id: blob.id, + filename: blob.path, + basename: File.basename(blob.path, File.extname(blob.path)), + ref: ref, + startline: 1, + data: blob.data, + project: project + ) + end + end + + attr_reader :project, :ref, :page, :per_page, :query delegate :repository, to: :project - def initialize(project, ref) + def initialize(project, ref, page: 1, per_page: 20) @project = project @ref = ref + @page = page + @per_page = [per_page.to_i, 100].min end def find(query) - query = Gitlab::Search::Query.new(query) do + @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) + results = Kaminari.paginate_array(find_by_filename + find_by_content).page(page).per(per_page) - already_found = Set.new(by_content.map(&:filename)) - by_filename = find_by_filename(query.term, except: already_found) + # convert to blob only items on the selected page of array + replace_filename_blobs!(results) + replace_content_blobs!(results) - files = (by_content + by_filename) - .sort_by(&:filename) + results.each_with_index { |blob, idx| results[idx] = [blob.filename, blob] } - query.filter_results(files).map { |blob| [blob.filename, blob] } + results end private - def find_by_content(query) - results = repository.search_files_by_content(query, ref).first(BATCH_SIZE) - results.map { |result| Gitlab::ProjectSearchResults.parse_search_result(result, project) } + def replace_content_blobs!(array) + array.each_with_index do |item, idx| + next unless item.is_a?(ContentMatch) + array[idx] = item.found_blob(project, ref) + end end - def find_by_filename(query, except: []) - filenames = search_filenames(query, except) + def replace_filename_blobs!(array) + filenames = array.select { |a| a.is_a?(FilenameMatch) }.map(&:match) + blobs = blobs(filenames) - blobs(filenames).map do |blob| - Gitlab::SearchResults::FoundBlob.new( - id: blob.id, - filename: blob.path, - basename: File.basename(blob.path, File.extname(blob.path)), - ref: ref, - startline: 1, - data: blob.data, - project: project - ) + array.each_with_index do |item, idx| + next unless item.is_a?(FilenameMatch) + item.blob = blobs.find { |b| b.path == item.match } + array[idx] = item.found_blob(project, ref) end end - def search_filenames(query, except) - filenames = repository.search_files_by_name(query, ref).first(BATCH_SIZE) + def find_by_content + files = repository.search_files_by_content(query.term, ref) + + filter_matches(ContentMatch, files) + end + + def find_by_filename + filenames = repository.search_files_by_name(query.term, ref) + + filter_matches(FilenameMatch, filenames) + end + + def filter_matches(match_class, matches) + matches = matches.map { |match| match_class.new(match) } - filenames.delete_if { |filename| except.include?(filename) } unless except.empty? + matches = query.filter_results(matches) if query.filters.any? - filenames + matches end def blob_refs(filenames) |