diff --git a/config/vufind/Summon.ini b/config/vufind/Summon.ini index 36434aaf9a6c6257b962eceb9810349283a5b487..22c12f627a24900c72994e39dea1a3a18654870f 100644 --- a/config/vufind/Summon.ini +++ b/config/vufind/Summon.ini @@ -23,6 +23,7 @@ default_top_recommend[] = SummonBestBets ;default_top_recommend[] = TopFacets:FacetsTop:Summon default_side_recommend[] = SideFacets:Facets:CheckboxFacets:Summon default_noresults_recommend[] = SwitchType +default_noresults_recommend[] = SwitchQuery:Summon ; Set this to true in order to highlight keywords from the search query when they ; appear in fields displayed in search results (may not apply to all fields). diff --git a/config/vufind/searches.ini b/config/vufind/searches.ini index 7f076760631ad843c300372d3be127eaae9a4f24..ceefb2dbb0b3532e0cdaef8fa26a60d69729115f 100644 --- a/config/vufind/searches.ini +++ b/config/vufind/searches.ini @@ -41,6 +41,7 @@ case_sensitive_ranges = true default_top_recommend[] = TopFacets:ResultsTop default_side_recommend[] = SideFacets:Results:CheckboxFacets default_noresults_recommend[] = SwitchType +default_noresults_recommend[] = SwitchQuery ; Set this to true in order to highlight keywords from the search query when they ; appear in fields displayed in search results. @@ -270,6 +271,12 @@ CallNumber = callnumber ; ; Available modules recommended for use in the "no results" area: ; +; SwitchQuery:[backend]:[checks to skip] +; This module analyzes the user's query and offers suggestions for ways to +; improve it. [backend] is the name of the search backend currently in use, +; which will help with accurate analysis (default = Solr). [checks to skip] +; is a comma-separated list of checks to disable; see the check*() methods +; in the module's code for a complete list of available checks. ; SwitchType:[field]:[field description] ; If the current search type is not the same as [field], display a link ; suggesting that the user try switching to [field]. [field description] diff --git a/languages/en-gb.ini b/languages/en-gb.ini index 882696344bb3a1b8f0074ca227192399ae2fdba0..66f061b9f226e3bbcda99b5644c8c87a81c883b8 100644 --- a/languages/en-gb.ini +++ b/languages/en-gb.ini @@ -562,8 +562,8 @@ Publication Frequency = "Publication Frequency" Published = Published Published in = "Published in" QR Code = "QR Code" -qrcode_show = "Show QR Code" qrcode_hide = "Hide QR Code" +qrcode_show = "Show QR Code" query time = "query time" Range = Range Range slider = "Range slider" @@ -681,6 +681,10 @@ Summon Results = "Summon Results" summon_database_recommendations = "You may find additional resources here:" Supplied by Amazon = "Supplied by Amazon" Switch view to = "Switch view to" +switchquery_intro = "You may be able to get more results by adjusting your search query." +switchquery_unwantedbools = "The words AND, OR and NOT may confuse the search; try adding quotes" +switchquery_unwantedquotes = "Removing quotes may allow a broader search" +switchquery_wildcard = "Adding a wildcard symbol may retrieve word variants" System Unavailable = "System Unavailable" Table of Contents = "Table of Contents" Table of Contents unavailable = "Table of Contents unavailable" diff --git a/languages/en.ini b/languages/en.ini index 3831e015a93a08ad46cd0de2004bfe33cdd3e790..f8c6368e2d8c2c06f8ff85e332665115dfa26859 100644 --- a/languages/en.ini +++ b/languages/en.ini @@ -562,8 +562,8 @@ Publication Frequency = "Publication Frequency" Published = Published Published in = "Published in" QR Code = "QR Code" -qrcode_show = "Show QR Code" qrcode_hide = "Hide QR Code" +qrcode_show = "Show QR Code" query time = "query time" Range = Range Range slider = "Range slider" @@ -681,6 +681,10 @@ Summon Results = "Summon Results" summon_database_recommendations = "You may find additional resources here:" Supplied by Amazon = "Supplied by Amazon" Switch view to = "Switch view to" +switchquery_intro = "You may be able to get more results by adjusting your search query." +switchquery_unwantedbools = "The words AND, OR and NOT may confuse the search; try adding quotes" +switchquery_unwantedquotes = "Removing quotes may allow a broader search" +switchquery_wildcard = "Adding a wildcard symbol may retrieve word variants" System Unavailable = "System Unavailable" Table of Contents = "Table of Contents" Table of Contents unavailable = "Table of Contents unavailable" diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index e4a6f0da1443aa19c399e6443fcef516504bba14..c199341bccc69f3235865b9a49d17c91bddd50e7 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -618,6 +618,11 @@ $config = array( $sm->getServiceLocator()->get('VuFind\SearchResultsPluginManager') ); }, + 'switchquery' => function ($sm) { + return new \VuFind\Recommend\SwitchQuery( + $sm->getServiceLocator()->get('VuFind\Search\BackendManager') + ); + }, 'topfacets' => function ($sm) { return new \VuFind\Recommend\TopFacets( $sm->getServiceLocator()->get('VuFind\Config') diff --git a/module/VuFind/src/VuFind/Recommend/SwitchQuery.php b/module/VuFind/src/VuFind/Recommend/SwitchQuery.php new file mode 100644 index 0000000000000000000000000000000000000000..bdf882a0a52af458cd5bda20d0c7e07bf4879e8c --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/SwitchQuery.php @@ -0,0 +1,247 @@ +<?php +/** + * SwitchQuery Recommendations Module + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:recommendation_modules Wiki + */ +namespace VuFind\Recommend; +use VuFind\Search\BackendManager; + +/** + * SwitchQuery Recommendations Module + * + * This class recommends adjusting your search query to yield better results. + * + * @category VuFind2 + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:recommendation_modules Wiki + */ +class SwitchQuery implements RecommendInterface +{ + /** + * Search backend identifier that we are working with. + * + * @var string + */ + protected $backend; + + /** + * Search backend plugin manager. + * + * @var BackendManager + */ + protected $backendManager; + + /** + * Improved query suggestions. + * + * @var array + */ + protected $suggestions = array(); + + /** + * Names of checks that should be skipped. These should correspond + * with check method names -- e.g. to skip the check found in the + * checkWildcard() method, you would put 'wildcard' into this array. + * + * @var array + */ + protected $skipChecks = array(); + + /** + * Constructor + * + * @param BackendManager $backendManager Search backend plugin manager + */ + public function __construct(BackendManager $backendManager) + { + $this->backendManager = $backendManager; + } + + /** + * setConfig + * + * Store the configuration of the recommendation module. + * + * @param string $settings Settings from searches.ini. + * + * @return void + */ + public function setConfig($settings) + { + $params = explode(':', $settings); + $this->backend = !empty($params[0]) ? $params[0] : 'Solr'; + $callback = function ($i) { + return trim(strtolower($i)); + }; + $this->skipChecks = !empty($params[1]) + ? array_map($callback, explode(',', $params[1])) : array(); + } + + /** + * init + * + * Called at the end of the Search Params objects' initFromRequest() method. + * This method is responsible for setting search parameters needed by the + * recommendation module and for reading any existing search parameters that may + * be needed. + * + * @param \VuFind\Search\Base\Params $params Search parameter object + * @param \Zend\StdLib\Parameters $request Parameter object representing user + * request. + * + * @return void + */ + public function init($params, $request) + { + } + + /** + * process + * + * Called after the Search Results object has performed its main search. This + * may be used to extract necessary information from the Search Results object + * or to perform completely unrelated processing. + * + * @param \VuFind\Search\Base\Results $results Search results object + * + * @return void + */ + public function process($results) + { + $this->results = $results; + + // We can't currently deal with advanced searches: + if ($this->results->getParams()->getSearchType() == 'advanced') { + return; + } + + // Get the query to manipulate: + $query = $this->results->getParams()->getDisplayQuery(); + + // Perform all checks (based on naming convention): + $methods = get_class_methods($this); + $checks = array(); + foreach ($methods as $method) { + if (substr($method, 0, 5) == 'check') { + $currentCheck = strtolower(substr($method, 5)); + if (!in_array($currentCheck, $this->skipChecks)) { + $result = $this->$method($query); + if ($result) { + $this->suggestions['switchquery_' . $currentCheck] = $result; + } + } + } + } + } + + /** + * Does the query contain terms that are being treated as boolean operators, + * perhaps unintentionally? + * + * @param string $query Query to check + * + * @return string|bool + */ + protected function checkUnwantedBools($query) + { + $query = trim($query); + $qb = $this->getQueryBuilder(); + if (!$qb || !is_callable(array($qb, 'containsBooleans')) + || !$qb->containsBooleans($query) + || (substr($query, 0, 1) == '"' && substr($query, -1) == '"') + ) { + return false; + } + return '"' . addcslashes($query, '"') . '"'; + } + + /** + * Would removing quotes help? + * + * @param string $query Query to check + * + * @return string|bool + */ + protected function checkUnwantedQuotes($query) + { + // Remove escaped quotes as they are of no consequence: + $query = str_replace('\"', ' ', $query); + return (strpos($query, '"') === false) + ? false : str_replace('"', ' ', $query); + } + + /** + * Will adding a wildcard help? + * + * @param string $query Query to check + * + * @return string|bool + */ + protected function checkWildcard($query) + { + // Don't pile wildcards on phrases: + if (substr($query, -1) == '"') { + return false; + } + $query = trim($query, ' ?'); + return (substr($query, -1) != '*') ? $query . '*' : false; + } + + /** + * Extract a query builder from the search backend. + * + * @return object + */ + protected function getQueryBuilder() + { + $backend = $this->backendManager->get($this->backend); + return is_callable(array($backend, 'getQueryBuilder')) + ? $backend->getQueryBuilder() : false; + } + + /** + * Get results stored in the object. + * + * @return \VuFind\Search\Base\Results + */ + public function getResults() + { + return $this->results; + } + + /** + * Get the new search handler, or false if it does not apply. + * + * @return array + */ + public function getSuggestions() + { + return $this->suggestions; + } +} \ No newline at end of file diff --git a/themes/blueprint/templates/Recommend/SwitchQuery.phtml b/themes/blueprint/templates/Recommend/SwitchQuery.phtml new file mode 100644 index 0000000000000000000000000000000000000000..bf0db3cc02e6a14c61a2b2d8af3a5d68d7006fa5 --- /dev/null +++ b/themes/blueprint/templates/Recommend/SwitchQuery.phtml @@ -0,0 +1,10 @@ +<? $suggestions = $this->recommend->getSuggestions(); if (!empty($suggestions)): ?> + <div class="info"> + <p><?=$this->transEsc('switchquery_intro')?></p> + <ul> + <? foreach ($suggestions as $desc => $query): ?> + <li><?=$this->transEsc($desc)?>: <a href="<?=$this->recommend->getResults()->getUrlQuery()->setSearchTerms($query)?>"><?=$this->escapeHtml($query)?></a>.</li> + <? endforeach; ?> + </ul> + </div> +<? endif; ?> \ No newline at end of file diff --git a/themes/bootstrap/templates/Recommend/SwitchQuery.phtml b/themes/bootstrap/templates/Recommend/SwitchQuery.phtml new file mode 100644 index 0000000000000000000000000000000000000000..c4b98e50976ddd55d7d218aa1c025f1156ccbabd --- /dev/null +++ b/themes/bootstrap/templates/Recommend/SwitchQuery.phtml @@ -0,0 +1,10 @@ +<? $suggestions = $this->recommend->getSuggestions(); if (!empty($suggestions)): ?> + <div class="alert alert-info"> + <p><?=$this->transEsc('switchquery_intro')?></p> + <ul> + <? foreach ($suggestions as $desc => $query): ?> + <li><?=$this->transEsc($desc)?>: <a href="<?=$this->recommend->getResults()->getUrlQuery()->setSearchTerms($query)?>"><?=$this->escapeHtml($query)?></a>.</li> + <? endforeach; ?> + </ul> + </div> +<? endif; ?> \ No newline at end of file