153bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney/* 253bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * Copyright (C) 2010 The Android Open Source Project 353bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * 453bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * Licensed under the Apache License, Version 2.0 (the "License"); 553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * you may not use this file except in compliance with the License. 653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * You may obtain a copy of the License at 753bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * 853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * http://www.apache.org/licenses/LICENSE-2.0 953bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * 1053bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * Unless required by applicable law or agreed to in writing, software 1153bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * distributed under the License is distributed on an "AS IS" BASIS, 1253bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1353bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * See the License for the specific language governing permissions and 1453bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * limitations under the License. 1553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney */ 1653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 1753bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinneypackage com.android.quicksearchbox; 1853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 19a48053f425e7a65f48bcabe1e0cf0cfb54162167Mathew Inwoodimport android.text.TextUtils; 2053bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinneyimport android.util.Log; 2153bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 2253bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinneyimport java.util.HashMap; 2353bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinneyimport java.util.Iterator; 2453bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinneyimport java.util.Map; 2553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 2653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney/** 2753bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * Decides whether a given source should be queried for a given query, taking 2853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * into account the source's query threshold and query after zero results flag. 2953bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * 3053bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * This class is thread safe. 3153bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney */ 3253bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinneyclass ShouldQueryStrategy { 33b5fc08b7f16a32d3865f44b7f26d8aaa5304a2adBjorn Bringert 34b5fc08b7f16a32d3865f44b7f26d8aaa5304a2adBjorn Bringert private static final boolean DBG = false; 3553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney private static final String TAG = "QSB.ShouldQueryStrategy"; 3653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 3753bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney // The last query we've seen 3853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney private String mLastQuery = ""; 3953bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 40e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood private final Config mConfig; 41e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood 42fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert // The current implementation keeps a record of those corpora that have 43fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert // returned zero results for some prefix of the current query. mEmptyCorpora 44fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert // maps from corpus to the length of the query which returned 4553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney // zero results. When a query is shortened (e.g., by deleting characters) 46fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert // or changed entirely, mEmptyCorpora is pruned (in updateQuery) 47fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert private final HashMap<Corpus, Integer> mEmptyCorpora 48fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert = new HashMap<Corpus, Integer>(); 4953bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 50e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood public ShouldQueryStrategy(Config config) { 51e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood mConfig = config; 52e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood } 53e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood 5453bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney /** 5553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * Returns whether we should query the given source for the given query. 5653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney */ 57a65408ff321345557f93effa41395174640870ebBjorn Bringert public boolean shouldQueryCorpus(Corpus corpus, String query) { 5853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney updateQuery(query); 59e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood if (query.length() == 0 60e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood && !corpus.isWebCorpus() // always query web, to warm up connection 61e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood && !mConfig.showSuggestionsForZeroQuery()) { 62e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood return false; 63e15fe38f0142174d223bfda0cd87d1a749a85facMathew Inwood } 64fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert if (query.length() >= corpus.getQueryThreshold()) { 65fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert if (!corpus.queryAfterZeroResults() && mEmptyCorpora.containsKey(corpus)) { 66fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert if (DBG) Log.i(TAG, "Not querying " + corpus + ", returned 0 after " 67fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert + mEmptyCorpora.get(corpus)); 6853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney return false; 6953bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 7053bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney return true; 7153bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 72a48053f425e7a65f48bcabe1e0cf0cfb54162167Mathew Inwood if (DBG) Log.d(TAG, "Query too short for corpus " + corpus); 7353bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney return false; 7453bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 7553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 7653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney /** 7753bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney * Called to notify ShouldQueryStrategy when a source reports no results for a query. 7853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney */ 79a65408ff321345557f93effa41395174640870ebBjorn Bringert public void onZeroResults(Corpus corpus, String query) { 8053bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney // Make sure this result is actually for a prefix of the current query. 81a48053f425e7a65f48bcabe1e0cf0cfb54162167Mathew Inwood if (mLastQuery.startsWith(query) && !corpus.queryAfterZeroResults() 82a48053f425e7a65f48bcabe1e0cf0cfb54162167Mathew Inwood && !TextUtils.isEmpty(query)) { 83a48053f425e7a65f48bcabe1e0cf0cfb54162167Mathew Inwood if (DBG) Log.d(TAG, corpus + " returned 0 results for '" + query + "'"); 84fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert mEmptyCorpora.put(corpus, query.length()); 8553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 8653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 8753bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney 8853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney private void updateQuery(String query) { 8953bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney if (query.startsWith(mLastQuery)) { 90a65408ff321345557f93effa41395174640870ebBjorn Bringert // This is a refinement of the last query, no changes to mEmptyCorpora needed 9153bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } else if (mLastQuery.startsWith(query)) { 9253bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney // This is a widening of the last query: clear out any sources 9353bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney // that reported zero results after this query. 94fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert Iterator<Map.Entry<Corpus, Integer>> iter = mEmptyCorpora.entrySet().iterator(); 9553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney while (iter.hasNext()) { 9653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney if (iter.next().getValue() > query.length()) { 9753bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney iter.remove(); 9853bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 9953bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 10053bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } else { 10153bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney // This is a completely different query, clear everything. 102fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert mEmptyCorpora.clear(); 10353bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 104a65408ff321345557f93effa41395174640870ebBjorn Bringert mLastQuery = query; 10553bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney } 10653bf1409474f26ab0f3754ee9b4d2de901a6be00Bryan Mawhinney} 107