123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Elasticsearch\SearchAdapter\Query\Builder;
- use Magento\Framework\Search\Request\Query\BoolExpression;
- use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface;
- use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface;
- use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface;
- /**
- * Builder for match query.
- */
- class Match implements QueryInterface
- {
- /**
- * Elasticsearch condition for case when query must not appear in the matching documents.
- */
- const QUERY_CONDITION_MUST_NOT = 'must_not';
- /**
- * @var FieldMapperInterface
- */
- private $fieldMapper;
- /**
- * @var PreprocessorInterface[]
- */
- protected $preprocessorContainer;
- /**
- * @param FieldMapperInterface $fieldMapper
- * @param PreprocessorInterface[] $preprocessorContainer
- */
- public function __construct(
- FieldMapperInterface $fieldMapper,
- array $preprocessorContainer
- ) {
- $this->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);
- }
- }
|