Home | History | Annotate | Download | only in search
      1 package com.android.settings.intelligence.search;
      2 
      3 import android.content.Context;
      4 import android.support.annotation.NonNull;
      5 import android.util.ArrayMap;
      6 import android.util.Log;
      7 
      8 import com.android.settings.intelligence.overlay.FeatureFactory;
      9 import com.android.settings.intelligence.search.query.DatabaseResultTask;
     10 import com.android.settings.intelligence.search.query.SearchQueryTask;
     11 
     12 import java.util.ArrayList;
     13 import java.util.Collections;
     14 import java.util.List;
     15 import java.util.Map;
     16 import java.util.PriorityQueue;
     17 import java.util.concurrent.ExecutionException;
     18 import java.util.concurrent.ExecutorService;
     19 import java.util.concurrent.TimeUnit;
     20 import java.util.concurrent.TimeoutException;
     21 
     22 /**
     23  * Collects the sorted list of all setting search results.
     24  */
     25 public class SearchResultAggregator {
     26 
     27     private static final String TAG = "SearchResultAggregator";
     28 
     29     /**
     30      * Timeout for subsequent tasks to allow for fast returning tasks.
     31      * TODO(70164062): Tweak the timeout values.
     32      */
     33     private static final long SHORT_CHECK_TASK_TIMEOUT_MS = 300;
     34 
     35     private static SearchResultAggregator sResultAggregator;
     36 
     37     private SearchResultAggregator() {
     38     }
     39 
     40     public static SearchResultAggregator getInstance() {
     41         if (sResultAggregator == null) {
     42             sResultAggregator = new SearchResultAggregator();
     43         }
     44 
     45         return sResultAggregator;
     46     }
     47 
     48     @NonNull
     49     public synchronized List<? extends SearchResult> fetchResults(Context context, String query) {
     50         final SearchFeatureProvider mFeatureProvider = FeatureFactory.get(context)
     51                 .searchFeatureProvider();
     52         final ExecutorService executorService = mFeatureProvider.getExecutorService();
     53 
     54         final List<SearchQueryTask> tasks =
     55                 mFeatureProvider.getSearchQueryTasks(context, query);
     56         // Start tasks
     57         for (SearchQueryTask task : tasks) {
     58             executorService.execute(task);
     59         }
     60 
     61         // Collect results
     62         final Map<Integer, List<? extends SearchResult>> taskResults = new ArrayMap<>();
     63         final long allTasksStart = System.currentTimeMillis();
     64         for (SearchQueryTask task : tasks) {
     65             final int taskId = task.getTaskId();
     66             try {
     67                 taskResults.put(taskId,
     68                         task.get(SHORT_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
     69             } catch (TimeoutException | InterruptedException | ExecutionException e) {
     70                 Log.d(TAG, "Could not retrieve result in time: " + taskId, e);
     71                 taskResults.put(taskId, Collections.EMPTY_LIST);
     72             }
     73         }
     74 
     75         // Merge results
     76         final long mergeStartTime = System.currentTimeMillis();
     77         if (SearchFeatureProvider.DEBUG) {
     78             Log.d(TAG, "Total result loader time: " + (mergeStartTime - allTasksStart));
     79         }
     80         final List<? extends SearchResult> mergedResults = mergeSearchResults(taskResults);
     81         if (SearchFeatureProvider.DEBUG) {
     82             Log.d(TAG, "Total merge time: " + (System.currentTimeMillis() - mergeStartTime));
     83             Log.d(TAG, "Total aggregator time: " + (System.currentTimeMillis() - allTasksStart));
     84         }
     85 
     86         return mergedResults;
     87     }
     88 
     89     // TODO (b/68255021) scale the dynamic search results ranks
     90     private List<? extends SearchResult> mergeSearchResults(
     91             Map<Integer, List<? extends SearchResult>> taskResults) {
     92 
     93         final List<SearchResult> searchResults = new ArrayList<>();
     94         // First add db results as a special case
     95         searchResults.addAll(taskResults.remove(DatabaseResultTask.QUERY_WORKER_ID));
     96 
     97         // Merge the rest into result list: add everything to heap then pop them out one by one.
     98         final PriorityQueue<SearchResult> heap = new PriorityQueue<>();
     99         for (List<? extends SearchResult> taskResult : taskResults.values()) {
    100             heap.addAll(taskResult);
    101         }
    102         while (!heap.isEmpty()) {
    103             searchResults.add(heap.poll());
    104         }
    105         return searchResults;
    106     }
    107 }
    108