5

Under app/code/Magento/CatalogSearch/etc/ there's a search_request.xml file with the following content:

<requests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_request.xsd">
    <request query="quick_search_container" index="catalogsearch_fulltext">
        <dimensions>
            <dimension name="scope" value="default"/>
        </dimensions>
        <queries>
            <query xsi:type="boolQuery" name="quick_search_container" boost="1">
                <queryReference clause="should" ref="search" />
                <queryReference clause="must" ref="category"/>
                <queryReference clause="must" ref="price"/>
                <queryReference clause="must" ref="visibility"/>
            </query>
            <query xsi:type="matchQuery" value="$search_term$" name="search">
                <match field="sku"/>
                <match field="*"/>
            </query>
            <query xsi:type="filteredQuery" name="category">
                <filterReference clause="must" ref="category_filter"/>
            </query>
            <query xsi:type="filteredQuery" name="price">
                <filterReference clause="must" ref="price_filter"/>
            </query>
            <query xsi:type="filteredQuery" name="visibility">
                <filterReference clause="must" ref="visibility_filter"/>
            </query>
        </queries>
        <filters>
            <filter xsi:type="termFilter" name="category_filter" field="category_ids" value="$category_ids$"/>
            <filter xsi:type="rangeFilter" name="price_filter" field="price" from="$price.from$" to="$price.to$"/>
            <filter xsi:type="termFilter" name="visibility_filter" field="visibility" value="$visibility$"/>
        </filters>
        <aggregations>
            <bucket name="price_bucket" field="price" xsi:type="dynamicBucket" method="$price_dynamic_algorithm$">
                <metrics>
                    <metric type="count"/>
                </metrics>
            </bucket>
            <bucket name="category_bucket" field="category_ids" xsi:type="termBucket">
                <metrics>
                    <metric type="count"/>
                </metrics>
            </bucket>
        </aggregations>
        <from>0</from>
        <size>10000</size>
    </request>
    <request query="advanced_search_container" index="catalogsearch_fulltext">
        <dimensions>
            <dimension name="scope" value="default"/>
        </dimensions>
        <queries>
            <query xsi:type="boolQuery" name="advanced_search_container" boost="1">
                <queryReference clause="should" ref="sku_query"/>
                <queryReference clause="should" ref="price_query"/>
                <queryReference clause="should" ref="category_query"/>
            </query>
            <query name="sku_query" xsi:type="filteredQuery">
                <filterReference clause="must" ref="sku_query_filter"/>
            </query>
            <query name="price_query" xsi:type="filteredQuery">
                <filterReference clause="must" ref="price_query_filter"/>
            </query>
            <query name="category_query" xsi:type="filteredQuery">
                <filterReference clause="must" ref="category_filter"/>
            </query>
        </queries>
        <filters>
            <filter xsi:type="wildcardFilter" name="sku_query_filter" field="sku" value="$sku$"/>
            <filter xsi:type="rangeFilter" name="price_query_filter" field="price" from="$price.from$" to="$price.to$"/>
            <filter xsi:type="termFilter" name="category_filter" field="category_ids" value="$category_ids$"/>
        </filters>
        <from>0</from>
        <size>10000</size>
    </request>
    <request query="catalog_view_container" index="catalogsearch_fulltext">
        <dimensions>
            <dimension name="scope" value="default"/>
        </dimensions>
        <queries>
            <query xsi:type="boolQuery" name="catalog_view_container" boost="1">
                <queryReference clause="must" ref="category"/>
                <queryReference clause="must" ref="price"/>
                <queryReference clause="must" ref="visibility"/>
            </query>
            <query xsi:type="filteredQuery" name="category">
                <filterReference clause="must" ref="category_filter"/>
            </query>
            <query xsi:type="filteredQuery" name="price">
                <filterReference clause="must" ref="price_filter"/>
            </query>
            <query xsi:type="filteredQuery" name="visibility">
                <filterReference clause="must" ref="visibility_filter"/>
            </query>
        </queries>
        <filters>
            <filter xsi:type="termFilter" name="category_filter" field="category_ids" value="$category_ids$"/>
            <filter xsi:type="rangeFilter" name="price_filter" field="price" from="$price.from$" to="$price.to$"/>
            <filter xsi:type="termFilter" name="visibility_filter" field="visibility" value="$visibility$"/>
        </filters>
        <aggregations>
            <bucket name="price_bucket" field="price" xsi:type="dynamicBucket" method="$price_dynamic_algorithm$">
                <metrics>
                    <metric type="count"/>
                </metrics>
            </bucket>
            <bucket name="category_bucket" field="category_ids" xsi:type="termBucket">
                <metrics>
                    <metric type="count"/>
                </metrics>
            </bucket>
        </aggregations>
        <from>0</from>
        <size>10000</size>
    </request>
</requests>

I'm assuming it is used for the search, I'm wondering how Magento processes this file and what are the following fields used for:

  • request
  • dimensions
  • queries
  • filters
  • aggregations
  • from
  • size
8

Magento 2 provides a declarative way to describe search request which should be executed. search_request.xml is a part of Query API. This declaration describes:

  1. What index should be queried
  2. What full-text queries should be executed (queries section)
  3. What filters should be applied (filters section)
  4. For which attributes faceted search should be built (Layered Navigation in terms of Magento) (aggregations section)

Naming convention here is very similar to ElasticSearch. For example, if you will look at XSD file (Search/etc/search_request.xsd) you will see that there are three possible query types:

  1. Bool Query (analogue for Elasticsearch BoolQuery)
  2. Match Query (analogue for Elasticsearch Match Query)
  3. Filter Query (analogue for Elasticsearch Filtered Query)

There is presentation which describes Magento 2 new Search approach and architecture

For now Magento indexes just product data. And there are 3 scenarios (read 3 search queries for that data):

  1. Quick Search (<request query="quick_search_container" index="catalogsearch_fulltext">)
  2. Advanced Search (<request query="advanced_search_container" index="catalogsearch_fulltext">)
  3. Category View (<request query="catalog_view_container" index="catalogsearch_fulltext">). As Magento implies Category View scenatio - as a search query without full text query in it.
| improve this answer | |
  • 1
    Be careful, Magento 2 ElasticSearch integration is limited and you will not be able to implement specific search request out of the box. Such as specific filter type, bucket size etc. Maybe it will be added in the next releases. – Franck Garnier Aug 8 '17 at 15:14
  • @FranckGarnier you can always do that (implement specific filter type, use dedicated analyzer etc.) customizing out of the box Magento 2 implementation. That's the role of developers who make Magento 2 customizations. Magento needs to provide enough extension points to make these customizations easy – Igor Minyaylo Aug 9 '17 at 18:08
  • Yes I did, but the Magento 2 interface is sometime time consuming to extend without breakchanges. – Franck Garnier Aug 10 '17 at 11:03
4

Looks like it's used to define attributes certain filters should be applied to and then searchable attributes are applied against this XML to configure the searchRequest - see:

Magento\CatalogSearch\Model\Search\RequestGenerator

private function generateRequest($attributeType, $container, $useFulltext)
{
    $request = [];
    foreach ($this->getSearchableAttributes() as $attribute) {
        if ($attribute->getData($attributeType)) {
            if (!in_array($attribute->getAttributeCode(), ['price', 'category_ids'])) {
                $queryName = $attribute->getAttributeCode() . '_query';

                $request['queries'][$container]['queryReference'][] = [
                    'clause' => 'should',
                    'ref' => $queryName,
                ];
                $filterName = $attribute->getAttributeCode() . self::FILTER_SUFFIX;
                $request['queries'][$queryName] = [
                    'name' => $queryName,
                    'type' => QueryInterface::TYPE_FILTER,
                    'filterReference' => [['ref' => $filterName]],
                ];
                $bucketName = $attribute->getAttributeCode() . self::BUCKET_SUFFIX;
                if ($attribute->getBackendType() == 'decimal') {
                    $request['filters'][$filterName] = [
                        'type' => FilterInterface::TYPE_RANGE,
                        'name' => $filterName,
                        'field' => $attribute->getAttributeCode(),
                        'from' => '$' . $attribute->getAttributeCode() . '.from$',
                        'to' => '$' . $attribute->getAttributeCode() . '.to$',
                    ];
                    $request['aggregations'][$bucketName] = [
                        'type' => BucketInterface::TYPE_DYNAMIC,
                        'name' => $bucketName,
                        'field' => $attribute->getAttributeCode(),
                        'method' => 'manual',
                        'metric' => [["type" => "count"]],
                    ];
                } else {
                    $request['filters'][$filterName] = [
                        'type' => FilterInterface::TYPE_TERM,
                        'name' => $filterName,
                        'field' => $attribute->getAttributeCode(),
                        'value' => '$' . $attribute->getAttributeCode() . '$',
                    ];
                    $request['aggregations'][$bucketName] = [
                        'type' => BucketInterface::TYPE_TERM,
                        'name' => $bucketName,
                        'field' => $attribute->getAttributeCode(),
                        'metric' => [["type" => "count"]],
                    ];
                }
            }
        }
        /** @var $attribute Attribute */
        if (in_array($attribute->getAttributeCode(), ['price', 'sku'])
            || !$attribute->getIsSearchable()
        ) {
            //same fields have special semantics
            continue;
        }
        if ($useFulltext) {
            $request['queries']['search']['match'][] = [
                'field' => $attribute->getAttributeCode(),
                'boost' => $attribute->getSearchWeight() ?: 1,
            ];
        }
    }
    return $request;
}
| improve this answer | |
  • yes, this request generator adds dynamic searchable attributes and filters/layered nav for them at a time of reading search_request.xml , because product attributes could be added/removed/changed dynamically by merchant, so it's not possible to specify filters by these attributes in advanced. – Igor Minyaylo May 12 '16 at 16:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.