[gnome-shell] [search] Use 'AND' instead of 'OR' on search terms



commit a9c0dcbd6b97ecea36075b35eb9674d45d3d61a2
Author: Florian Müllner <fmuellner gnome org>
Date:   Mon Jun 7 01:09:15 2010 +0200

    [search] Use 'AND' instead of 'OR' on search terms
    
    The current search system uses the OR operator to concatenate search
    terms. While results which are matched multiple times sort before
    other matches, it is almost guaranteed that adding an additional term
    to the search increments the number of results, which is rather
    surprising.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=610955

 js/misc/docInfo.js     |   49 +++++++++------------
 js/ui/placeDisplay.js  |   26 ++++++++----
 js/ui/search.js        |   12 +++--
 src/shell-app-system.c |  108 +++++++++++++++++++++++++++++++-----------------
 4 files changed, 115 insertions(+), 80 deletions(-)
---
diff --git a/js/misc/docInfo.js b/js/misc/docInfo.js
index 0774a28..6888ced 100644
--- a/js/misc/docInfo.js
+++ b/js/misc/docInfo.js
@@ -39,15 +39,12 @@ DocInfo.prototype = {
             let term = terms[i];
             let idx = this._lowerName.indexOf(term);
             if (idx == 0) {
-                if (mtype != Search.MatchType.NONE)
-                    return Search.MatchType.MULTIPLE;
                 mtype = Search.MatchType.PREFIX;
             } else if (idx > 0) {
-                if (mtype != Search.MatchType.NONE)
-                    return Search.MatchType.MULTIPLE;
-                mtype = Search.MatchType.SUBSTRING;
+                if (mtype == Search.MatchType.NONE)
+                    mtype = Search.MatchType.SUBSTRING;
             } else {
-                continue;
+                return Search.MatchType.NONE;
             }
         }
         return mtype;
@@ -109,39 +106,35 @@ DocManager.prototype = {
         return this._docSystem.queue_existence_check(count);
     },
 
-    initialSearch: function(terms) {
-        let multipleMatches = [];
+    _searchDocs: function(items, terms) {
+        let multiplePrefixMatches = [];
         let prefixMatches = [];
+        let multipleSubtringMatches = [];
         let substringMatches = [];
-        for (let i = 0; i < this._infosByTimestamp.length; i++) {
-            let item = this._infosByTimestamp[i];
+        for (let i = 0; i < items.length; i++) {
+            let item = items[i];
             let mtype = item.matchTerms(terms);
-            if (mtype == Search.MatchType.MULTIPLE)
-                multipleMatches.push(item.uri);
+            if (mtype == Search.MatchType.MULTIPLE_PREFIX)
+                multiplePrefixMatches.push(item.uri);
             else if (mtype == Search.MatchType.PREFIX)
                 prefixMatches.push(item.uri);
+            else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
+                multipleSubtringMatches.push(item.uri);
             else if (mtype == Search.MatchType.SUBSTRING)
                 substringMatches.push(item.uri);
          }
-        return multipleMatches.concat(prefixMatches.concat(substringMatches));
+        return multiplePrefixMatches.concat(prefixMatches.concat(multipleSubtringMatches.concat(substringMatches)));
+    },
+
+    initialSearch: function(terms) {
+        return this._searchDocs(this._infosByTimestamp, terms);
     },
 
     subsearch: function(previousResults, terms) {
-        let multipleMatches = [];
-        let prefixMatches = [];
-        let substringMatches = [];
-        for (let i = 0; i < previousResults.length; i++) {
-            let uri = previousResults[i];
-            let item = this._infosByUri[uri];
-            let mtype = item.matchTerms(terms);
-            if (mtype == Search.MatchType.MULTIPLE)
-                multipleMatches.push(uri);
-            else if (mtype == Search.MatchType.PREFIX)
-                prefixMatches.push(uri);
-            else if (mtype == Search.MatchType.SUBSTRING)
-                substringMatches.push(uri);
-        }
-        return multipleMatches.concat(prefixMatches.concat(substringMatches));
+        return this._searchDocs(previousResults.map(Lang.bind(this,
+            function(url) {
+                return this._infosByUri[url];
+            })), terms);
     }
 };
 
diff --git a/js/ui/placeDisplay.js b/js/ui/placeDisplay.js
index 5bc3a6a..aaae4f7 100644
--- a/js/ui/placeDisplay.js
+++ b/js/ui/placeDisplay.js
@@ -46,10 +46,14 @@ PlaceInfo.prototype = {
         for (let i = 0; i < terms.length; i++) {
             let term = terms[i];
             let idx = this._lowerName.indexOf(term);
-            if (idx == 0)
-                return Search.MatchType.PREFIX;
-            else if (idx > 0)
-                mtype = Search.MatchType.SUBSTRING;
+            if (idx == 0) {
+                mtype = Search.MatchType.PREFIX;
+            } else if (idx > 0) {
+                if (mtype == Search.MatchType.NONE)
+                    mtype = Search.MatchType.SUBSTRING;
+            } else {
+                return Search.MatchType.NONE;
+            }
         }
         return mtype;
     },
@@ -571,8 +575,9 @@ PlaceSearchProvider.prototype = {
     },
 
     _searchPlaces: function(places, terms) {
-        let multipleResults = [];
+        let multiplePrefixResults = [];
         let prefixResults = [];
+        let multipleSubstringResults = [];
         let substringResults = [];
 
         terms = terms.map(String.toLowerCase);
@@ -580,17 +585,20 @@ PlaceSearchProvider.prototype = {
         for (let i = 0; i < places.length; i++) {
             let place = places[i];
             let mtype = place.matchTerms(terms);
-            if (mtype == Search.MatchType.MULTIPLE)
-                multipleResults.push(place.id);
+            if (mtype == Search.MatchType.MULTIPLE_PREFIX)
+                multiplePrefixResults.push(place.id);
             else if (mtype == Search.MatchType.PREFIX)
                 prefixResults.push(place.id);
+            else if (mtype == Search.MatchType.MULTIPLE_SUBSTRING)
+                multipleSubstringResults.push(place.id);
             else if (mtype == Search.MatchType.SUBSTRING)
                 substringResults.push(place.id);
         }
-        multipleResults.sort(this._compareResultMeta);
+        multiplePrefixResults.sort(this._compareResultMeta);
         prefixResults.sort(this._compareResultMeta);
+        multipleSubstringResults.sort(this._compareResultMeta);
         substringResults.sort(this._compareResultMeta);
-        return multipleResults.concat(prefixResults.concat(substringResults));
+        return multiplePrefixResults.concat(prefixResults.concat(multipleSubstringResults.concat(substringResults)));
     },
 
     getInitialResultSet: function(terms) {
diff --git a/js/ui/search.js b/js/ui/search.js
index fea4198..2624f09 100644
--- a/js/ui/search.js
+++ b/js/ui/search.js
@@ -9,9 +9,10 @@ const RESULT_ICON_SIZE = 24;
 // implementations.
 const MatchType = {
     NONE: 0,
-    MULTIPLE: 1,
-    PREFIX: 2,
-    SUBSTRING: 3
+    SUBSTRING: 1,
+    MULTIPLE_SUBSTRING: 2,
+    PREFIX: 3,
+    MULTIPLE_PREFIX: 4
 };
 
 function SearchResultDisplay(provider) {
@@ -107,7 +108,7 @@ SearchProvider.prototype = {
 
     /**
      * getInitialResultSet:
-     * @terms: Array of search terms, treated as logical OR
+     * @terms: Array of search terms, treated as logical AND
      *
      * Called when the user first begins a search (most likely
      * therefore a single term of length one or two), or when
@@ -119,7 +120,8 @@ SearchProvider.prototype = {
      * item.  Ordering of returned results is up to the discretion of the provider,
      * but you should follow these heruistics:
      *
-     *  * Put items which match multiple search terms before single matches
+     *  * Put items where the term matches multiple criteria (e.g. name and
+     *    description) before single matches
      *  * Put items which match on a prefix before non-prefix substring matches
      *
      * This function should be fast; do not perform unindexed full-text searches
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
index f2813ea..ad37767 100644
--- a/src/shell-app-system.c
+++ b/src/shell-app-system.c
@@ -663,9 +663,10 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
 
 typedef enum {
   MATCH_NONE,
-  MATCH_MULTIPLE, /* Matches multiple terms */
+  MATCH_SUBSTRING, /* Not prefix, substring */
+  MATCH_MULTIPLE_SUBSTRING, /* Matches multiple criteria with substrings */
   MATCH_PREFIX, /* Strict prefix */
-  MATCH_SUBSTRING /* Not prefix, substring */
+  MATCH_MULTIPLE_PREFIX, /* Matches multiple criteria, at least one prefix */
 } ShellAppInfoSearchMatch;
 
 static char *
@@ -733,36 +734,45 @@ shell_app_info_match_terms (ShellAppInfo  *info,
   match = MATCH_NONE;
   for (iter = terms; iter; iter = iter->next)
     {
+      ShellAppInfoSearchMatch current_match;
       const char *term = iter->data;
       const char *p;
 
+      current_match = MATCH_NONE;
+
       p = strstr (info->casefolded_name, term);
       if (p == info->casefolded_name)
-        {
-          if (match != MATCH_NONE)
-            return MATCH_MULTIPLE;
-          else
-            match = MATCH_PREFIX;
-         }
+        current_match = MATCH_PREFIX;
       else if (p != NULL)
-        match = MATCH_SUBSTRING;
+        current_match = MATCH_SUBSTRING;
 
       p = strstr (info->casefolded_exec, term);
-      if (p == info->casefolded_exec)
+      if (p != NULL)
         {
-          if (match != MATCH_NONE)
-            return MATCH_MULTIPLE;
-          else
-            match = MATCH_PREFIX;
-         }
-      else if (p != NULL)
-        match = MATCH_SUBSTRING;
+          if (p == info->casefolded_exec)
+            current_match = (current_match == MATCH_NONE) ? MATCH_PREFIX
+                                                          : MATCH_MULTIPLE_PREFIX;
+          else if (current_match < MATCH_PREFIX)
+            current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
+                                                          : MATCH_MULTIPLE_SUBSTRING;
+        }
 
-      if (!info->casefolded_description)
-        continue;
-      p = strstr (info->casefolded_description, term);
-      if (p != NULL)
-        match = MATCH_SUBSTRING;
+      if (info->casefolded_description && current_match < MATCH_PREFIX)
+        {
+          /* Only do substring matches, as prefix matches are not meaningful
+           * enough for descriptions
+           */
+          p = strstr (info->casefolded_description, term);
+          if (p != NULL)
+            current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
+                                                          : MATCH_MULTIPLE_SUBSTRING;
+        }
+
+      if (current_match == MATCH_NONE)
+        return current_match;
+
+      if (current_match > match)
+        match = current_match;
     }
   return match;
 }
@@ -788,14 +798,24 @@ shell_app_info_compare (gconstpointer a,
 
 static GSList *
 sort_and_concat_results (ShellAppSystem *system,
-                         GSList         *multiple_matches,
+                         GSList         *multiple_prefix_matches,
                          GSList         *prefix_matches,
+                         GSList         *multiple_substring_matches,
                          GSList         *substring_matches)
 {
-  multiple_matches = g_slist_sort_with_data (multiple_matches, shell_app_info_compare, system);
-  prefix_matches = g_slist_sort_with_data (prefix_matches, shell_app_info_compare, system);
-  substring_matches = g_slist_sort_with_data (substring_matches, shell_app_info_compare, system);
-  return g_slist_concat (multiple_matches, g_slist_concat (prefix_matches, substring_matches));
+  multiple_prefix_matches = g_slist_sort_with_data (multiple_prefix_matches,
+                                                    shell_app_info_compare,
+                                                    system);
+  prefix_matches = g_slist_sort_with_data (prefix_matches,
+                                           shell_app_info_compare,
+                                           system);
+  multiple_substring_matches = g_slist_sort_with_data (multiple_substring_matches,
+                                                       shell_app_info_compare,
+                                                       system);
+  substring_matches = g_slist_sort_with_data (substring_matches,
+                                              shell_app_info_compare,
+                                              system);
+  return g_slist_concat (multiple_prefix_matches, g_slist_concat (prefix_matches, g_slist_concat (multiple_substring_matches, substring_matches)));
 }
 
 /**
@@ -821,8 +841,9 @@ static inline void
 shell_app_system_do_match (ShellAppSystem   *system,
                            ShellAppInfo     *info,
                            GSList           *terms,
-                           GSList          **multiple_results,
+                           GSList          **multiple_prefix_results,
                            GSList          **prefix_results,
+                           GSList          **multiple_substring_results,
                            GSList          **substring_results)
 {
   const char *id = shell_app_info_get_id (info);
@@ -836,12 +857,17 @@ shell_app_system_do_match (ShellAppSystem   *system,
     {
       case MATCH_NONE:
         break;
-      case MATCH_MULTIPLE:
-        *multiple_results = g_slist_prepend (*multiple_results, (char *) id);
+      case MATCH_MULTIPLE_PREFIX:
+        *multiple_prefix_results = g_slist_prepend (*multiple_prefix_results,
+                                                    (char *) id);
         break;
       case MATCH_PREFIX:
         *prefix_results = g_slist_prepend (*prefix_results, (char *) id);
         break;
+      case MATCH_MULTIPLE_SUBSTRING:
+        *multiple_substring_results = g_slist_prepend (*multiple_substring_results,
+                                                    (char *) id);
+        break;
       case MATCH_SUBSTRING:
         *substring_results = g_slist_prepend (*substring_results, (char *) id);
         break;
@@ -853,8 +879,9 @@ shell_app_system_initial_search_internal (ShellAppSystem  *self,
                                           GSList          *terms,
                                           GSList          *source)
 {
-  GSList *multiple_results = NULL;
+  GSList *multiple_prefix_results = NULL;
   GSList *prefix_results = NULL;
+  GSList *multiple_subtring_results = NULL;
   GSList *substring_results = NULL;
   GSList *iter;
   GSList *normalized_terms = normalize_terms (terms);
@@ -863,19 +890,21 @@ shell_app_system_initial_search_internal (ShellAppSystem  *self,
     {
       ShellAppInfo *info = iter->data;
 
-      shell_app_system_do_match (self, info, normalized_terms, &multiple_results, &prefix_results, &substring_results);
+      shell_app_system_do_match (self, info, normalized_terms,
+                                 &multiple_prefix_results, &prefix_results,
+                                 &multiple_subtring_results, &substring_results);
     }
   g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
   g_slist_free (normalized_terms);
 
-  return sort_and_concat_results (self, multiple_results, prefix_results, substring_results);
+  return sort_and_concat_results (self, multiple_prefix_results, prefix_results, multiple_subtring_results, substring_results);
 }
 
 /**
  * shell_app_system_initial_search:
  * @self: A #ShellAppSystem
  * @prefs: %TRUE if we should search preferences instead of apps
- * @terms: (element-type utf8): List of terms, logical OR
+ * @terms: (element-type utf8): List of terms, logical AND
  *
  * Search through applications for the given search terms.  Note that returned
  * strings are only valid until a return to the main loop.
@@ -896,7 +925,7 @@ shell_app_system_initial_search (ShellAppSystem  *self,
  * @self: A #ShellAppSystem
  * @prefs: %TRUE if we should search preferences instead of apps
  * @previous_results: (element-type utf8): List of previous results
- * @terms: (element-type utf8): List of terms, logical OR
+ * @terms: (element-type utf8): List of terms, logical AND
  *
  * Search through a previous result set; for more information, see
  * js/ui/search.js.  Note the value of @prefs must be
@@ -912,8 +941,9 @@ shell_app_system_subsearch (ShellAppSystem   *system,
                             GSList           *terms)
 {
   GSList *iter;
-  GSList *multiple_results = NULL;
+  GSList *multiple_prefix_results = NULL;
   GSList *prefix_results = NULL;
+  GSList *multiple_substring_results = NULL;
   GSList *substring_results = NULL;
   GSList *normalized_terms = normalize_terms (terms);
 
@@ -931,7 +961,9 @@ shell_app_system_subsearch (ShellAppSystem   *system,
       if (!info)
         continue;
 
-      shell_app_system_do_match (system, info, normalized_terms, &multiple_results, &prefix_results, &substring_results);
+      shell_app_system_do_match (system, info, normalized_terms,
+                                 &multiple_prefix_results, &prefix_results,
+                                 &multiple_substring_results, &substring_results);
     }
   g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
   g_slist_free (normalized_terms);
@@ -939,7 +971,7 @@ shell_app_system_subsearch (ShellAppSystem   *system,
   /* Note that a shorter term might have matched as a prefix, but
      when extended only as a substring, so we have to redo the
      sort rather than reusing the existing ordering */
-  return sort_and_concat_results (system, multiple_results, prefix_results, substring_results);
+  return sort_and_concat_results (system, multiple_prefix_results, prefix_results, multiple_substring_results, substring_results);
 }
 
 const char *



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]