/** * Creates a database query condition for a given search filter. * * Used as a helper method in createDbQuery(). * * @param \Drupal\search_api\Query\FilterInterface $filter * The filter for which a condition should be created. * @param array $fields * Internal information about the index's fields. * @param \Drupal\Core\Database\Query\SelectInterface $db_query * The database query to which the condition will be added. * @param \Drupal\search_api\IndexInterface $index * The index we're searching on. * * @return \Drupal\Core\Database\Query\ConditionInterface|null * The condition to set on the query, or NULL if none is necessary. * * @throws \Drupal\search_api\SearchApiException * Thrown if an unknown field was used in the filter. */ protected function createFilterCondition(FilterInterface $filter, array $fields, SelectInterface $db_query, IndexInterface $index) { $cond = new Condition($filter->getConjunction()); $db_info = $this->getIndexDbInfo($index); $empty = TRUE; // Store whether a JOIN already occurred for a field, so we don't JOIN // repeatedly for OR filters. $first_join = array(); // Store the table aliases for the fields in this condition group. $tables = array(); foreach ($filter->getFilters() as $f) { if (is_object($f)) { $c = $this->createFilterCondition($f, $fields, $db_query, $index); if ($c) { $empty = FALSE; $cond->condition($c); } } else { $empty = FALSE; // We don't index the datasource explicitly, so this needs a bit of // magic. // @todo Index the datasource explicitly so this doesn't need magic. if ($f[0] === 'search_api_datasource') { $alias = $this->getTableAlias(array('table' => $db_info['index_table']), $db_query); // @todo Stop recognizing "!=" as an operator. $operator = ($f[2] == '<>' || $f[2] == '!=') ? 'NOT LIKE' : 'LIKE'; $prefix = Utility::createCombinedId($f[1], ''); $cond->condition($alias . '.item_id', $this->database->escapeLike($prefix) . '%', $operator); continue; } if (!isset($fields[$f[0]])) { throw new SearchApiException(new FormattableMarkup('Unknown field in filter clause: @field.', array('@field' => $f[0]))); } $field = $fields[$f[0]]; // Fields have their own table, so we have to check for NULL values in // a special way (i.e., check for missing entries in that table). // @todo This can probably always use the denormalized table. if ($f[1] === NULL) { $query = $this->database->select($field['table'], 't') ->fields('t', array('item_id')); $cond->condition('t.item_id', $query, $f[2] == '<>' || $f[2] == '!=' ? 'IN' : 'NOT IN'); continue; } if (Utility::isTextType($field['type'])) { $keys = $this->prepareKeys($f[1]); $query = $this->createKeysQuery($keys, array($f[0] => $field), $fields, $index); // We don't need the score, so we remove it. The score might either be // an expression or a field. $query_expressions = &$query->getExpressions(); if ($query_expressions) { $query_expressions = array(); } else { $query_fields = &$query->getFields(); unset($query_fields['score']); unset($query_fields); } unset($query_expressions); $cond->condition('t.item_id', $query, $f[2] == '<>' || $f[2] == '!=' ? 'NOT IN' : 'IN'); } else { $new_join = ($filter->getConjunction() == 'AND' || empty($first_join[$f[0]])); if ($new_join || empty($tables[$f[0]])) { $tables[$f[0]] = $this->getTableAlias($field, $db_query, $new_join); $first_join[$f[0]] = TRUE; } $column = $tables[$f[0]] . '.' . 'value'; if ($f[1] !== NULL) { $cond->condition($column, $f[1], $f[2]); } else { $method = ($f[2] == '=') ? 'isNull' : 'isNotNull'; $cond->$method($column); } } } } return $empty ? NULL : $cond; }
/** * {@inheritdoc} */ public function condition($field, $value, $operator = '=') { $this->filter->condition($field, $value, $operator); return $this; }