Your IP : 127.0.0.1


Current Path : /home/Mirasvit/-Search/Service/
Upload File :
Current File : /home/Mirasvit/-Search/Service/QueryService.php

<?php
/**
 * Mirasvit
 *
 * This source file is subject to the Mirasvit Software License, which is available at https://mirasvit.com/license/.
 * Do not edit or add to this file if you wish to upgrade the to newer versions in the future.
 * If you wish to customize this module for your needs.
 * Please refer to http://www.magentocommerce.com for more information.
 *
 * @category  Mirasvit
 * @package   mirasvit/module-search
 * @version   1.0.151
 * @copyright Copyright (C) 2020 Mirasvit (https://mirasvit.com/)
 */



namespace Mirasvit\Search\Service;

use Magento\Store\Model\StoreManagerInterface;
use Mirasvit\Search\Api\Service\QueryServiceInterface;
use Mirasvit\Search\Api\Service\StemmingServiceInterface;
use Mirasvit\Search\Api\Service\StopwordServiceInterface;
use Mirasvit\Search\Api\Service\SynonymServiceInterface;
use Mirasvit\Search\Model\Config;

class QueryService implements QueryServiceInterface
{
    /**
     * @var array
     */
    private static $cache = [];

    /**
     * @var Config
     */
    private $config;

    /**
     * @var StopwordServiceInterface
     */
    private $stopwordService;

    /**
     * @var SynonymServiceInterface
     */
    private $synonymService;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var StemmingServiceInterface
     */
    private $stemmingService;

    /**
     * QueryService constructor.
     * @param Config $config
     * @param StopwordServiceInterface $stopwordService
     * @param SynonymServiceInterface $synonymService
     * @param StoreManagerInterface $storeManager
     * @param StemmingServiceInterface $stemmingService
     */
    public function __construct(
        Config $config,
        StopwordServiceInterface $stopwordService,
        SynonymServiceInterface $synonymService,
        StoreManagerInterface $storeManager,
        StemmingServiceInterface $stemmingService
    ) {
        $this->config = $config;
        $this->stopwordService = $stopwordService;
        $this->synonymService = $synonymService;
        $this->storeManager = $storeManager;
        $this->stemmingService = $stemmingService;
    }

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     */
    public function build($query)
    {
        $query = urldecode($query);
        $storeId = $this->storeManager->getStore()->getId();

        if (function_exists('mb_strtolower')) {
            $query = mb_strtolower($query);
        } else {
            $query = strtolower($query);
        }

        $identifier = $storeId . $query;

        if (!array_key_exists($identifier, self::$cache)) {
            // required if synonym contains more 1 word
            $query = ' ' . $query . ' ';

            $result = [];

            $replaceWords = $this->config->getReplaceWords();

            foreach ($replaceWords as $replacement) {
                $query = str_replace(' ' . $replacement['from'] . ' ', ' ' . $replacement['to'] . ' ', $query);
            }

            $terms = preg_split('#\s#siu', $query, null, PREG_SPLIT_NO_EMPTY);

            $arSynonyms = $this->synonymService->getSynonyms($terms, $storeId);

            $condition = '$like';
            foreach ($terms as $term) {
                if (in_array($term, $this->config->getNotWords())) {
                    $condition = '$!like';
                    continue;
                }

                if ($this->stopwordService->isStopword($term, $storeId)) {
                    continue;
                }

                $wordArr = [];
                $this->addTerms($wordArr, [$term]);

                if ($condition == '$like') {
                    $this->addTerms($wordArr, [$this->applyLongTail($term)]);
                    $this->addTerms($wordArr, [$this->applyStemming($term)]);

                    if (isset($arSynonyms[$term])) {
                        # for synonyms we always disable wildcards
                        $this->addTerms($wordArr, $arSynonyms[$term], Config::WILDCARD_DISABLED);
                    }

                    if ($this->config->getMatchMode() == Config::MATCH_MODE_OR) {
                        $mode = '$or';
                    } else {
                        $mode = '$and';
                    }

                    $result[$condition][$mode][] = ['$or' => $wordArr];
                } else {
                    $result[$condition]['$and'][] = ['$and' => $wordArr];
                }
            }

            self::$cache[$identifier] = $result;
        }

        return self::$cache[$identifier];
    }

    /**
     * @param array $to
     * @param array $terms
     * @param int|null $wildcard
     * @return void
     */
    private function addTerms(array &$to, array $terms, $wildcard = null)
    {
        $exceptions = $this->config->getWildcardExceptions();
        if ($wildcard == null) {
            $wildcard = $this->config->getWildcardMode();
        }

        foreach ($terms as $term) {
            $term = trim($term);

            if ($term == '') {
                continue;
            }

            if ($wildcard == Config::WILDCARD_PREFIX) {
                $item = [
                    '$phrase'   => $term,
                    '$wildcard' => Config::WILDCARD_PREFIX,
                ];
            } elseif ($wildcard == Config::WILDCARD_SUFFIX) {
                $item = [
                    '$phrase'   => $term,
                    '$wildcard' => Config::WILDCARD_SUFFIX,
                ];
            } elseif ($wildcard == Config::WILDCARD_DISABLED || in_array($term, $exceptions)) {
                $item = [
                    '$phrase'   => $term,
                    '$wildcard' => Config::WILDCARD_DISABLED,
                ];
            } else {
                $item = [
                    '$phrase'   => $term,
                    '$wildcard' => Config::WILDCARD_INFIX,
                ];
            }

            $to[implode(array_values($item))]['$term'] = $item;
        }
    }

    /**
     * Apply long tail expression for word
     *
     * @param string $term
     *
     * @return string
     */
    private function applyLongTail($term)
    {
        $expressions = $this->config->getLongTailExpressions();

        foreach ($expressions as $expr) {
            $matches = null;
            preg_match_all($expr['match_expr'], $term, $matches);

            foreach ($matches[0] as $math) {
                $math = preg_replace($expr['replace_expr'], $expr['replace_char'], $math);
                if ($math) {
                    $term = $math;
                }
            }
        }

        return $term;
    }

    /**
     * @param string $term
     * @return string
     */
    private function applyStemming($term)
    {
        return $this->stemmingService->singularize($term);
    }
}