]> source.dussan.org Git - redmine.git/commitdiff
Cache search result ids for faster search pagination (#18631).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 20 Dec 2014 09:33:02 +0000 (09:33 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sat, 20 Dec 2014 09:33:02 +0000 (09:33 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@13770 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/search_controller.rb
config/application.rb
lib/redmine/search.rb

index ac52f42b6e6cc22aa2c5e04317b48fb526b94716..3f80b18b07370b01bb897e125845e314433e0070 100644 (file)
@@ -18,8 +18,6 @@
 class SearchController < ApplicationController
   before_filter :find_optional_project
 
-  @@search_cache_store ||= ActiveSupport::Cache.lookup_store :memory_store
-
   def index
     @question = params[:q] || ""
     @question.strip!
@@ -57,7 +55,7 @@ class SearchController < ApplicationController
 
     fetcher = Redmine::Search::Fetcher.new(
       @question, User.current, @scope, projects_to_search,
-      :all_words => @all_words, :titles_only => @titles_only
+      :all_words => @all_words, :titles_only => @titles_only, :cache => params[:page].present?
     )
 
     if fetcher.tokens.present?
index b05c5287ef9e8a21d5c99feea73447e37fa69a25..a2918a65c7311e38710f77aa9ef9e662198720bd 100644 (file)
@@ -47,6 +47,14 @@ module RedmineApp
     # Do not include all helpers
     config.action_controller.include_all_helpers = false
 
+    # Specific cache for search results, the default file store cache is not
+    # a good option as it could grow fast. A memory store (32MB max) is used
+    # as the default. If you're running multiple server processes, it's
+    # recommended to switch to a shared cache store (eg. mem_cache_store).
+    # See http://guides.rubyonrails.org/caching_with_rails.html#cache-stores
+    # for more options (same options as config.cache_store).
+    config.redmine_search_cache_store = :memory_store
+
     config.session_store :cookie_store, :key => '_redmine_session'
 
     if File.exists?(File.join(File.dirname(__FILE__), 'additional_environment.rb'))
index 42d3004b2c553bbd816a867d2e78db7387ab395e..da1d5c6f67276e0d92d25a71b5b41014e62b3337 100644 (file)
@@ -31,6 +31,18 @@ module Redmine
         search_type = search_type.to_s
         @@available_search_types << search_type unless @@available_search_types.include?(search_type)
       end
+
+      # Returns the cache store for search results
+      # Can be configured with config.redmine_search_cache_store= in config/application.rb
+      def cache_store
+        @@cache_store ||= begin
+          # if config.search_cache_store was not previously set, a no method error would be raised
+          config = Rails.application.config.redmine_search_cache_store rescue :memory_store
+          if config
+            ActiveSupport::Cache.lookup_store config
+          end
+        end
+      end
     end
 
     class Fetcher
@@ -41,6 +53,7 @@ module Redmine
         @question = question.strip
         @scope = scope
         @projects = projects
+        @cache = options.delete(:cache)
         @options = options
 
         # extract tokens from the question
@@ -52,10 +65,12 @@ module Redmine
         @tokens.slice! 5..-1
       end
 
+      # Returns the total result count
       def result_count
         result_ids.size
       end
 
+      # Returns the result count by type
       def result_count_by_type
         ret = Hash.new {|h,k| h[k] = 0}
         result_ids.group_by(&:first).each do |scope, ids|
@@ -64,6 +79,7 @@ module Redmine
         ret
       end
 
+      # Returns the results for the given offset and limit
       def results(offset, limit)
         result_ids_to_load = result_ids[offset, limit] || []
   
@@ -78,12 +94,31 @@ module Redmine
         end.compact
       end
 
+      # Returns the results ids, sorted by rank
       def result_ids
-        @ranks_and_ids ||= load_result_ids
+        @ranks_and_ids ||= load_result_ids_from_cache
       end
 
       private
 
+      def project_ids
+        Array.wrap(@projects).map(&:id)
+      end
+
+      def load_result_ids_from_cache
+        if Redmine::Search.cache_store
+          cache_key = ActiveSupport::Cache.expand_cache_key(
+            [@question, @user.id, @scope.sort, @options, project_ids.sort]
+          )
+  
+          Redmine::Search.cache_store.fetch(cache_key, :force => !@cache) do
+            load_result_ids
+          end
+        else
+          load_result_ids
+        end
+      end
+
       def load_result_ids
         ret = []
         # get all the results ranks and ids
@@ -95,6 +130,7 @@ module Redmine
         end
         # sort results, higher rank and id first
         ret.sort! {|a,b| b.last <=> a.last}
+        # only keep ids now that results are sorted
         ret.map! {|scope, r| [scope, r.last]}
         ret
       end