cancel
Showing results for 
Search instead for 
Did you mean: 

CMS-Page in Elasticsearch

CMS-Page in Elasticsearch

I have the following question. I have saved all CMS pages in Elasticsearch. It works correctly. There are two indexes in ES, each for a store. All the fields that I have defined are also in the index. I can output them all with CURL -X GET commands and also search in the index and I get the correct results.

If I search like this:

<?php

namespace Vendor\Cms\Model\Page;

use Magento\AdvancedSearch\Model\Client\ClientInterface;
use Magento\Elasticsearch\SearchAdapter\ConnectionManager;
use Psr\Log\LoggerInterface;


class CmsPageSearch
{
    protected ClientInterface $client;

    public function __construct(
        protected ConnectionManager $connectionManager,
        private readonly LoggerInterface $logger
    ) {
        $this->client = $connectionManager->getConnection();
    }

    public function searchCmsPages(string $termToSearch, string $store): array
    {
        $fields = [
            'identifier',
            'title',
            'page_layout',
            'meta_title',
            'meta_keywords',
            'meta_description',
            'content_heading',
            'content'
        ];

       
        $indexPrefix = 'magento2';
        $params = [
            'index' => $indexPrefix . '_cms_page_' . $store,
            'body' => [
                'query' => [
                    'bool' => [
                        'should' => [
                            [
                                'multi_match' => [
                                    'query' => $termToSearch,
                                    'type' => 'best_fields',
                                    'fields' => [
                                        'data.identifier',
                                        'data.title',
                                        'data.page_layout',
                                        'data.meta_title',
                                        'data.meta_keywords',
                                        'data.meta_description',
                                        'data.content_heading',
                                        'data.content'
                                    ],
                                    'analyzer' => 'standard'
                                ],
                            ],
                            [
                                'fuzzy' => [
                                    'data.title' => [
                                        'value' => $termToSearch,
                                        'fuzziness' => 'AUTO',
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
            'size' => 5,
            '_source' => ['data.title', 'data.identifier'] 
        ];

        try {
            $response = $this->client->query($params);
            $this->logger->info(print_r($response, true));
            if (!empty($response['hits']['hits'])) {
                foreach ($response['hits']['hits'] as $hit) {
                    $this->logger->info('Gefundene Seite: ' . print_r($hit['_source'], true));
                }
            } else {
                $this->logger->info('Keine Treffer gefunden.');
            }
            return $response['hits']['hits'];
        } catch (\Exception $e) {
            $this->logger->error($e->getMessage());
            return [];
        }
    }
}

then it works too. But it should also work on Magento, like it does with full-text product searches. I tried that:

<?php

namespace Vendor\Cms\Model;

use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolverInterface;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolverFactory;
use Magento\Framework\Api\Search\DocumentInterface;
use Magento\Framework\Api\Search\SearchCriteriaBuilder;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Search\Api\SearchInterface;
use Magento\Cms\Model\ResourceModel\Page\CollectionFactory;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\Api\Search\SearchResultInterface;
use Psr\Log\LoggerInterface;

class CmsPagesSearch
{

    public function __construct(
        protected SearchCriteriaBuilder $searchCriteriaBuilder,
        protected SearchCriteriaResolverFactory $searchCriteriaResolverFactory,
        protected FilterBuilder $filterBuilder,
        protected SearchInterface $search,
        protected CollectionFactory $cmsCollectionFactory,
        protected SearchResultInterface $searchResult,
        protected StoreManagerInterface $storeManager,
        protected LoggerInterface $logger
    ) {
    }

    /**
     * Retrieve search result from elastic search
     *
     * @param string $query
     * @return DocumentInterface[]
     * @throws NoSuchEntityException
     */
    public function search(string $query): array
    {
        $searchCriteria = $this->getSearchCriteriaResolver($query)->resolve();
       // $this->logger->info('Search Criteria: ' . json_encode($searchCriteria->getData()));
        $this->searchResult =  $this->search->search($searchCriteria);

        return $this->searchResult->getItems();
    }

    /**
     * @throws NoSuchEntityException
     */
    private function getSearchCriteriaResolver($query): SearchCriteriaResolverInterface
    {
        $storeId = $this->storeManager->getStore()->getId();
        $this->filterBuilder->setField('search_term');
        $this->filterBuilder->setValue($query);
        $searchTermFilter = $this->filterBuilder->create();

        $this->filterBuilder->setField('store_id');
        $this->filterBuilder->setValue($storeId);
        $storeIdFilter = $this->filterBuilder->create();

        $this->searchCriteriaBuilder->addFilter($searchTermFilter);
        $this->searchCriteriaBuilder->addFilter($storeIdFilter);

        return $this->searchCriteriaResolverFactory->create(
            [
                'builder' => $this->searchCriteriaBuilder,
                'collection' => $this->cmsCollectionFactory->create(),
                'searchRequestName' => 'cms_search_container',
            ]
        );
    }
}

Here is 

'cms_search_container'

a container that I defined like this 

search_request.xml

<?xml version="1.0"?>
<requests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_request.xsd">
    <request query="cms_search_container" index="elastic_cms_page_fulltext">
        <dimensions>
            <dimension name="scope" value="default"/>
        </dimensions>
        <queries>
            <query xsi:type="boolQuery" name="cms_search_container" boost="1">
                <queryReference clause="should" ref="search" />
                <queryReference clause="must" ref="is_active"/>
            </query>
            <query xsi:type="matchQuery" value="$search_term$" name="search">
                <match field="*"/>
                <match field="title" matchCondition="match"/>
                <match field="content" matchCondition="match"/>
            </query>
            <query xsi:type="filteredQuery" name="is_active">
                <filterReference clause="must" ref="is_active_filter"/>
            </query>
        </queries>
        <filters>
            <filter xsi:type="termFilter" name="is_active_filter" field="is_active" value="1"/>
        </filters>
        <from>0</from>
        <size>10000</size>
    </request>
</requests>

and then

<?php

namespace Vendor\Cms\Controller\Search;

use Vendor\Cms\Model\CmsPagesSearch;
use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Model\StoreManagerInterface;

class Cmssearch implements HttpGetActionInterface
{

    public function __construct(
        protected JsonFactory $jsonFactory,
        protected CmsPageSearch $cmsPageSearch,
        protected RequestInterface $request,
        protected StoreManagerInterface $storeManager,
        protected CmsPagesSearch $cmsPagesSearch
    ) {
    }

    /**
     * Execute action based on request and return result
     *
     * @return ResultInterface|ResponseInterface
     * @throws NoSuchEntityException
     */
    public function execute(): ResultInterface|ResponseInterface
    {
     
        $query = $this->request->getParam(\Magento\Search\Model\QueryFactory::QUERY_VAR_NAME);
        $searchResults = $this->cmsPagesSearch->search($query);

        $result = $this->jsonFactory->create();
        $data = ['result' => $searchResults];
        return $result->setData($data);
    }
}

'elastic_cms_page_fulltext' -> indexer.xml and mview.xml

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Indexer/etc/indexer.xsd">
    <indexer id="elastic_cms_page_fulltext" view_id="elastic_cms_page_fulltext"
             class="Vendor\Cms\Model\Page\Indexer\Fulltext">
        <title translate="true">Elastic Cms Page Indexing</title>
        <description translate="true">Reindex Elastic cms pages.</description>
    </indexer>
</config>
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Mview/etc/mview.xsd">

    <view id="elastic_cms_page_fulltext" class="Vendor\Cms\Model\Page\Indexer\Fulltext" group="indexer">
        <subscriptions>
            <table name="cms_page" entity_column="page_id" />
        </subscriptions>
    </view>

</config>

indexer.xml and mview.xml are intended for importing data into ES and have nothing to do with the search. As I already wrote, storing data in ES works.

 

but it doesn't work. I get an empty response

 

What am I doing wrong, what have I forgotten or how can I use ES Search for CMS pages as well as for products? This should also work without the ElasticSuite from Smile. Please do not write links to commercial modules. Let's just discuss how this can be implemented and what I am doing wrong.

 

Many thanks

Vladimir