Commit ca306bf3 authored by alexpott's avatar alexpott
Browse files

Issue #1842226 by jhodgdon | spartlow: Fixed Search OR statements don't work...

Issue #1842226 by jhodgdon | spartlow: Fixed Search OR statements don't work if same keyword is used.
parent 96dd466f
......@@ -235,7 +235,7 @@ protected function parseSearchExpression() {
}
// Classify tokens.
$or = FALSE;
$in_or = FALSE;
$limit_combinations = \Drupal::config('search.settings')->get('and_or_limit');
// The first search expression does not count as AND.
$and_count = -1;
......@@ -248,13 +248,14 @@ protected function parseSearchExpression() {
break;
}
$phrase = FALSE;
// Strip off phrase quotes.
$phrase = FALSE;
if ($match[2]{0} == '"') {
$match[2] = substr($match[2], 1, -1);
$phrase = TRUE;
$this->simple = FALSE;
}
// Simplify keyword according to indexing rules and external
// preprocessors. Use same process as during search indexing, so it
// will match search index.
......@@ -275,7 +276,7 @@ protected function parseSearchExpression() {
$last = array($last);
}
$this->keys['positive'][] = $last;
$or = TRUE;
$in_or = TRUE;
$or_count++;
continue;
}
......@@ -290,7 +291,7 @@ protected function parseSearchExpression() {
// Lower-case "or" instead of "OR" is a warning condition.
$this->status |= SearchQuery::LOWER_CASE_OR;
}
if ($or) {
if ($in_or) {
// 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);
}
......@@ -299,33 +300,38 @@ protected function parseSearchExpression() {
$and_count++;
}
}
$or = FALSE;
$in_or = FALSE;
}
// Convert keywords into SQL statements.
$simple_and = FALSE;
$simple_or = FALSE;
$has_and = FALSE;
$has_or = FALSE;
// Positive matches.
foreach ($this->keys['positive'] as $key) {
// Group of ORed terms.
if (is_array($key) && count($key)) {
$simple_or = TRUE;
$any = FALSE;
// If we had already found one OR, this is another one AND-ed with the
// first, meaning it is not a simple query.
if ($has_or) {
$this->simple = FALSE;
}
$has_or = TRUE;
$has_new_scores = FALSE;
$queryor = db_or();
foreach ($key as $or) {
list($num_new_scores) = $this->parseWord($or);
$any |= $num_new_scores;
$has_new_scores |= $num_new_scores;
$queryor->condition('d.data', "% $or %", 'LIKE');
}
if (count($queryor)) {
$this->conditions->condition($queryor);
// A group of OR keywords only needs to match once.
$this->matches += ($any > 0);
$this->matches += ($has_new_scores > 0);
}
}
// Single ANDed term.
else {
$simple_and = TRUE;
$has_and = TRUE;
list($num_new_scores, $num_valid_words) = $this->parseWord($key);
$this->conditions->condition('d.data', "% $key %", 'LIKE');
if (!$num_valid_words) {
......@@ -335,9 +341,10 @@ protected function parseSearchExpression() {
$this->matches += $num_new_scores;
}
}
if ($simple_and && $simple_or) {
if ($has_and && $has_or) {
$this->simple = FALSE;
}
// Negative matches.
foreach ($this->keys['negative'] as $key) {
$this->conditions->condition('d.data', "% $key %", 'NOT LIKE');
......@@ -409,8 +416,15 @@ public function prepareAndNormalize() {
$this
->condition('i.type', $this->type)
->groupBy('i.type')
->groupBy('i.sid')
->having('COUNT(*) >= :matches', array(':matches' => $this->matches));
->groupBy('i.sid');
// 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.
$normalize_query = clone $this->query;
......
......@@ -88,15 +88,13 @@ function getText2($n) {
* Run predefine queries looking for indexed terms.
*/
function _testQueries() {
/*
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 the query.
e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good
e.g. dolore OR ut = (dolore) OR (ut) -> bad
This is a design limitation to avoid full table scans.
*/
// 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
// the query. Examples:
// enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut)
// is good, and
// dolore OR ut = (dolore) OR (ut)
// is bad. This is a design limitation to avoid full table scans.
$queries = array(
// Simple AND queries.
'ipsum' => array(1),
......@@ -109,7 +107,7 @@ function _testQueries() {
'ut minim' => array(5),
'xx minim' => array(),
'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 xxxxx' => array(2, 7),
'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7),
......@@ -119,6 +117,14 @@ function _testQueries() {
'minim dolore OR ipsum OR enim' => array(5, 6, 7),
'dolore xx OR yy' => 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.
'dolore -sit' => array(7),
'dolore -eu' => array(2),
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment