magento2opensearchsynonym

Magento 2.4.6 Opensearch Search synonyms


I am trying to make use of search synonyms. I have created a synonym group, and added synonyms comma separated, and afterwards ran full re index.

enter image description here

Now I am testing this out with the following API call

http://website/rest/V1/products?searchCriteria[filter_groups][0][filters][0][field]=name&searchCriteria[filter_groups][0][filters][0][value]=%25cucumber%25&searchCriteria[filter_groups][0][filters][0][condition_type]=like&searchCriteria[pageSize]=5&searchCriteria[currentPage]=1

This is returning data. But if I replace 'cucumber' with the synonyms , if returns no results. I am using open search as my search engine, and I found this block of code in vendor/magento/module-open-search/etc/search_engine.xml

<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd">
    <engine name="opensearch">
        <feature name="synonyms" support="true" />
    </engine>
</engines>

I am not sure if I am missing anything or doing it wrong.


Solution

  • As I couldn't find any article online which tells me why search criteria api doesn't work with synonyms, did my workaround. Sharing if this helps anyone in future. Created my custom search api.

        /**
         * Check if query string has synonyms set
         * 
         * @param  string       $queryString
         * @return array|bool
         */
        private function checkForSynonyms($queryString,$processResponse = false)
        {
            $analyzedData = $synonyms = [];
            /**
             * @var \Magento\Search\Api\SynonymAnalyzerInterface
             */
            $analyzedData = $this->synonymAnalyzerInterface->getSynonymsForPhrase($queryString);
            foreach($analyzedData as $synm):
                if(count($synm) > 1):
                    $synonyms = $synm;
                endif;
            endforeach;
    
            return $processResponse ? $this->processSynonymResponse($queryString,$synonyms) : $synonyms ?? false;
        }
    
        /**
         * Prepare synonyms for api call
         * 
         * @param  string       $queryString
         * @param  array        $synonyms
         * @return array|bool
         */
        private function processSynonymResponse($queryString,$synonyms = [])
        {
            $querySynm = null;
            $searchStrings = [];
            if($synonyms):
                foreach($synonyms as $synonym) {
                    if(str_contains($queryString, $synonym)) 
                        $querySynm = $synonym;
                }
    
                foreach($synonyms as $synm) {
                    if($querySynm && $synm != $querySynm) {
                        $searchStrings[] = str_replace($querySynm, $synm, $queryString);
                    }
                }
                array_push($searchStrings, $queryString);
    
                return $searchStrings;
            endif;
    
            return false;
        }
    

    Called the above function in search api, and added the synonyms as OR conditions

        $synonym = $this->checkForSynonyms(strtolower($queryString),true);
        if($synonym):
            foreach($synonym as $synmQuery):
                $filters[] = $this->filterBuilder->setField('name')
                        ->setValue('%'.$synmQuery.'%')
                        ->setConditionType('like')->create();
            endforeach;
    
            foreach($filters as $filter) {
                $this->filterGroupBuilder->addFilter($filter);
            }
    
            $filterGrpBuilder = $this->filterGroupBuilder->create();
            $this->searchCriteriaBuilder->setFilterGroups([$filterGrpBuilder]);
        else:
            $this->searchCriteriaBuilder->addFilter('name', '%'.$queryString.'%', 'like');
        endif;
    

    I have not tested this on different scenarios, but this works for my case.