From 073a269831d5bf6d9b9c55d91826d87721746bc9 Mon Sep 17 00:00:00 2001
From: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Date: Sat, 18 Jan 2014 14:16:46 +0200
Subject: [PATCH] Refactor search autocomplete. Use ajax for source

Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
---
 app/assets/javascripts/dispatcher.js.coffee   |  8 ++-
 .../javascripts/search_autocomplete.js.coffee |  9 +++-
 app/controllers/search_controller.rb          | 10 ++++
 app/helpers/search_helper.rb                  | 53 +++++++++++++------
 app/models/project.rb                         |  4 ++
 app/views/layouts/_search.html.haml           |  2 +-
 config/routes.rb                              |  1 +
 7 files changed, 65 insertions(+), 22 deletions(-)

diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index e264e281309..8ea302f256a 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -47,5 +47,9 @@ class Dispatcher
 
 
   initSearch: ->
-    autocomplete_json = $('.search-autocomplete-json').data('autocomplete-opts')
-    new SearchAutocomplete(autocomplete_json)
+    opts = $('.search-autocomplete-opts')
+    path = opts.data('autocomplete-path')
+    project_id = opts.data('autocomplete-project-id')
+    project_ref = opts.data('autocomplete-project-ref')
+
+    new SearchAutocomplete(path, project_id, project_ref)
diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee
index 3418690e109..e144dfa1d68 100644
--- a/app/assets/javascripts/search_autocomplete.js.coffee
+++ b/app/assets/javascripts/search_autocomplete.js.coffee
@@ -1,7 +1,12 @@
 class SearchAutocomplete
-  constructor: (json) ->
+  constructor: (search_autocomplete_path, project_id, project_ref) ->
+    project_id = '' unless project_id
+    project_ref = '' unless project_ref
+    query = "?project_id=" + project_id + "&project_ref=" + project_ref
+
     $("#search").autocomplete
-      source: json
+      source: search_autocomplete_path + query
+      minLength: 1
       select: (event, ui) ->
         location.href = ui.item.url
 
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index e853c22800a..0152d53f7fc 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -1,4 +1,6 @@
 class SearchController < ApplicationController
+  include SearchHelper
+
   def show
     @project = Project.find_by_id(params[:project_id]) if params[:project_id].present?
     @group = Group.find_by_id(params[:group_id]) if params[:group_id].present?
@@ -10,4 +12,12 @@ class SearchController < ApplicationController
       @search_results = Search::GlobalService.new(current_user, params).execute
     end
   end
+
+  def autocomplete
+    term = params[:term]
+    @project = Project.find(params[:project_id]) if params[:project_id].present?
+    @ref = params[:project_ref] if params[:project_ref].present?
+
+    render json: search_autocomplete_opts(term).to_json
+  end
 end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index f24156e4d85..20b50debb44 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -1,16 +1,22 @@
 module SearchHelper
-  def search_autocomplete_source
+  def search_autocomplete_opts(term)
     return unless current_user
+
+    resources_results = [
+      groups_autocomplete(term),
+      projects_autocomplete(term),
+      public_projects_autocomplete(term),
+    ].flatten
+
+    generic_results = project_autocomplete + default_autocomplete + help_autocomplete
+    generic_results.select! { |result| result[:label] =~ Regexp.new(term, "i") }
+
     [
-      groups_autocomplete,
-      projects_autocomplete,
-      public_projects_autocomplete,
-      default_autocomplete,
-      project_autocomplete,
-      help_autocomplete
+      resources_results,
+      generic_results
     ].flatten.uniq do |item|
       item[:label]
-    end.to_json
+    end
   end
 
   private
@@ -65,23 +71,36 @@ module SearchHelper
   end
 
   # Autocomplete results for the current user's groups
-  def groups_autocomplete
-    current_user.authorized_groups.map do |group|
-      { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) }
+  def groups_autocomplete(term, limit = 5)
+    current_user.authorized_groups.search(term).limit(limit).map do |group|
+      {
+        label: "group: #{simple_sanitize(group.name)}",
+        url: group_path(group)
+      }
     end
   end
 
   # Autocomplete results for the current user's projects
-  def projects_autocomplete
-    current_user.authorized_projects.non_archived.map do |p|
-      { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
+  def projects_autocomplete(term, limit = 5)
+    current_user.authorized_projects.search_by_title(term).non_archived.limit(limit).map do |p|
+      {
+        label: "project: #{simple_sanitize(p.name_with_namespace)}",
+        url: project_path(p)
+      }
     end
   end
 
   # Autocomplete results for the current user's projects
-  def public_projects_autocomplete
-    Project.public_or_internal_only(current_user).non_archived.map do |p|
-      { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
+  def public_projects_autocomplete(term, limit = 5)
+    Project.public_or_internal_only(current_user).search_by_title(term).non_archived.limit(limit).map do |p|
+      {
+        label: "project: #{simple_sanitize(p.name_with_namespace)}",
+        url: project_path(p)
+      }
     end
   end
+
+  def simple_sanitize(str)
+    Sanitize.clean(str)
+  end
 end
diff --git a/app/models/project.rb b/app/models/project.rb
index f322b5a2e25..2a792f0bbd3 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -138,6 +138,10 @@ class Project < ActiveRecord::Base
       joins(:namespace).where("projects.archived = ?", false).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%")
     end
 
+    def search_by_title query
+      where("projects.archived = ?", false).where("projects.name LIKE :query", query: "%#{query}%")
+    end
+
     def find_with_namespace(id)
       if id.include?("/")
         id = id.split("/")
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 9a0db99332a..a0e55b21c32 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -7,4 +7,4 @@
       = hidden_field_tag :search_code, true
     = hidden_field_tag :repository_ref, @ref
     = submit_tag 'Go' if ENV['RAILS_ENV'] == 'test'
-    .search-autocomplete-json.hide{:'data-autocomplete-opts' => search_autocomplete_source }
+    .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref }
diff --git a/config/routes.rb b/config/routes.rb
index 734421ede1d..315c339016b 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -6,6 +6,7 @@ Gitlab::Application.routes.draw do
   # Search
   #
   get 'search' => "search#show"
+  get 'search/autocomplete' => "search#autocomplete", as: :search_autocomplete
 
   # API
   API::API.logger Rails.logger
-- 
GitLab