How does Elastic Search work with Magento?

1. Overview

Elasticsearch is used as a search engine in Magento. Since Magento 2.4, Elasticsearch is a required component and installation of Magento without Elasticsearch is not possible.

To integrate Magento with Elasticsearch, the following steps are needed:

The version of Elasticsearch should be compatible with the Elasticsearch library and Magento. Magento has a separate module for each Elasticsearch version. Not every version is supported.

By default, there is only one index in Elasticsearch for each Magento store – search index for products. All existing query types use the same index. There are no indexes for other entities such as categories, customers or CMS pages.

It is possible to create other indexes in Elasticsearch and new query types using these indexes, although it seems quite a large effort.

Magento ignores any indexes in Elasticsearch not defined in Magento, so it is possible to use the same instance of Elasticsearch for other purposes.

Elasticsearch is used mainly in storefront. Regular search in admin panel does not use Elasticsearch.

2. Reindex

Data in Elasticsearch is always secondary data, synchronized with data in MySQL database. The process of synchronization is called reindex. Magento provides different ways of making reindex:

  1. Full reindex – which can be triggered manually by a developer – in command line or in code
  2. Partial reindex – reindex of only those products that actually changed
    1. on save – reindex happens immediately when product is saved
    2. on schedule – saved product is only added to the queue of products waiting for reindex. A background process that runs every minute makes reindex of all products in queue asynchronously. This is a better option considering performance

The only Magento Indexer related to Elasticsearch is Catalog Search.

A product record in Elasticsearch looks like that:

{
   "store_id":"1",
   "sku":"24-MB01",
   "status":1,
   "status_value":"Enabled",
   "visibility":4,
   "name":"joust duffle bag",
   "url_key":"joust-duffle-bag",
   "description":"The sporty Joust Duffle Bag can't be beat - not in the gym, not on the luggage carousel, not anywhere. Big enough to haul a basketball or soccer ball and some sneakers with plenty of room to spare, it's ideal for athletes with places to go. Dual top handles. Adjustable shoulder strap. Full-length zipper. L 29\" x W 13\" x H 11\".",
   "category_ids":[
      2,
      3,
      4
   ],
   "position_category_2":"0",
   "name_category_2":"Default Category",
   "position_category_3":"0",
   "name_category_3":"Gear",
   "position_category_4":"0",
   "name_category_4":"Bags",
   "price_0_1":"34.000000",
   "price_1_1":"34.000000",
   "price_2_1":"34.000000",
   "price_3_1":"34.000000"
}

To index additional product data in Elasticsearch, the simplest solution is to create a product EAV attribute with proper parameters (e.g. Use n Search = Yes, or Visible in Advanced Search = Yes).

It is also possible to create a fake product attribute (static EAV attribute without actual column in database) and index an arbitrary value using a Magento plugin.

3. Search Request

Requests to Elasticsearch are built based on configuration in search_request.xml files.

The files define different query types. By default, Magento uses the following query types:

In Magento Commerce B2B extension, there is one additional query type:

Main search_request.xml file looks like that:

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<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="should" ref="partial_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="*"/>
            </query>
            <query xsi:type="matchQuery" value="$search_term$" name="partial_search">
                <match field="*"/>
                <match field="name" matchCondition="match_phrase_prefix"/>
                <match field="sku" matchCondition="match_phrase_prefix"/>
            </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"/>
                <queryReference clause="must" ref="visibility_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>
            <query name="visibility_query" xsi:type="filteredQuery">
                <filterReference clause="must" ref="visibility_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$"/>
            <filter xsi:type="termFilter" name="visibility_filter" field="visibility" value="$visibility$"/>
        </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>

The xml mentions some product fields like visibility. However, since Magento allows creating custom product attributes, even in the admin panel, query generated by this xml is further modified to include all searchable product attributes.

This is done by a plugin to xml reader:

Magento\CatalogSearch\Model\Search\ReaderPlugin::afterRead()
$result = array_merge_recursive(
  $xmlData,
  $this->requestGenerator->generate()
);

In RequestGenerator::generate(), Magento adds some dynamic changes to the search request, including clauses related to product attributes. It is possible to add additional changes to the request using a similar mechanism.

This plugin is executed once and cached. To see changes implemented in search_request.xml or added by plugins, it is necessary to clean cache (so this way, it is not possible to use different requests depending on customer, current date etc., although there may be other ways of implementing this).

Based on the xml and a list of searchable attributes, Magento sends a request to Elasticsearch, which may look as follows:

{
   "from":0,
   "size":12,
   "query":{
      "bool":{
         "must":[
            {
               "terms":{
                  "visibility":[
                     "3",
                     "4"
                  ],
                  "boost":1.0
               }
            }
         ],
         "should":[
            {
               "match":{
                  "_search":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "name":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":6.0
                  }
               }
            },
            {
               "match":{
                  "sku":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":7.0
                  }
               }
            },
            {
               "match":{
                  "description":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "short_description":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "manufacturer_value":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "status_value":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "url_key":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "tax_class_id_value":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "_search":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match_phrase_prefix":{
                  "name":{
                     "query":"my_search_term",
                     "slop":0,
                     "max_expansions":50,
                     "boost":2.0
                  }
               }
            },
            {
               "match_phrase_prefix":{
                  "sku":{
                     "query":"my_search_term",
                     "slop":0,
                     "max_expansions":50,
                     "boost":2.0
                  }
               }
            }
         ],
         "adjust_pure_negative":true,
         "minimum_should_match":"1",
         "boost":1.0
      }
   },
   "stored_fields":[
      "_id",
      "_score"
   ],
   "sort":[
      {
         "_score":{
            "order":"desc"
         }
      }
   ],
   "track_total_hits":2147483647,
   "aggregations":{
      "price_bucket":{
         "extended_stats":{
            "field":"price_0_1",
            "sigma":2.0
         }
      },
      "category_bucket":{
         "terms":{
            "field":"category_ids",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      }
   }
}

The format of search_request.xml allows the use of the following Elasticsearch queries:

The number of search results is configured in search_request.xml, e.g. <size>10</size>. Default: 10 000. 10 000 is also a default limit in Elasticsearch (index.max_result_window). If it is necessary to return more than 10 000 results, changes in code and Elasticsearch configuration may be needed.

4. Category view page

Viewing the category page is basically a kind of search and the logic is very similar to the logic of quick search.

The basic mechanism is as follows:

Additionally, Magento uses Elasticsearch to get information about the number of matching products next to each option in layered navigation.

Example: customer enters category page Bags and selects some filters in layered navigation, e.g. Price $40.00-49.99

Category and filters are added to request url, e.g. http://mystore.pl/gear/bags.html?price=40-50

Magento parses the url parameters and makes request to Elasticsearch to get 2 pieces of information:

The request may look like that:

{
   "from":0,
   "size":12,
   "query":{
      "bool":{
         "must":[
            {
               "term":{
                  "category_ids":{
                     "value":"4",
                     "boost":1.0
                  }
               }
            },
            {
               "range":{
                  "price_0_1":{
                     "from":"40",
                     "to":"49.999",
                     "include_lower":true,
                     "include_upper":true,
                     "boost":1.0
                  }
               }
            },
            {
               "terms":{
                  "visibility":[
                     "2",
                     "4"
                  ],
                  "boost":1.0
               }
            }
         ],
         "adjust_pure_negative":true,
         "boost":1.0
      }
   },
   "stored_fields":[
      "_id",
      "_score"
   ],
   "sort":[
      {
         "position_category_4":{
            "order":"asc"
         }
      }
   ],
   "track_total_hits":2147483647,
   "aggregations":{
      "price_bucket":{
         "extended_stats":{
            "field":"price_0_1",
            "sigma":2.0
         }
      },
      "category_bucket":{
         "terms":{
            "field":"category_ids",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "manufacturer_bucket":{
         "terms":{
            "field":"manufacturer",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "color_bucket":{
         "terms":{
            "field":"color",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "activity_bucket":{
         "terms":{
            "field":"activity",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "style_bags_bucket":{
         "terms":{
            "field":"style_bags",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "material_bucket":{
         "terms":{
            "field":"material",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "strap_bags_bucket":{
         "terms":{
            "field":"strap_bags",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "features_bags_bucket":{
         "terms":{
            "field":"features_bags",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "gender_bucket":{
         "terms":{
            "field":"gender",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "category_gear_bucket":{
         "terms":{
            "field":"category_gear",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "size_bucket":{
         "terms":{
            "field":"size",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "eco_collection_bucket":{
         "terms":{
            "field":"eco_collection",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "performance_fabric_bucket":{
         "terms":{
            "field":"performance_fabric",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "erin_recommends_bucket":{
         "terms":{
            "field":"erin_recommends",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "new_bucket":{
         "terms":{
            "field":"new",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "sale_bucket":{
         "terms":{
            "field":"sale",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "format_bucket":{
         "terms":{
            "field":"format",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "purpose_bucket":{
         "terms":{
            "field":"purpose",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "style_bottom_bucket":{
         "terms":{
            "field":"style_bottom",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "style_general_bucket":{
         "terms":{
            "field":"style_general",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "sleeve_bucket":{
         "terms":{
            "field":"sleeve",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "collar_bucket":{
         "terms":{
            "field":"collar",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "pattern_bucket":{
         "terms":{
            "field":"pattern",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      },
      "climate_bucket":{
         "terms":{
            "field":"climate",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      }
   }
}

After receiving a list of products from Elasticsearch, Magento fetches the products from MySQL database. The SQL includes filter by ids from Elasticsearch + some additional filters. It may look like that:

SELECT `e`.*, 
       `price_index`.`price`, 
       `price_index`.`tax_class_id`, 
       `price_index`.`final_price`, 
       IF(price_index.tier_price IS NOT NULL, Least(price_index.min_price, 
                                              price_index.tier_price), 
       price_index.min_price)                   AS `minimal_price`, 
       `price_index`.`min_price`, 
       `price_index`.`max_price`, 
       `price_index`.`tier_price`, 
       Ifnull(review_summary.reviews_count, 0)  AS `reviews_count`, 
       Ifnull(review_summary.rating_summary, 0) AS `rating_summary`, 
       `stock_status_index`.`stock_status`      AS `is_salable` 
FROM   `catalog_product_entity` AS `e` 
       INNER JOIN `catalog_product_index_price` AS `price_index` 
               ON price_index.entity_id = e.entity_id 
                  AND price_index.customer_group_id = 0 
                  AND price_index.website_id = '1' 
       LEFT JOIN `review_entity_summary` AS `review_summary` 
              ON e.entity_id = review_summary.entity_pk_value 
                 AND review_summary.store_id = 1 
                 AND review_summary.entity_type = (SELECT 
                     `review_entity`.`entity_id` 
                                                   FROM   `review_entity` 
                                                   WHERE  ( entity_code = 
                                                            'product' )) 
       INNER JOIN `cataloginventory_stock_status` AS `stock_status_index` 
               ON e.entity_id = stock_status_index.product_id 
WHERE  ( ( stock_status_index.stock_status = 1 ) 
         AND ( e.entity_id IN ( 4, 5, 13, 14 ) ) ) 
       AND ( e.created_in <= 1 ) 
       AND ( e.updated_in > 1 ) 
ORDER  BY Field(e.entity_id, 4, 5, 13, 14);

In this SQL, the following part is based on the results from Elasticsearch:

e.entity_id IN ( 4, 5, 13, 14 ).

This is actually filter by category and price.

The logic of filtering based on choices in layered navigation is applied to Elasticsearch request only. The logic of filtering based on visibility (product visibility, category permissions etc.) is applied twice: first as part of Elasticsearch request, then again as part of the SQL. Therefore, it might happen that product that should be visible is not visible due to invalid data in Elasticsearch, but it should not happen that product that should be invisible is visible due to invalid data in Elasticsearch.

Custom code that applies additional visibility filters should also apply the filters twice. If the filter is applied to MySQL only, it is possible that the displayed number of products found will be higher than the actual number of products on the page.

After loading products, Magento builds the view of layered navigation. Each filter is a select with options. For each option, Magento presents the number of matching products. This number comes from Elasticsearch (aggregations).

In previous versions of Magento, there was a choice to use MySQL instead of Elasticsearch for fulltext search. At that time, MySQL EAV indexer was used to support layered navigation. According to Magento documentation, if the merchant uses Elasticsearch for fulltext search and there is no custom extension depending on EAV indexer, EAV indexer is not needed and can be disabled in the configuration:  Stores > Settings > Configuration > Catalog > Catalog > Catalog Search > Enable EAV Indexer.

Layered navigation can be hidden by a change in the layout XML file. However, the underlying logic of layered navigation cannot be disabled without considerable effort.

Product attributes are available in layered navigation based on their parameters

Only some data types and input types can be used in layered navigation. E.g. text attributes cannot be used in layered navigation.

If there is some mismatch between Elasticsearch and MySQL, the common indicators are:

Category page is cached by full page cache, so the search actually takes place only once, the first time any customer enters the page. Subsequent customers from the same group receive page from cache, so no request to Elasticsearch or MySQL is made. Flushing full page cache is necessary to refresh search results.

Each category has a parameter “Is Anchor”: Yes or No.

If there is a category structure like that:

Juices

  Orange juices

If Juices has the parameter “Is Anchor” = Yes, Juices will also show Orange Juices. If Juices has the parameter “Is Anchor” = No, Juices will only display products directly assigned to category Juices.

5. Quick Search

The basic mechanism is as follows:

Data kept in Elasticsearch is never really displayed on the frontend. It is only used for searching, but once the product is found, it is loaded from the MySQL database and all data comes from MySQL.

Example: if product name is Cat in MySQL and Dog in Elasticsearch:

If product is not indexed in Elasticsearch, it cannot be found.

Attributes used for search include some basic attributes + EAV attributes with proper properties, e.g. Use in Search – Yes

Request to Elasticsearch may look like that:

{
   "from":0,
   "size":12,
   "query":{
      "bool":{
         "must":[
            {
               "terms":{
                  "visibility":[
                     "3",
                     "4"
                  ],
                  "boost":1.0
               }
            }
         ],
         "should":[
            {
               "match":{
                  "_search":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "name":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":6.0
                  }
               }
            },
            {
               "match":{
                  "sku":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":7.0
                  }
               }
            },
            {
               "match":{
                  "description":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "short_description":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "manufacturer_value":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "status_value":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "url_key":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "tax_class_id_value":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match":{
                  "_search":{
                     "query":"my_search_term",
                     "operator":"OR",
                     "prefix_length":0,
                     "max_expansions":50,
                     "fuzzy_transpositions":true,
                     "lenient":false,
                     "zero_terms_query":"NONE",
                     "auto_generate_synonyms_phrase_query":true,
                     "boost":2.0
                  }
               }
            },
            {
               "match_phrase_prefix":{
                  "name":{
                     "query":"my_search_term",
                     "slop":0,
                     "max_expansions":50,
                     "boost":2.0
                  }
               }
            },
            {
               "match_phrase_prefix":{
                  "sku":{
                     "query":"my_search_term",
                     "slop":0,
                     "max_expansions":50,
                     "boost":2.0
                  }
               }
            }
         ],
         "adjust_pure_negative":true,
         "minimum_should_match":"1",
         "boost":1.0
      }
   },
   "stored_fields":[
      "_id",
      "_score"
   ],
   "sort":[
      {
         "_score":{
            "order":"desc"
         }
      }
   ],
   "track_total_hits":2147483647,
   "aggregations":{
      "price_bucket":{
         "extended_stats":{
            "field":"price_0_1",
            "sigma":2.0
         }
      },
      "category_bucket":{
         "terms":{
            "field":"category_ids",
            "size":500,
            "min_doc_count":1,
            "shard_min_doc_count":0,
            "show_term_doc_count_error":false,
            "order":[
               {
                  "_count":"desc"
               },
               {
                  "_key":"asc"
               }
            ]
         }
      }
   }
}

By default, Magento uses OR condition, so if you type “sku1 sku2”, you may find two products. If you type “orange juice”, you may find orange t-shirt and raspberry juice.

A search request sent to Elasticsearch includes many clauses, e.g. the name should be “Cat”, the description should be “Cat”, a short description should be “Cat”. It is enough for one clause to be true to find a  product. This depends on Elasticsearch parameter  minimum_should_match, which has a value of 1 hard-coded in Magento.

If you make a typo in a search term, Magento does not show the product in search results, but there may be a search suggestion (“Did you mean …?”) with a given product. If you type just the middle part of a word – the same effect as for typos. Search suggestions are a separate feature of Magento and they are provided by a separate request to Elasticsearch.

In Magento Commerce, there is a configuration for which customer groups can use search bar (Category Permissions > Disallow Catalog Search By).

The search results page has layered navigation that allows to further filter the results. There is a separate list of attributes that can be used in layered navigation in the category page vs. search result page.

6. Advanced Search

Advanced search is similar to quick search, but the customer can configure search details.

Attributes are displayed in advanced search form based on parameter: Visible in Advanced Search.

7. GraphQL

GraphQL is one of Magento APIs. It allows external systems to fetch data from Magento. GraphQL allows to get a list of products. There is an endpoint that allows to get a list of products based on fulltext search, which uses pretty much the same logic as search on storefront.

8. Search suggestions

Search Suggestions is an additional feature of Magento Commerce, it displays a block “Did you mean?” on the search results page.

Suggestions are loaded from Elasticsearch by a separate query that may look as follows:

{
   "suggest":{
      "text":"my_search_term",
      "phrase_style_general_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"style_general_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"style_general_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_style_bags_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"style_bags_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"style_bags_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_strap_bags_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"strap_bags_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"strap_bags_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_eco_collection_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"eco_collection_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"eco_collection_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_color_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"color_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"color_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_features_bags_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"features_bags_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"features_bags_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_allow_open_amount_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"allow_open_amount_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"allow_open_amount_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_country_of_manufacture_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"country_of_manufacture_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"country_of_manufacture_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_manufacturer_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"manufacturer_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"manufacturer_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_format_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"format_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"format_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_giftcard_type_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"giftcard_type_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"giftcard_type_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_climate_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"climate_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"climate_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_collar_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"collar_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"collar_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_description":{
         "phrase":{
            "analyzer":"standard",
            "field":"description",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"description",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_sale_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"sale_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"sale_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_price_view_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"price_view_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"price_view_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_custom_layout_update_file_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"custom_layout_update_file_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"custom_layout_update_file_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_is_returnable_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"is_returnable_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"is_returnable_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_page_layout_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"page_layout_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"page_layout_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_gift_wrapping_available_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"gift_wrapping_available_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"gift_wrapping_available_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_shipment_type_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"shipment_type_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"shipment_type_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_msrp_display_actual_price_type_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"msrp_display_actual_price_type_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"msrp_display_actual_price_type_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_purpose_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"purpose_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"purpose_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_size_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"size_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"size_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_status_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"status_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"status_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_custom_design_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"custom_design_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"custom_design_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_options_container_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"options_container_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"options_container_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_style_bottom_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"style_bottom_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"style_bottom_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_tax_class_id_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"tax_class_id_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"tax_class_id_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_short_description":{
         "phrase":{
            "analyzer":"standard",
            "field":"short_description",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"short_description",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_activity_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"activity_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"activity_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_custom_layout_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"custom_layout_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"custom_layout_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_quantity_and_stock_status_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"quantity_and_stock_status_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"quantity_and_stock_status_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_name":{
         "phrase":{
            "analyzer":"standard",
            "field":"name",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"name",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_category_gear_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"category_gear_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"category_gear_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_gender_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"gender_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"gender_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_sleeve_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"sleeve_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"sleeve_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_performance_fabric_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"performance_fabric_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"performance_fabric_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_material_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"material_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"material_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_pattern_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"pattern_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"pattern_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_erin_recommends_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"erin_recommends_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"erin_recommends_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_url_key":{
         "phrase":{
            "analyzer":"standard",
            "field":"url_key",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"url_key",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_visibility_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"visibility_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"visibility_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_sku":{
         "phrase":{
            "analyzer":"standard",
            "field":"sku",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"sku",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_gift_message_available_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"gift_message_available_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"gift_message_available_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      },
      "phrase_new_value":{
         "phrase":{
            "analyzer":"standard",
            "field":"new_value",
            "size":2,
            "real_word_error_likelihood":0.95,
            "confidence":1.0,
            "separator":" ",
            "max_errors":1.0,
            "force_unigrams":true,
            "token_limit":10,
            "direct_generator":[
               {
                  "field":"new_value",
                  "min_word_length":3,
                  "min_doc_freq":1.0
               }
            ]
         }
      }
   }
}

There is a hardcoded limit of max_errors = 1, so the feature will not provide relevant results when customer makes more than one typo.

Elasticsearch returns a list of suggestions with a score for each attribute separately (sku, name, description, …).

Suggestions are displayed even if there are correct results.

9. Additional search features

It is possible to see popular search terms in admin panel: Marketing > SEO & Search > Search Terms

This feature uses MySQL database only.

It is also possible to define a redirect for a search term, e.g. when a customer types “Juices”, he/she may be redirected to the category page of Juices (or any other page). However, there needs to be an exact match. This feature does not use Elasticsearch.

It is also possible to specify synonyms for search:

There is a feature of Search Recommendations (“Related search terms”), which is similar but different from search suggestions.

10. Vue Storefront

Vue Storefront is a frontend PWA application which can be connected to Magento. Vue Storefront uses Elasticsearch as NoSQL database. Entities from Magento are indexed in Elasticsearch, so that Vue Storefront can access them directly without requests to Magento.

Indexing of data from Magento is done by custom application mage2vuestorefront (https://github.com/vuestorefront/mage2vuestorefront) written in Javascript that fetches data from Magento using Magento Rest API. It supports many entities such as categories, product reviews, CMS pages.


Elasticsearch has a lot of strengths and is the most powerful search engine, available for free. It analyzes a high volume of data in almost real-time, providing users with needed results immediately. It provides a lot of features that are worth to be discovered and applied to your eCommerce store.
If you need any help with your store and would like to discuss how Elasticsearch can be used in your business, contact us and we will be pleased to help you.

What we learned at Magento Live 2019

1. B2B Order approval process

B2B sales are a complex beast. They are usually done with more than one decision maker & until now there was no way to handle this in Magento. With the new Order approval process (currently in advanced development) you will be able to create an order, which needs to be verified by the manager before placing the Purchase Order. Adobe focused on making it highly customizable. You have bulk approval options; It will also have API and the rules can be set up from the admin panel. Can’t wait for this feature.

2. Other changes in B2B

Magento did not give any hard promises on when they plan to deliver these features, but the stories in development look as follows:

*Adding quote filtering in My Quotes

*Searching the order history in My Orders

*Exporting the requisition lists to .csv files

*Quick adding items to requisition lists

*Creating the requisition lists from the cart

*Login as Customer to frontend (so-called “ghost-mode”)

*Different shipping methods per company

*Granular ACL for all B2B modules

Last but not least – new Venia PWA B2B theme! Can’t wait to see these features in action!

3. PWA Venia

Progressive Web Apps are the next best thing. We should know – we already implemented 4 projects with this approach. Now Magento is doing “half” of the work for us with a new PWA theme called Venia. It was presented at this year’s Magento Imagine, but they keep adding new features.

In 2019 they added Google Analytics tracking, authoring of catalog and increased the API coverage. For 2020 they plan:

*Content optimization for mobile devices with a preview

*B2B Venia theme

*Full AEM integration (more details below)

*B2B Data Import/Export (including shared catalog)

*100% GraphQL API coverage

4. Manage content better

You all heard of Page Builder. Adobe is now pushing a page-builder on steroids – Adobe Experience Manager (AEM). This tool not only allows you to manage content in the omnichannel environment, but also, it provides you with an AI capable of adapting the experience of every person visiting your store. You can now get so much more from your marketing team. The system was also production-tested with the Venia theme. Get ready for personalization!

5. Better reporting

In 2019 Adobe has already done a lot with Business Intelligence. In 2020, you can expect further development in this field with Role-Specific Dashboards, Adobe Analytics integration & New B2B Reports.

6. Security updates

Starting with the next M2.3.X quarterly release, Magento will offer streamlined “security-only” updates based on the prior quarter’s comprehensive update. This means that instead of having to update the entire system you will be able to only update the Security update, which will keep your store safe. The process gives you more time to install the Quality update.

7. Updated Support Policy

Starting with Magento 2.3, end of support date for a minor release will be based on the General Availability date of the next minor release. The Quality updates will be rolled out via incremental quarterly releases for +12 months. The Security ones, will follow a +18 months roll-out process. This gives you more time to adapt your store. This also means that you can accommodate the releases in the yearly budget better. Please refer to the Magento Imagine summary for more information on this.

8. Azure Early Adopter

As you are aware, Magento Cloud will now allow you to choose between AWS and Microsoft Azure for your servers. What you might not know, is that in Q1 2020 you can expect additional support from Magento for the first 6 Early Adapters. This support is only for the first stores that choose Azure. What they offer is a highly collaborative engagement of their team. If you are interested in working with Azure – contact us at [email protected] (we are official Microsoft Azure partners with years of e-Commerce experience).

9. What’s next?

We can’t wait to hear what Magento has planned for 2021 at Magento Imagine / Adobe Summit. If you want to talk to them sooner, they are planning to attend quite a bit of conferences in 2020:

*EComm Expo UK – Sept 2020

*DMEXCO – Sept 2020

*WWVD – Jan 2020

*Netcom Italy – May 2020

And of course – see you there at Adobe Summit 2020 in May!

Magento Imagine 2019

What did we learn?

1.Progressive Web Apps

It is no surprise that Adobe would be pushing this hard, as we already heard about PWA Studio before the conference. This year they presented a road map of things to come. They include things like Docker support, UI components, and GraphQL development. What was most interesting was the presentation of the apps built with PWA Studio and the promise to connect the PWA front to Page Builder in the future.

2.Longer support policy

One thing which will make everyone happy is the elongated support policy for the new releases of Magento. Adobe has received a lot of feedback from the users, and they are listening. Starting from Magento 2.3, each new minor release will be supported until the date of the next major release. Furthermore, the large quality updates will be supported for even 12 months after the next release. For security updates, it will be 18 months. This means that if you have M2.3 and M2.4 shows up, you can still get updates for M2.3 for 12 to 18 months.

3.Magento Cloud with Microsoft Azure

Currently, Magento Cloud is hosted on Amazon Web Services. Soon you will also have an option to choose Microsoft Azure for your Magento Cloud implementation. Adobe is also working on horizontal auto-scaling features to bring added flexibility for cluster-based setups with improved performance guaranteed.

4. Magento connects to Amazon and Google Merchant Center

Amazon and Google Merchant Center. Inventory management, prices and working with multiple Amazon accounts will be available right from the admin panel.

As for Google – now you can manage the campaigns right from the admin panel as well. With almost everybody using at minimum one of the two platforms – this is a great step in the right direction for the convenience of Magento store managers. You can read more about this directly from the source here.

5. Magento is now part of Adobe Commerce Cloud

We are not surprised. With the acquisition of Magento by Adobe, we expected this to happen. Magento will become connected to all the tools available from Adobe, creating an all-you-need easy to use solution. This includes Business Intelligence tools, integration with Adobe Analytics and others. The end result is a Magento integrated solution with all the tools needed for data analysis, customer segmentation, personalization, easy to use content management and advertising in a single bundle known as the Adobee Experience Cloud. This will give you all you need to make it big. You can read more about all the details of the products and services that are part of the Experience Cloud here.

6. SMB and Mid-Market support

As Adobe embraces Magento as part of a larger scalable Experience Solution, one thing Magento and Adobe both cannot reiterate enough is the continued support and growth of the smaller guys. Magento and Adobe will continue to grow their engagement with the SMB and Mid-Market business owners. They know that without these sectors, the Magento community would not be where it is today. Thanks to the engagement received from the Magento community, in most part driven by innovations for the SMB and Mid-Market, we can expect a big push for continued engagement and bigger supporting forces behind community innovations. We cannot be happier about this.

7. Our takeaways

As Adobe embraces Magento as part of a larger scalable Experience Solution, one thing Magento and Adobe both cannot reiterate enough is the continued support and growth of the smaller guys. Magento and Adobe will continue to grow their engagement with the SMB and Mid-Market business owners. They know that without these sectors, the Magento community would not be where it is today. Thanks to the engagement received from the Magento community, in most part driven by innovations for the SMB and Mid-Market, we can expect a big push for continued engagement and bigger supporting forces behind community innovations. We cannot be happier about this.

All in all – the conference was amazing, a highly recommended experience to anyone who would like to understand the strengths of the community behind Magento and Adobe.

Nowhere else can you encounter fanboys all dressed in orange, with orange shoes and orange suits made to look like a Magento marketplace. Amazing opportunity to see more than a dozen celebrated Magento Masters awarded for their state-of-the-art innovations or numerous successful contributions to the Magento and Adobe ecosystems. Being part of a community of hundreds of Technology and Solution Partners who come together to share ideas and to try and figure out ways to work together (or outsmart each other with the next best thing!) keeps us coming back for more.

Here is to another successful year until we meet again at Adobe Summit 2020!

[x]

This page uses cookies

I consent to the processing of my personal data contained in cookies (both session and permanent) by ORBA co. based in Warsaw, in order to adapt the content of the website to my preferences, optimize the use of websites, create anonymous statistics that allow monitoring of how the website is being used.

Accept