View source
<?php
namespace Drupal\elasticsearch_connector\ElasticSearch\Parameters\Builder;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\elasticsearch_connector\ElasticSearch\Parameters\Factory\FilterFactory;
use Drupal\elasticsearch_connector\ElasticSearch\Parameters\Factory\IndexFactory;
use Drupal\search_api\Entity\Index;
use Drupal\search_api\Query\Condition;
use Drupal\search_api\Query\ConditionGroupInterface;
use Drupal\search_api\Query\QueryInterface;
use Elasticsearch\Common\Exceptions\ElasticsearchException;
class SearchBuilder {
use StringTranslationTrait;
protected $index;
protected $query;
public function __construct(QueryInterface $query) {
$this->query = $query;
$this->index = $query
->getIndex();
}
public function build() {
$params = IndexFactory::index($this->index, TRUE);
$query_options = $this
->getSearchQueryOptions();
$body =& $params['body'];
$body['from'] = $query_options['query_offset'];
$body['size'] = $query_options['query_limit'];
if (!empty($query_options['sort'])) {
$body['sort'] = $query_options['sort'];
}
$body['fields'] = [];
$fields =& $body['fields'];
if (!empty($query_options['mlt'])) {
$mlt_query['more_like_this'] = [];
$mlt_query['more_like_this']['like_text'] = $query_options['mlt']['id'];
$mlt_query['more_like_this']['fields'] = array_values($query_options['mlt']['fields']);
$mlt_query['more_like_this']['max_query_terms'] = 1;
$mlt_query['more_like_this']['min_doc_freq'] = 1;
$mlt_query['more_like_this']['min_term_freq'] = 1;
$fields += array_values($query_options['mlt']['fields']);
$body['query'] = $mlt_query;
}
if (!empty($query_options['query_search_string']) && !empty($query_options['query_search_filter'])) {
$body['query']['filtered']['query'] = $query_options['query_search_string'];
$body['query']['filtered']['filter'] = $query_options['query_search_filter'];
}
elseif (!empty($query_options['query_search_string'])) {
if (empty($body['query'])) {
$body['query'] = [];
}
$body['query'] += $query_options['query_search_string'];
}
elseif (!empty($query_options['query_search_filter'])) {
$body['filter'] = $query_options['query_search_filter'];
}
if (empty($fields)) {
unset($body['fields']);
}
if (empty($body['filter'])) {
unset($body['filter']);
}
if (empty($query_body)) {
$query_body['match_all'] = [];
}
$this->query
->setOption('ElasticParams', $params);
return $params;
}
protected function getSearchQueryOptions() {
$query_options = $this->query
->getOptions();
$parse_mode = $this->query
->getParseMode();
$index_fields = $this->index
->getFields();
$query_offset = empty($query_options['offset']) ? 0 : $query_options['offset'];
$query_limit = empty($query_options['limit']) ? 10 : $query_options['limit'];
$query_search_string = NULL;
$query_search_filter = NULL;
$keys = $this->query
->getKeys();
if (!empty($keys)) {
if (is_string($keys)) {
$keys = [
$keys,
];
}
$query_full_text_fields = $this->index
->getFulltextFields();
$query_fields = array();
foreach ($query_full_text_fields as $full_text_field_name) {
$full_text_field = $index_fields[$full_text_field_name];
$query_fields[] = $full_text_field
->getFieldIdentifier() . '^' . $full_text_field
->getBoost();
}
$search_string = $this
->flattenKeys($keys, $parse_mode);
if (!empty($search_string)) {
$query_search_string = [
'query_string' => [],
];
$query_search_string['query_string']['query'] = $search_string;
$query_search_string['query_string']['fields'] = $query_fields;
$query_search_string['query_string']['analyzer'] = 'snowball';
$query_search_string['query_string']['default_operator'] = 'OR';
}
}
$sort = NULL;
try {
$sort = $this
->getSortSearchQuery();
} catch (ElasticsearchException $e) {
watchdog_exception('Elasticsearch Search API', $e);
drupal_set_message($e
->getMessage(), 'error');
}
try {
$parsed_query_filters = $this
->getQueryFilters($this->query
->getConditionGroup(), $index_fields);
if (!empty($parsed_query_filters)) {
$query_search_filter = $parsed_query_filters[0];
}
} catch (ElasticsearchException $e) {
watchdog_exception('Elasticsearch Search API', $e, Html::escape($e
->getMessage()));
drupal_set_message(Html::escape($e
->getMessage()), 'error');
}
$mlt = [];
if (isset($query_options['search_api_mlt'])) {
$mlt = $query_options['search_api_mlt'];
}
return [
'query_offset' => $query_offset,
'query_limit' => $query_limit,
'query_search_string' => $query_search_string,
'query_search_filter' => $query_search_filter,
'sort' => $sort,
'mlt' => $mlt,
];
}
protected function flattenKeys(array $keys, $parse_mode = '', $full_text_fields = []) {
$conjunction = isset($keys['#conjunction']) ? $keys['#conjunction'] : 'AND';
$negation = !empty($keys['#negation']);
$values = [];
foreach ($keys as $key_nr => $key) {
if ($key_nr[0] === '#' || !$key) {
continue;
}
if (is_array($key)) {
$values[] = $this
->flattenKeys($key);
}
elseif (is_string($key)) {
if ($parse_mode
->getPluginId() !== 'direct') {
$key = '"' . $key . '"';
}
$values[] = $key;
}
}
if (!empty($values)) {
return ($negation === TRUE ? 'NOT ' : '') . '(' . implode(" {$conjunction} ", $values) . ')';
}
else {
return '';
}
}
protected function getSortSearchQuery() {
$index_fields = $this->index
->getFields();
$sort = [];
foreach ($this->query
->getSorts() as $field_id => $direction) {
$direction = Unicode::strtolower($direction);
if ($field_id === 'search_api_relevance') {
$sort['_score'] = $direction;
}
elseif ($field_id === 'search_api_id') {
$sort['id'] = $direction;
}
elseif (isset($index_fields[$field_id])) {
$sort[$field_id] = $direction;
}
else {
throw new \Exception(t('Incorrect sorting!.'));
}
}
return $sort;
}
protected function getQueryFilters(ConditionGroupInterface $condition_group, array $index_fields, $ignored_field_id = '') {
if (empty($condition_group)) {
return NULL;
}
else {
$conjunction = $condition_group
->getConjunction();
$filters = [];
foreach ($condition_group
->getConditions() as $condition) {
$filter = NULL;
if ($condition instanceof Condition) {
if (!$condition
->getField() || !$condition
->getValue() || !$condition
->getOperator()) {
}
$field_id = $condition
->getField();
if (!isset($index_fields[$field_id])) {
throw new \Exception(t(':field_id Undefined field ! Incorrect filter criteria is using for searching!', [
':field_id' => $field_id,
]));
}
if (!$condition
->getOperator()) {
throw new \Exception(t('Empty filter operator for :field_id field! Incorrect filter criteria is using for searching!', [
':field_id' => $field_id,
]));
}
$filter = FilterFactory::filterFromCondition($condition);
if (!empty($filter)) {
$filters[] = $filter;
}
}
elseif ($condition instanceof ConditionGroupInterface) {
$nested_filters = $this
->getQueryFilters($condition, $index_fields, $ignored_field_id);
if (!empty($nested_filters)) {
$filters = array_merge($filters, $nested_filters);
}
}
}
$filters = $this
->setFiltersConjunction($filters, $conjunction);
return $filters;
}
}
protected function setFiltersConjunction(array &$filters, $conjunction) {
if (count($filters) > 1) {
if ($conjunction === 'OR') {
$filters = [
[
'or' => $filters,
],
];
}
elseif ($conjunction === 'AND') {
$filters = [
[
'and' => $filters,
],
];
}
else {
throw new \Exception(t('Undefined conjunction :conjunction! Available values are :avail_conjunction! Incorrect filter criteria is using for searching!', [
':conjunction!' => $conjunction,
':avail_conjunction' => $conjunction,
]));
}
}
return $filters;
}
}