You are here

function search_parse_query in Drupal 6

Same name and namespace in other branches
  1. 4 modules/search.module \search_parse_query()
  2. 5 modules/search/search.module \search_parse_query()

Parse a search query into SQL conditions.

We build two queries that matches the dataset bodies. @See do_search for more about these.

Parameters

$text: The search keys.

Return value

A list of six elements.

  • A series of statements AND'd together which will be used to provide all possible matches.
  • Arguments for this query part.
  • A series of exact word matches OR'd together.
  • Arguments for this query part.
  • A bool indicating whether this is a simple query or not. Negative terms, presence of both AND / OR make this FALSE.
  • A bool indicating the presence of a lowercase or. Maybe the user wanted to use OR.
1 call to search_parse_query()
do_search in modules/search/search.module
Do a query on the full-text search index for a word or words.

File

modules/search/search.module, line 710
Enables site-wide keyword searching.

Code

function search_parse_query($text) {
  $keys = array(
    'positive' => array(),
    'negative' => array(),
  );

  // Tokenize query string
  preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $text, $matches, PREG_SET_ORDER);
  if (count($matches) < 1) {
    return NULL;
  }

  // Classify tokens
  $or = FALSE;
  $warning = '';
  $simple = TRUE;
  foreach ($matches as $match) {
    $phrase = FALSE;

    // Strip off phrase quotes
    if ($match[2][0] == '"') {
      $match[2] = substr($match[2], 1, -1);
      $phrase = TRUE;
      $simple = FALSE;
    }

    // Simplify keyword according to indexing rules and external preprocessors
    $words = search_simplify($match[2]);

    // Re-explode in case simplification added more words, except when matching a phrase
    $words = $phrase ? array(
      $words,
    ) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY);

    // Negative matches
    if ($match[1] == '-') {
      $keys['negative'] = array_merge($keys['negative'], $words);
    }
    elseif ($match[2] == 'OR' && count($keys['positive'])) {
      $last = array_pop($keys['positive']);

      // Starting a new OR?
      if (!is_array($last)) {
        $last = array(
          $last,
        );
      }
      $keys['positive'][] = $last;
      $or = TRUE;
      continue;
    }
    elseif ($match[2] == 'AND' || $match[2] == 'and') {
      $warning = $match[2];
      continue;
    }
    else {
      if ($match[2] == 'or') {
        $warning = $match[2];
      }
      if ($or) {

        // Add to last element (which is an array)
        $keys['positive'][count($keys['positive']) - 1] = array_merge($keys['positive'][count($keys['positive']) - 1], $words);
      }
      else {
        $keys['positive'] = array_merge($keys['positive'], $words);
      }
    }
    $or = FALSE;
  }

  // Convert keywords into SQL statements.
  $query = array();
  $query2 = array();
  $arguments = array();
  $arguments2 = array();
  $matches = 0;
  $simple_and = FALSE;
  $simple_or = FALSE;

  // Positive matches
  foreach ($keys['positive'] as $key) {

    // Group of ORed terms
    if (is_array($key) && count($key)) {
      $simple_or = TRUE;
      $queryor = array();
      $any = FALSE;
      foreach ($key as $or) {
        list($q, $num_new_scores) = _search_parse_query($or, $arguments2);
        $any |= $num_new_scores;
        if ($q) {
          $queryor[] = $q;
          $arguments[] = $or;
        }
      }
      if (count($queryor)) {
        $query[] = '(' . implode(' OR ', $queryor) . ')';

        // A group of OR keywords only needs to match once
        $matches += $any > 0;
      }
    }
    else {
      $simple_and = TRUE;
      list($q, $num_new_scores, $num_valid_words) = _search_parse_query($key, $arguments2);
      if ($q) {
        $query[] = $q;
        $arguments[] = $key;
        if (!$num_valid_words) {
          $simple = FALSE;
        }

        // Each AND keyword needs to match at least once
        $matches += $num_new_scores;
      }
    }
  }
  if ($simple_and && $simple_or) {
    $simple = FALSE;
  }

  // Negative matches
  foreach ($keys['negative'] as $key) {
    list($q) = _search_parse_query($key, $arguments2, TRUE);
    if ($q) {
      $query[] = $q;
      $arguments[] = $key;
      $simple = FALSE;
    }
  }
  $query = implode(' AND ', $query);

  // Build word-index conditions for the first pass
  $query2 = substr(str_repeat("i.word = '%s' OR ", count($arguments2)), 0, -4);
  return array(
    $query,
    $arguments,
    $query2,
    $arguments2,
    $matches,
    $simple,
    $warning,
  );
}