protected function FuzzySearchService::createKeysQuery in Fuzzy Search 7
Helper method for creating a SELECT query for given search keys.
Return value
SelectQueryInterface A SELECT query returning item_id and score (or only item_id, if $keys['#negation'] is set).
2 calls to FuzzySearchService::createKeysQuery()
- FuzzySearchService::createFilterCondition in includes/
service.inc - Helper method for creating a condition for filtering search results.
- FuzzySearchService::search in includes/
service.inc - Executes a search on the server represented by this object.
File
- includes/
service.inc, line 948
Class
- FuzzySearchService
- Search service class using the database for storing index information.
Code
protected function createKeysQuery($keys, array $fields, array $all_fields, array $fuzzy) {
if (!is_array($keys)) {
$keys = array(
'#conjunction' => 'AND',
0 => $keys,
);
}
$or = db_or();
$and = db_and();
$neg = !empty($keys['#negation']);
$conj = $keys['#conjunction'];
$words = array();
$nested = array();
$negated = array();
$db_query = NULL;
$mul_words = FALSE;
// Whether the query will nest UNIONed subqueries (FALSE)
// or just leave them that way (TRUE).
$not_nested = FALSE;
foreach ($keys as $i => $key) {
if (!element_child($i)) {
continue;
}
if (is_scalar($key)) {
$words[] = $key;
}
elseif (empty($key['#negation'])) {
if ($neg) {
// If this query is negated, we also only need item_ids from
// subqueries.
$key['#negation'] = TRUE;
}
$nested[] = $key;
}
else {
$negated[] = $key;
}
}
$subs = count($words) + count($nested);
$not_nested = $subs <= 1 && count($fields) == 1 || $neg && $conj == 'OR' && !$negated;
if ($words) {
if (count($words) > 1) {
$mul_words = TRUE;
foreach ($words as $word) {
$ngrams = fuzzysearch_parse_word($word, $fuzzy);
foreach ($ngrams['ngrams'] as $ngram) {
$and = db_and();
$and
->condition('ngram', $ngram)
->condition('completeness', $ngrams['comp_min'], '>=')
->condition('completeness', $ngrams['comp_max'], '<=');
$or
->condition($and);
}
}
}
else {
$word = array_shift($words);
}
foreach ($fields as $field) {
$table = $field['table'];
$query = db_select($table, 't', $this->queryOptions);
if ($neg) {
$query
->fields('t', array(
'item_id',
));
}
elseif ($not_nested) {
$query
->fields('t', array(
'item_id',
'score',
));
}
else {
$query
->fields('t');
}
if ($mul_words) {
$query
->condition($or);
}
else {
$ngrams = fuzzysearch_parse_word($word, $fuzzy);
foreach ($ngrams['ngrams'] as $ngram) {
$and = db_and();
$and
->condition('ngram', $ngram)
->condition('completeness', $ngrams['comp_min'], '>=')
->condition('completeness', $ngrams['comp_max'], '<=');
$or
->condition($and);
}
$query
->condition($or);
}
if (!isset($db_query)) {
$db_query = $query;
}
elseif ($not_nested) {
$db_query
->union($query, 'UNION');
}
else {
$db_query
->union($query, 'UNION ALL');
}
// Clone the query for search excerpting.
fuzzysearch_static_search_query(clone $db_query);
// Group by word and then entity.
$query
->groupBy('t.word_id');
$query
->groupBY('t.item_id');
$query
->addExpression('SUM(t.completeness)', 'percent');
$query
->having('SUM(t.completeness) >= :value1', array(
':value1' => $fuzzy['min_completeness'],
));
}
}
if ($nested) {
$word = '';
foreach ($nested as $k) {
$query = $this
->createKeysQuery($k, $fields, $all_fields);
if (!$neg) {
$word .= ' ';
$var = ':word' . strlen($word);
$query
->addExpression($var, 'word', array(
$var => $word,
));
}
if (!isset($db_query)) {
$db_query = $query;
}
elseif ($not_nested) {
$db_query
->union($query, 'UNION');
}
else {
$db_query
->union($query, 'UNION ALL');
}
}
}
if (isset($db_query) && !$not_nested) {
$db_query = db_select($db_query, 't', $this->queryOptions);
$db_query
->addField('t', 'item_id', 'item_id');
if (!$neg) {
$db_query
->addExpression('SUM(t.score)', 'score');
$db_query
->addExpression('SUM(percent)', 'percent');
$db_query
->groupBy('t.item_id');
}
if ($conj == 'AND' && $subs > 1) {
$var = ':subs' . (int) $subs;
if (!$db_query
->getGroupBy()) {
$db_query
->groupBy('t.item_id');
}
// @codingStandardsIgnoreStart
// if ($mul_words) {
// $db_query->having('COUNT(DISTINCT t.word) >= ' . $var, array($var => $subs));
// }
// else {
// $db_query->having('COUNT(DISTINCT t.word) >= ' . $var, array($var => $subs));
// }
// @codingStandardsIgnoreEnd
}
}
if ($negated) {
if (!isset($db_query) || $conj == 'OR') {
if (isset($all_fields['search_api_language'])) {
// We use this table because all items should be contained exactly
// once.
$table = $all_fields['search_api_language']['table'];
}
else {
$distinct = TRUE;
foreach ($all_fields as $field) {
$table = $field['table'];
if (!search_api_is_list_type($field['type']) && !search_api_is_text_type($field['type'])) {
unset($distinct);
break;
}
}
}
if (isset($db_query)) {
// We are in a rather bizarre case where the keys are something like
// "a OR (NOT b)".
$old_query = $db_query;
}
$db_query = db_select($table, 't', $this->queryOptions);
$db_query
->addField('t', 'item_id', 'item_id');
if (!$neg) {
$db_query
->addExpression(':score', 'score', array(
':score' => 1,
));
}
if (isset($distinct)) {
$db_query
->distinct();
}
}
if ($conj == 'AND') {
foreach ($negated as $k) {
$db_query
->condition('t.item_id', $this
->createKeysQuery($k, $fields, $all_fields), 'NOT IN');
}
}
else {
$or = db_or();
foreach ($negated as $k) {
$or
->condition('t.item_id', $this
->createKeysQuery($k, $fields, $all_fields), 'NOT IN');
}
if (isset($old_query)) {
$or
->condition('t.item_id', $old_query, 'NOT IN');
}
$db_query
->condition($or);
}
}
return $db_query;
}