diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php index 1055b1b9c2cd427a4d404d834fa9e530674296c9..22b59b17840290c3daea98c99d15a1fa7e261fa4 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php @@ -154,19 +154,24 @@ class QueryBuilder implements QueryBuilderInterface $params->set('hl.q', $oldString); } } - } else { - if ($handler->hasDismax()) { - $params->set('qf', implode(' ', $handler->getDismaxFields())); - $params->set('qt', $handler->getDismaxHandler()); - foreach ($handler->getDismaxParams() as $param) { - $params->add(reset($param), next($param)); - } - if ($handler->hasFilterQuery()) { - $params->add('fq', $handler->getFilterQuery()); - } - } else { - $string = $handler->createSimpleQueryString($string); + } else if ($handler->hasDismax()) { + // If we're using extended dismax, we'll miss out on the question + // mark fix in createAdvancedInnerSearchString(), so we should + // apply it here. If other query munges arise that are valuable + // to both dismax and edismax, we should add a wrapper function + // around them and call it from here instead of this one very + // specific check. + $string = $this->fixTrailingQuestionMarks($string); + $params->set('qf', implode(' ', $handler->getDismaxFields())); + $params->set('qt', $handler->getDismaxHandler()); + foreach ($handler->getDismaxParams() as $param) { + $params->add(reset($param), next($param)); } + if ($handler->hasFilterQuery()) { + $params->add('fq', $handler->getFilterQuery()); + } + } else { + $string = $handler->createSimpleQueryString($string); } } $params->set('q', $string); @@ -362,6 +367,28 @@ class QueryBuilder implements QueryBuilderInterface } } + /** + * If the query ends in a non-escaped question mark, the user may not really + * intend to use the question mark as a wildcard -- let's account for that + * possibility. + * + * @param string $string Search query to adjust + * + * @return string + */ + protected function fixTrailingQuestionMarks($string) + { + if (substr($string, -1) == '?' && substr($string, -2) != '\?') { + // Make sure all question marks are properly escaped (first unescape + // any that are already escaped to prevent double-escapes, then escape + // all of them): + $strippedQuery + = str_replace('?', '\?', str_replace('\?', '?', $string)); + $string = "({$string}) OR (" . $strippedQuery . ")"; + } + return $string; + } + /** * Return advanced inner search string based on input and handler. * @@ -386,17 +413,8 @@ class QueryBuilder implements QueryBuilderInterface return $string; } - // If the query ends in a non-escaped question mark, the user may not really - // intend to use the question mark as a wildcard -- let's account for that - // possibility - if (substr($string, -1) == '?' && substr($string, -2) != '\?') { - // Make sure all question marks are properly escaped (first unescape - // any that are already escaped to prevent double-escapes, then escape - // all of them): - $strippedQuery - = str_replace('?', '\?', str_replace('\?', '?', $string)); - $string = "({$string}) OR (" . $strippedQuery . ")"; - } + // Account for trailing question marks: + $string = $this->fixTrailingQuestionMarks($string); return $handler ? $handler->createAdvancedQueryString($string, false) : $string; 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 cf889b9425c26f807b38b13df41796964b7f374b..2ea5b62f11576630730ea79612837a95f0882a4b 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 @@ -129,6 +129,34 @@ class QueryBuilderTest extends \VuFindTest\Unit\TestCase } } + /** + * Test generation with a query handler with edismax + * + * @return void + */ + public function testQueryHandlerWithEdismax() + { + // Set up an array of expected inputs and outputs: + // @codingStandardsIgnoreStart + $tests = [ + ['this?', '(this?) OR (this\?)'],// trailing question mark + ]; + // @codingStandardsIgnoreEnd + + $qb = new QueryBuilder( + [ + 'test' => ['DismaxHandler' => 'edismax', 'DismaxFields' => ['foo']] + ] + ); + foreach ($tests as $test) { + list($input, $output) = $test; + $q = new Query($input, 'test'); + $response = $qb->build($q); + $processedQ = $response->get('q'); + $this->assertEquals($output, $processedQ[0]); + } + } + /** * Test that the appropriate handler gets called for a quoted search when exact * settings are enabled.