fieldMapper = $fieldMapper; $this->preprocessorContainer = $preprocessorContainer; } /** * @inheritdoc */ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $conditionType) { $queryValue = $this->prepareQuery($requestQuery->getValue(), $conditionType); $queries = $this->buildQueries($requestQuery->getMatches(), $queryValue); $requestQueryBoost = $requestQuery->getBoost() ?: 1; foreach ($queries as $query) { $queryBody = $query['body']; $matchKey = isset($queryBody['match_phrase']) ? 'match_phrase' : 'match'; foreach ($queryBody[$matchKey] as $field => $matchQuery) { $matchQuery['boost'] = $requestQueryBoost + $matchQuery['boost']; $queryBody[$matchKey][$field] = $matchQuery; } $selectQuery['bool'][$query['condition']][] = $queryBody; } return $selectQuery; } /** * Prepare query. * * @param string $queryValue * @param string $conditionType * @return array */ protected function prepareQuery($queryValue, $conditionType) { $queryValue = $this->escape($queryValue); foreach ($this->preprocessorContainer as $preprocessor) { $queryValue = $preprocessor->process($queryValue); } $condition = $conditionType === BoolExpression::QUERY_CONDITION_NOT ? self::QUERY_CONDITION_MUST_NOT : $conditionType; return [ 'condition' => $condition, 'value' => $queryValue, ]; } /** * Creates valid ElasticSearch search conditions from Match queries. * * The purpose of this method is to create a structure which represents valid search query * for a full-text search. * It sets search query condition, the search query itself, and sets the search query boost. * * The search query boost is an optional in the search query and therefore it will be set to 1 by default * if none passed with a match query. * * @param array $matches * @param array $queryValue * @return array */ protected function buildQueries(array $matches, array $queryValue) { $conditions = []; // Checking for quoted phrase \"phrase test\", trim escaped surrounding quotes if found $count = 0; $value = preg_replace('#^\\\\"(.*)\\\\"$#m', '$1', $queryValue['value'], -1, $count); $condition = ($count) ? 'match_phrase' : 'match'; foreach ($matches as $match) { $resolvedField = $this->fieldMapper->getFieldName( $match['field'], ['type' => FieldMapperInterface::TYPE_QUERY] ); $conditions[] = [ 'condition' => $queryValue['condition'], 'body' => [ $condition => [ $resolvedField => [ 'query' => $value, 'boost' => isset($match['boost']) ? $match['boost'] : 1, ], ], ], ]; } return $conditions; } /** * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. * * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error. * https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs. * * @param string $value * @return string */ protected function escape($value) { $value = preg_replace('/@+|[@+-]+$/', '', $value); $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/'; $replace = '\\\$1'; return preg_replace($pattern, $replace, $value); } }