Issue #1842226 by jhodgdon | spartlow: Fixed Search OR statements don't work if same keyword is used.
parent
96dd466f8e
commit
ca306bf3f1
|
@ -235,7 +235,7 @@ class SearchQuery extends SelectExtender {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Classify tokens.
|
// Classify tokens.
|
||||||
$or = FALSE;
|
$in_or = FALSE;
|
||||||
$limit_combinations = \Drupal::config('search.settings')->get('and_or_limit');
|
$limit_combinations = \Drupal::config('search.settings')->get('and_or_limit');
|
||||||
// The first search expression does not count as AND.
|
// The first search expression does not count as AND.
|
||||||
$and_count = -1;
|
$and_count = -1;
|
||||||
|
@ -248,13 +248,14 @@ class SearchQuery extends SelectExtender {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$phrase = FALSE;
|
|
||||||
// Strip off phrase quotes.
|
// Strip off phrase quotes.
|
||||||
|
$phrase = FALSE;
|
||||||
if ($match[2]{0} == '"') {
|
if ($match[2]{0} == '"') {
|
||||||
$match[2] = substr($match[2], 1, -1);
|
$match[2] = substr($match[2], 1, -1);
|
||||||
$phrase = TRUE;
|
$phrase = TRUE;
|
||||||
$this->simple = FALSE;
|
$this->simple = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simplify keyword according to indexing rules and external
|
// Simplify keyword according to indexing rules and external
|
||||||
// preprocessors. Use same process as during search indexing, so it
|
// preprocessors. Use same process as during search indexing, so it
|
||||||
// will match search index.
|
// will match search index.
|
||||||
|
@ -275,7 +276,7 @@ class SearchQuery extends SelectExtender {
|
||||||
$last = array($last);
|
$last = array($last);
|
||||||
}
|
}
|
||||||
$this->keys['positive'][] = $last;
|
$this->keys['positive'][] = $last;
|
||||||
$or = TRUE;
|
$in_or = TRUE;
|
||||||
$or_count++;
|
$or_count++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +291,7 @@ class SearchQuery extends SelectExtender {
|
||||||
// Lower-case "or" instead of "OR" is a warning condition.
|
// Lower-case "or" instead of "OR" is a warning condition.
|
||||||
$this->status |= SearchQuery::LOWER_CASE_OR;
|
$this->status |= SearchQuery::LOWER_CASE_OR;
|
||||||
}
|
}
|
||||||
if ($or) {
|
if ($in_or) {
|
||||||
// Add to last element (which is an array).
|
// Add to last element (which is an array).
|
||||||
$this->keys['positive'][count($this->keys['positive']) - 1] = array_merge($this->keys['positive'][count($this->keys['positive']) - 1], $words);
|
$this->keys['positive'][count($this->keys['positive']) - 1] = array_merge($this->keys['positive'][count($this->keys['positive']) - 1], $words);
|
||||||
}
|
}
|
||||||
|
@ -299,33 +300,38 @@ class SearchQuery extends SelectExtender {
|
||||||
$and_count++;
|
$and_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$or = FALSE;
|
$in_or = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert keywords into SQL statements.
|
// Convert keywords into SQL statements.
|
||||||
$simple_and = FALSE;
|
$has_and = FALSE;
|
||||||
$simple_or = FALSE;
|
$has_or = FALSE;
|
||||||
// Positive matches.
|
// Positive matches.
|
||||||
foreach ($this->keys['positive'] as $key) {
|
foreach ($this->keys['positive'] as $key) {
|
||||||
// Group of ORed terms.
|
// Group of ORed terms.
|
||||||
if (is_array($key) && count($key)) {
|
if (is_array($key) && count($key)) {
|
||||||
$simple_or = TRUE;
|
// If we had already found one OR, this is another one AND-ed with the
|
||||||
$any = FALSE;
|
// first, meaning it is not a simple query.
|
||||||
|
if ($has_or) {
|
||||||
|
$this->simple = FALSE;
|
||||||
|
}
|
||||||
|
$has_or = TRUE;
|
||||||
|
$has_new_scores = FALSE;
|
||||||
$queryor = db_or();
|
$queryor = db_or();
|
||||||
foreach ($key as $or) {
|
foreach ($key as $or) {
|
||||||
list($num_new_scores) = $this->parseWord($or);
|
list($num_new_scores) = $this->parseWord($or);
|
||||||
$any |= $num_new_scores;
|
$has_new_scores |= $num_new_scores;
|
||||||
$queryor->condition('d.data', "% $or %", 'LIKE');
|
$queryor->condition('d.data', "% $or %", 'LIKE');
|
||||||
}
|
}
|
||||||
if (count($queryor)) {
|
if (count($queryor)) {
|
||||||
$this->conditions->condition($queryor);
|
$this->conditions->condition($queryor);
|
||||||
// A group of OR keywords only needs to match once.
|
// A group of OR keywords only needs to match once.
|
||||||
$this->matches += ($any > 0);
|
$this->matches += ($has_new_scores > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Single ANDed term.
|
// Single ANDed term.
|
||||||
else {
|
else {
|
||||||
$simple_and = TRUE;
|
$has_and = TRUE;
|
||||||
list($num_new_scores, $num_valid_words) = $this->parseWord($key);
|
list($num_new_scores, $num_valid_words) = $this->parseWord($key);
|
||||||
$this->conditions->condition('d.data', "% $key %", 'LIKE');
|
$this->conditions->condition('d.data', "% $key %", 'LIKE');
|
||||||
if (!$num_valid_words) {
|
if (!$num_valid_words) {
|
||||||
|
@ -335,9 +341,10 @@ class SearchQuery extends SelectExtender {
|
||||||
$this->matches += $num_new_scores;
|
$this->matches += $num_new_scores;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($simple_and && $simple_or) {
|
if ($has_and && $has_or) {
|
||||||
$this->simple = FALSE;
|
$this->simple = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative matches.
|
// Negative matches.
|
||||||
foreach ($this->keys['negative'] as $key) {
|
foreach ($this->keys['negative'] as $key) {
|
||||||
$this->conditions->condition('d.data', "% $key %", 'NOT LIKE');
|
$this->conditions->condition('d.data', "% $key %", 'NOT LIKE');
|
||||||
|
@ -409,8 +416,15 @@ class SearchQuery extends SelectExtender {
|
||||||
$this
|
$this
|
||||||
->condition('i.type', $this->type)
|
->condition('i.type', $this->type)
|
||||||
->groupBy('i.type')
|
->groupBy('i.type')
|
||||||
->groupBy('i.sid')
|
->groupBy('i.sid');
|
||||||
->having('COUNT(*) >= :matches', array(':matches' => $this->matches));
|
|
||||||
|
// If the query is simple, we should have calculated the number of
|
||||||
|
// matching words we need to find, so impose that criterion. For non-
|
||||||
|
// simple queries, this condition could lead to incorrectly deciding not
|
||||||
|
// to continue with the full query.
|
||||||
|
if ($this->simple) {
|
||||||
|
$this->having('COUNT(*) >= :matches', array(':matches' => $this->matches));
|
||||||
|
}
|
||||||
|
|
||||||
// Clone the query object to calculate normalization.
|
// Clone the query object to calculate normalization.
|
||||||
$normalize_query = clone $this->query;
|
$normalize_query = clone $this->query;
|
||||||
|
|
|
@ -88,15 +88,13 @@ class SearchMatchTest extends SearchTestBase {
|
||||||
* Run predefine queries looking for indexed terms.
|
* Run predefine queries looking for indexed terms.
|
||||||
*/
|
*/
|
||||||
function _testQueries() {
|
function _testQueries() {
|
||||||
/*
|
// Note: OR queries that include short words in OR groups are only accepted
|
||||||
Note: OR queries that include short words in OR groups are only accepted
|
// if the ORed terms are ANDed with at least one long word in the rest of
|
||||||
if the ORed terms are ANDed with at least one long word in the rest of the query.
|
// the query. Examples:
|
||||||
|
// enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut)
|
||||||
e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good
|
// is good, and
|
||||||
e.g. dolore OR ut = (dolore) OR (ut) -> bad
|
// dolore OR ut = (dolore) OR (ut)
|
||||||
|
// is bad. This is a design limitation to avoid full table scans.
|
||||||
This is a design limitation to avoid full table scans.
|
|
||||||
*/
|
|
||||||
$queries = array(
|
$queries = array(
|
||||||
// Simple AND queries.
|
// Simple AND queries.
|
||||||
'ipsum' => array(1),
|
'ipsum' => array(1),
|
||||||
|
@ -109,7 +107,7 @@ class SearchMatchTest extends SearchTestBase {
|
||||||
'ut minim' => array(5),
|
'ut minim' => array(5),
|
||||||
'xx minim' => array(),
|
'xx minim' => array(),
|
||||||
'enim veniam am minim ut' => array(5),
|
'enim veniam am minim ut' => array(5),
|
||||||
// Simple OR queries.
|
// Simple OR and AND/OR queries.
|
||||||
'dolore OR ipsum' => array(1, 2, 7),
|
'dolore OR ipsum' => array(1, 2, 7),
|
||||||
'dolore OR xxxxx' => array(2, 7),
|
'dolore OR xxxxx' => array(2, 7),
|
||||||
'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7),
|
'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7),
|
||||||
|
@ -119,6 +117,14 @@ class SearchMatchTest extends SearchTestBase {
|
||||||
'minim dolore OR ipsum OR enim' => array(5, 6, 7),
|
'minim dolore OR ipsum OR enim' => array(5, 6, 7),
|
||||||
'dolore xx OR yy' => array(),
|
'dolore xx OR yy' => array(),
|
||||||
'xxxxx dolore OR ipsum' => array(),
|
'xxxxx dolore OR ipsum' => array(),
|
||||||
|
// Sequence of OR queries.
|
||||||
|
'minim' => array(5, 6, 7),
|
||||||
|
'minim OR xxxx' => array(5, 6, 7),
|
||||||
|
'minim OR xxxx OR minim' => array(5, 6, 7),
|
||||||
|
'minim OR xxxx minim' => array(5, 6, 7),
|
||||||
|
'minim OR xxxx minim OR yyyy' => array(5, 6, 7),
|
||||||
|
'minim OR xxxx minim OR cillum' => array(6, 7, 5),
|
||||||
|
'minim OR xxxx minim OR xxxx' => array(5, 6, 7),
|
||||||
// Negative queries.
|
// Negative queries.
|
||||||
'dolore -sit' => array(7),
|
'dolore -sit' => array(7),
|
||||||
'dolore -eu' => array(2),
|
'dolore -eu' => array(2),
|
||||||
|
|
Loading…
Reference in New Issue