From 69ff65c06554476cea3440a3f0717b8d208b07da Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Fri, 11 Oct 2013 09:21:43 -0400 Subject: [PATCH] Allow users to specify which booleans are capitalized. - Resolves VUFIND-915. --- config/vufind/Summon.ini | 7 +- config/vufind/searches.ini | 3 +- .../Backend/Solr/QueryBuilder.php | 64 ++++++++++++++++--- .../Backend/Summon/QueryBuilder.php | 6 +- .../Backend/Solr/QueryBuilderTest.php | 42 +++++++++++- 5 files changed, 103 insertions(+), 19 deletions(-) diff --git a/config/vufind/Summon.ini b/config/vufind/Summon.ini index f50757c17dd..23d72420e98 100644 --- a/config/vufind/Summon.ini +++ b/config/vufind/Summon.ini @@ -4,9 +4,10 @@ ; option should be one of the options present in the [Sorting] section below. default_sort = relevance -; If this setting is true, boolean operators in searches (AND/OR/NOT) will only -; be recognized if they are ALL UPPERCASE. If set to false, they will be -; recognized regardless of case. +; If this setting is true, boolean operators in searches (AND/OR/NOT) will only +; be recognized if they are ALL UPPERCASE. If set to false, they will be +; recognized regardless of case. If set to a comma-separated list of operators +; (e.g. "AND,NOT") then only the listed operators will be case-sensitive. case_sensitive_bools = true ; These are the default recommendations modules to use when no specific setting diff --git a/config/vufind/searches.ini b/config/vufind/searches.ini index ceefb2dbb0b..42cc6eca260 100644 --- a/config/vufind/searches.ini +++ b/config/vufind/searches.ini @@ -23,7 +23,8 @@ default_limit = 20 ; If this setting is true, boolean operators in searches (AND/OR/NOT) will only ; be recognized if they are ALL UPPERCASE. If set to false, they will be -; recognized regardless of case. +; recognized regardless of case. If set to a comma-separated list of operators +; (e.g. "AND,NOT") then only the listed operators will be case-sensitive. case_sensitive_bools = true ; If this setting is true, range operators in searches ([a TO b] or {a TO b}) diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php index 292ca290d34..3e5be2dad3e 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php @@ -80,9 +80,11 @@ class QueryBuilder implements QueryBuilderInterface public $caseSensitiveRanges = true; /** - * Force boolean operators to uppercase? + * Force boolean operators to uppercase? Set to true to make all Booleans + * case-sensitive; false to make no Booleans case-sensitive; comma-separated + * string to make only certain operators case sensitive. * - * @var bool + * @var bool|string */ public $caseSensitiveBooleans = true; @@ -214,10 +216,8 @@ class QueryBuilder implements QueryBuilderInterface // Build a regular expression to detect booleans -- AND/OR/NOT surrounded // by whitespace, or NOT leading the query and followed by whitespace. $boolReg = '/((\s+(AND|OR|NOT)\s+)|^NOT\s+)/'; - if (!$this->caseSensitiveBooleans) { - $boolReg .= "i"; - } - return preg_match($boolReg, $searchString) ? true : false; + $checkString = $this->capitalizeCaseInsensitiveBooleans($searchString); + return preg_match($boolReg, $checkString) ? true : false; } /** @@ -464,9 +464,8 @@ class QueryBuilder implements QueryBuilderInterface // Force boolean operators to uppercase if we are in a // case-insensitive mode: - if (!$this->caseSensitiveBooleans) { - $searchString = $this->capitalizeBooleans($searchString); - } + $searchString = $this->capitalizeCaseInsensitiveBooleans($searchString); + // Adjust range operators if we are in a case-insensitive mode: if (!$this->caseSensitiveRanges) { $searchString = $this->capitalizeRanges($searchString); @@ -603,6 +602,48 @@ class QueryBuilder implements QueryBuilderInterface return $input; } + /** + * Convert the caseSensitiveBooleans property into an array for use with the + * capitalizeBooleans function. + * + * @return array + */ + protected function getBoolsToCap() + { + $allBools = array('AND', 'OR', 'NOT'); + if ($this->caseSensitiveBooleans === false) { + return $allBools; + } else if ($this->caseSensitiveBooleans === true) { + return array(); + } + + // Callback function to clean up configuration settings: + $callback = function ($i) { + return strtoupper(trim($i)); + }; + + // Return all values from $allBools not found in the configuration: + return array_values( + array_diff( + $allBools, + array_map($callback, explode(',', $this->caseSensitiveBooleans)) + ) + ); + } + + /** + * Wrapper around capitalizeBooleans that accounts for the caseSensitiveBooleans + * property of this class. + * + * @param string $string Search string + * + * @return string + */ + protected function capitalizeCaseInsensitiveBooleans($string) + { + return $this->capitalizeBooleans($string, $this->getBoolsToCap()); + } + /** * Capitalize boolean operators. * @@ -613,6 +654,11 @@ class QueryBuilder implements QueryBuilderInterface */ public function capitalizeBooleans($string, $bools = array('AND', 'OR', 'NOT')) { + // Short-circuit if no Booleans were selected: + if (empty($bools)) { + return $string; + } + // Load the "inside quotes" lookahead so we can use it to prevent // switching case of Boolean reserved words inside quotes, since // that can cause problems in case-sensitive fields when the reserved diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/QueryBuilder.php index f9087590974..afcce044366 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/QueryBuilder.php @@ -39,7 +39,7 @@ use VuFindSearch\ParamBag; /** * Summon QueryBuilder. (Currently extends Solr query builder for access to - * capitalizeBooleans() support method). + * capitalizeCaseInsensitiveBooleans() support method). * * @category VuFind2 * @package Search @@ -158,9 +158,7 @@ class QueryBuilder extends \VuFindSearch\Backend\Solr\QueryBuilder // Force boolean operators to uppercase if we are in a // case-insensitive mode: - if (!$this->caseSensitiveBooleans) { - $lookfor = $this->capitalizeBooleans($lookfor); - } + $lookfor = $this->capitalizeCaseInsensitiveBooleans($lookfor); // Prepend the index name, unless it's the special "AllFields" // index: diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php index bcc5632fa54..202d0d01405 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php @@ -31,7 +31,6 @@ namespace VuFindTest\Backend\Solr; use VuFindSearch\Query\Query; use VuFindSearch\Backend\Solr\QueryBuilder; -use PHPUnit_Framework_TestCase; /** * Unit tests for SOLR query builder @@ -42,7 +41,7 @@ use PHPUnit_Framework_TestCase; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org */ -class QueryBuilderTest extends PHPUnit_Framework_TestCase +class QueryBuilderTest extends \VuFindTest\Unit\TestCase { /** * Test capitalizeBooleans functionality. @@ -112,6 +111,45 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase ); } + /** + * Test getBoolsToCap(). + * + * @return void + */ + public function testGetBoolsToCap() + { + $qb = new QueryBuilder(); + + // Default behavior: do not capitalize: + $this->assertEquals( + array(), $this->callMethod($qb, 'getBoolsToCap') + ); + + // Test "capitalize all": + $qb->caseSensitiveBooleans = false; + $this->assertEquals( + array('AND', 'OR', 'NOT'), $this->callMethod($qb, 'getBoolsToCap') + ); + + // Test selective capitalization: + $qb->caseSensitiveBooleans = ' not '; + $this->assertEquals( + array('AND', 'OR'), $this->callMethod($qb, 'getBoolsToCap') + ); + $qb->caseSensitiveBooleans = 'NOT'; + $this->assertEquals( + array('AND', 'OR'), $this->callMethod($qb, 'getBoolsToCap') + ); + $qb->caseSensitiveBooleans = 'AND,OR'; + $this->assertEquals( + array('NOT'), $this->callMethod($qb, 'getBoolsToCap') + ); + $qb->caseSensitiveBooleans = 'and, or'; + $this->assertEquals( + array('NOT'), $this->callMethod($qb, 'getBoolsToCap') + ); + } + /** * Test capitalizeRanges functionality. * -- GitLab