protected function SearchQuery::parseSearchExpression in Drupal 10
Same name and namespace in other branches
- 8 core/modules/search/src/SearchQuery.php \Drupal\search\SearchQuery::parseSearchExpression()
- 9 core/modules/search/src/SearchQuery.php \Drupal\search\SearchQuery::parseSearchExpression()
Parses the search query into SQL conditions.
Sets up the following variables:
- $this->keys
- $this->words
- $this->conditions
- $this->simple
- $this->matches
2 calls to SearchQuery::parseSearchExpression()
- SearchQuery::prepareAndNormalize in core/
modules/ search/ src/ SearchQuery.php - Prepares the query and calculates the normalization factor.
- ViewsSearchQuery::publicParseSearchExpression in core/
modules/ search/ src/ ViewsSearchQuery.php - Executes and returns the protected parseSearchExpression method.
File
- core/
modules/ search/ src/ SearchQuery.php, line 223
Class
- SearchQuery
- Search query extender and helper functions.
Namespace
Drupal\searchCode
protected function parseSearchExpression() {
// Matches words optionally prefixed by a - sign. A word in this case is
// something between two spaces, optionally quoted.
preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->searchExpression, $keywords, PREG_SET_ORDER);
if (count($keywords) == 0) {
return;
}
// Classify tokens.
$in_or = FALSE;
$limit_combinations = \Drupal::config('search.settings')
->get('and_or_limit');
/** @var \Drupal\search\SearchTextProcessorInterface $text_processor */
$text_processor = \Drupal::service('search.text_processor');
// The first search expression does not count as AND.
$and_count = -1;
$or_count = 0;
foreach ($keywords as $match) {
if ($or_count && $and_count + $or_count >= $limit_combinations) {
// Ignore all further search expressions to prevent Denial-of-Service
// attacks using a high number of AND/OR combinations.
$this->status |= SearchQuery::EXPRESSIONS_IGNORED;
break;
}
// 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.
$words = $text_processor
->analyze($match[2]);
// Re-explode in case simplification added more words, except when
// matching a phrase.
$words = $phrase ? [
$words,
] : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY);
// Negative matches.
if ($match[1] == '-') {
$this->keys['negative'] = array_merge($this->keys['negative'], $words);
}
elseif ($match[2] == 'OR' && count($this->keys['positive'])) {
$last = array_pop($this->keys['positive']);
// Starting a new OR?
if (!is_array($last)) {
$last = [
$last,
];
}
$this->keys['positive'][] = $last;
$in_or = TRUE;
$or_count++;
continue;
}
elseif ($match[2] == 'AND' || $match[2] == 'and') {
continue;
}
else {
if ($match[2] == 'or') {
// Lower-case "or" instead of "OR" is a warning condition.
$this->status |= SearchQuery::LOWER_CASE_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);
}
else {
$this->keys['positive'] = array_merge($this->keys['positive'], $words);
$and_count++;
}
}
$in_or = FALSE;
}
// Convert keywords into SQL statements.
$has_and = FALSE;
$has_or = FALSE;
// Positive matches.
foreach ($this->keys['positive'] as $key) {
// Group of ORed terms.
if (is_array($key) && count($key)) {
// If we had already found one OR, this is another one ANDed with the
// first, meaning it is not a simple query.
if ($has_or) {
$this->simple = FALSE;
}
$has_or = TRUE;
$has_new_scores = FALSE;
$queryor = $this->connection
->condition('OR');
foreach ($key as $or) {
[
$num_new_scores,
] = $this
->parseWord($or);
$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 += $has_new_scores > 0;
}
}
else {
$has_and = TRUE;
[
$num_new_scores,
$num_valid_words,
] = $this
->parseWord($key);
$this->conditions
->condition('d.data', "% {$key} %", 'LIKE');
if (!$num_valid_words) {
$this->simple = FALSE;
}
// Each AND keyword needs to match at least once.
$this->matches += $num_new_scores;
}
}
if ($has_and && $has_or) {
$this->simple = FALSE;
}
// Negative matches.
foreach ($this->keys['negative'] as $key) {
$this->conditions
->condition('d.data', "% {$key} %", 'NOT LIKE');
$this->simple = FALSE;
}
}