Optimize subsearches

This is probably not the biggest optimization that needs to be
made at least for application searching, but we can optimize the
case where we're going from a search of "fi" to "fire" by just
re-searching the list of things we already had that matched "fi"
instead of looping over everything.

https://bugzilla.gnome.org/show_bug.cgi?id=596119
This commit is contained in:
Colin Walters 2009-09-23 16:16:38 -04:00
parent 3564d78d30
commit 32ef951fe0

View File

@ -17,6 +17,10 @@ const DND = imports.ui.dnd;
const Link = imports.ui.link; const Link = imports.ui.link;
const Main = imports.ui.main; const Main = imports.ui.main;
const RedisplayFlags = { NONE: 0,
RESET_CONTROLS: 1 << 0,
SUBSEARCH: 1 << 1 };
const ITEM_DISPLAY_NAME_COLOR = new Clutter.Color(); const ITEM_DISPLAY_NAME_COLOR = new Clutter.Color();
ITEM_DISPLAY_NAME_COLOR.from_pixel(0xffffffff); ITEM_DISPLAY_NAME_COLOR.from_pixel(0xffffffff);
const ITEM_DISPLAY_DESCRIPTION_COLOR = new Clutter.Color(); const ITEM_DISPLAY_DESCRIPTION_COLOR = new Clutter.Color();
@ -357,8 +361,14 @@ GenericDisplay.prototype = {
// Sets the search string and displays the matching items. // Sets the search string and displays the matching items.
setSearch: function(text) { setSearch: function(text) {
this._search = text.toLowerCase(); let lowertext = text.toLowerCase();
this._redisplay(true); let flags = RedisplayFlags.RESET_CONTROLS;
if (this._search != '') {
if (lowertext.indexOf(this._search) == 0)
flags |= RedisplayFlags.SUBSEARCH;
}
this._search = lowertext;
this._redisplay(flags);
}, },
// Launches the item that is currently selected, closing the Overview // Launches the item that is currently selected, closing the Overview
@ -439,7 +449,7 @@ GenericDisplay.prototype = {
// Load the initial state // Load the initial state
load: function() { load: function() {
this._redisplay(true); this._redisplay(RedisplayFlags.RESET_CONTROLS);
}, },
// Should be called when the display is closed // Should be called when the display is closed
@ -587,19 +597,23 @@ GenericDisplay.prototype = {
/* /*
* Updates the displayed items, applying the search string if one exists. * Updates the displayed items, applying the search string if one exists.
* * @flags: Flags controlling redisplay behavior as follows:
* resetPage - indicates if the page selection should be reset when displaying the matching results. * RESET_CONTROLS - indicates if the page selection should be reset when displaying the matching results.
* We reset the page selection when the change in results was initiated by the user by * We reset the page selection when the change in results was initiated by the user by
* entering a different search criteria or by viewing the results list in a different * entering a different search criteria or by viewing the results list in a different
* size mode, but we keep the page selection the same if the results got updated on * size mode, but we keep the page selection the same if the results got updated on
* their own while the user was browsing through the result pages. * their own while the user was browsing through the result pages.
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
* one, which implies we only need to re-search through previous results.
*/ */
_redisplay: function(resetPage) { _redisplay: function(flags) {
let resetPage = flags & RedisplayFlags.RESET_CONTROLS;
let isSubSearch = flags & RedisplayFlags.SUBSEARCH;
this._refreshCache(); this._refreshCache();
if (!this._filterActive()) if (!this._filterActive())
this._setDefaultList(); this._setDefaultList();
else else
this._doSearchFilter(); this._doSearchFilter(isSubSearch);
if (resetPage) if (resetPage)
this._list.page = 0; this._list.page = 0;
@ -643,35 +657,49 @@ GenericDisplay.prototype = {
//// Private methods //// //// Private methods ////
_getSearchMatchedItems: function() { _getItemSearchScore: function(itemId, terms) {
let matchedItemsForSearch = {}; let item = this._allItems[itemId];
let score = 0;
for (let i = 0; i < terms.length; i++) {
let term = terms[i];
if (this._isInfoMatching(item, term)) {
score++;
}
}
return score;
},
_getSearchMatchedItems: function(isSubSearch) {
// Break the search up into terms, and search for each // Break the search up into terms, and search for each
// individual term. Keep track of the number of terms // individual term. Keep track of the number of terms
// each item matched. // each item matched.
let terms = this._search.split(/\s+/); let terms = this._search.split(/\s+/);
for (let i = 0; i < terms.length; i++) { let matchScores = {};
let term = terms[i];
for (itemId in this._allItems) { if (isSubSearch) {
let item = this._allItems[itemId]; for (let i = 0; i < this._matchedItems.length; i++) {
if (this._isInfoMatching(item, term)) { let itemId = this._matchedItems[i];
let count = matchedItemsForSearch[itemId]; let score = this._getItemSearchScore(itemId, terms);
if (!count) if (score > 0)
count = 0; matchScores[itemId] = score;
count += 1; }
matchedItemsForSearch[itemId] = count; } else {
} for (let itemId in this._allItems) {
let score = this._getItemSearchScore(itemId, terms);
if (score > 0)
matchScores[itemId] = score;
} }
} }
return matchedItemsForSearch; return matchScores;
}, },
// Applies the search string to the list of items to find matches, // Applies the search string to the list of items to find matches,
// and displays the matching items. // and displays the matching items.
_doSearchFilter: function() { _doSearchFilter: function(isSubSearch) {
let matchedItemsForSearch; let matchedItemsForSearch;
if (this._filterActive()) { if (this._filterActive()) {
matchedItemsForSearch = this._getSearchMatchedItems(); matchedItemsForSearch = this._getSearchMatchedItems(isSubSearch);
} else { } else {
matchedItemsForSearch = {}; matchedItemsForSearch = {};
for (let itemId in this._allItems) { for (let itemId in this._allItems) {