from the default field to a tailored search index
Magento's default OpenSearch mapping treats almost every attribute the same, whether it is a paragraph of body copy, a SKU suffix or a technical internal code. Anyone who wants real control over search quality, facets and relevance needs to understand how attributes map to field types and build a custom OpenSearch attribute mapping with the right analyzers.
Table of Contents
- 1. Why the default mapping falls short
- 2. How Magento maps attributes to OpenSearch field types
- 3. Setting search_weight and searchable correctly
- 4. Customizing the mapping via FieldMapperInterface
- 5. Defining analyzers and normalizers for custom attributes
- 6. Making an attribute usable as a layered navigation facet
- 7. Reindex strategy for mapping changes
- 8. Inspecting the mapping and testing queries directly
- 9. Default mapping vs. custom mapping compared
- 10. Summary
- 11. FAQ
1. Why the default mapping falls short
An OpenSearch attribute mapping describes how a Magento product attribute is stored in the search index: as a full text field with an analyzer, as an exact keyword value, as a number, or as a date. Magento generates this mapping automatically from the EAV attribute type, but that automatic derivation is deliberately conservative. A varchar attribute is almost always mapped as a text field with the default analyzer, regardless of whether it holds a product description, a SKU suffix, or a technical code.
In practice this causes problems that only surface once the catalog grows. A product code like ABC-1234-XL gets split by the default analyzer into tokens such as abc, 1234 and xl, which is useful for full text search inside descriptions but dilutes exact code lookups. A custom OpenSearch attribute mapping solves exactly this problem by defining the appropriate field type and analyzer per attribute, instead of relying on Magento's blanket derivation.
The second reason to build a custom OpenSearch attribute mapping is performance. Every additional analyzed field grows the index and slows down both indexing and facet aggregations. Deciding deliberately which attributes become text, which become keyword, and which are excluded from the index altogether noticeably reduces index size and speeds up full text search as well as layered navigation.
2. How Magento maps attributes to OpenSearch field types
Magento derives its OpenSearch attribute mapping using the class Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper, which decides the resulting index field type based on an attribute's backend type (backend_type) and input type (frontend_input). A select attribute is typically mapped as keyword, a text or textarea field as an analyzed text type, numeric attributes as float or integer.
Magento additionally distinguishes between the actual search field (often suffixed, such as attribute_code_value) and the field used for facet aggregation (typically the plain attribute_code without further analysis). This duplication explains why an attribute frequently appears twice in the index: once analyzed for search, once as keyword for exact filtering and aggregation. Understanding an OpenSearch attribute mapping requires treating these two fields separately, otherwise changes in one place will not behave as expected.
{
"properties": {
"sku": {
"type": "keyword",
"ignore_above": 256
},
"name": {
"type": "text",
"analyzer": "default"
},
"description": {
"type": "text",
"analyzer": "default"
},
"color": {
"type": "keyword"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
}
}
}
This excerpt shows a typical, unmodified OpenSearch attribute mapping: sku as an exact keyword value, name and description as analyzed text, color as a keyword for facets, price as a scaled float. This structure is the starting point for any custom adjustments.
3. Setting search_weight and searchable correctly
Before writing any code, it is worth reviewing the attribute configuration itself. In the Magento backend, the flag Used in Search Results (database column searchable) controls whether an attribute participates in full text search at all. The Search Weight field (1 to 10) influences that attribute's relevance weighting inside the search query and directly affects the generated OpenSearch attribute mapping, because the weight is carried into the multi_match query as a ^weight boost.
A common mistake: fields like internal supplier codes or technical note fields get accidentally marked as searchable, diluting relevance and producing irrelevant hits. Conversely, attributes that should carry strong weight, such as the product name or a short description containing a model number, are often left at the default weight of 1. A clean OpenSearch attribute mapping always starts with a deliberate review of every searchable attribute and its weighting, before any code is touched.
For facets, the Use in Layered Navigation flag matters just as much. An attribute can well be searchable and filterable at the same time, but that requires an additional, non analyzed field in the index for aggregation. Planning both flags independently avoids surprises in the generated mapping later on.
4. Customizing the mapping via FieldMapperInterface
To truly enforce a custom OpenSearch attribute mapping, backend configuration alone is not enough. Magento provides the interface Magento\AdvancedSearch\Model\Adapter\Mapper\FieldMapperInterface, whose implementations are wired together through di.xml via virtualType. The cleanest approach is a plugin on ProductFieldMapper::getAllAttributesTypes() or getFieldName() that forces a different field type for specific attribute codes.
<?php
declare(strict_types=1);
namespace Mironsoft\SearchMapping\Plugin;
use Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper;
/**
* Overrides the field type for specific attributes in the OpenSearch mapping.
*/
final class ForceKeywordMappingPlugin
{
/** @var array<string,string> Attribute code to forced OpenSearch field type */
private const FORCED_TYPES = [
'manufacturer_sku' => 'keyword',
'ean' => 'keyword',
'internal_note' => 'none',
];
/**
* Forces selected attributes to bypass the default type resolution.
*
* @param ProductFieldMapper $subject Original field mapper
* @param array<string,string> $result Resolved attribute types
* @return array<string,string>
*/
public function afterGetAllAttributesTypes(
ProductFieldMapper $subject,
array $result
): array {
foreach (self::FORCED_TYPES as $attributeCode => $forcedType) {
if (isset($result[$attributeCode])) {
$result[$attributeCode] = $forcedType;
}
}
return $result;
}
}
Registration happens the usual way, through di.xml. Important: attribute visibility should still be controlled through searchable in the backend, the plugin only changes the field type, not whether the attribute is present in the index at all. For more complex cases, for example when an attribute needs different analysis per store language, a dedicated virtualType for the FieldMapper is worth it instead of a plain plugin, since it also encapsulates new analyzer assignments cleanly.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\ProductFieldMapper">
<plugin name="mironsoft_force_keyword_mapping"
type="Mironsoft\SearchMapping\Plugin\ForceKeywordMappingPlugin"
sortOrder="10"/>
</type>
</config>
5. Defining analyzers and normalizers for custom attributes
A field type alone is rarely enough for a truly fitting OpenSearch attribute mapping. The analyzer decides how a text value is split into searchable tokens, and this is where most of the leverage over search quality lives. For product codes with fixed separators, a custom keyword normalizer is a good fit, unifying case without splitting the value into word parts. For multilingual description text, a language specific analyzer with stemming is worth the effort instead of the generic default analyzer.
A common use case is an edge_ngram analyzer for autocomplete fields, indexing partial strings starting at three characters. This pattern also belongs to OpenSearch attribute mapping work, since it creates an additional field with its own analyzer alongside the regular search field. Analyzers and normalizers need to be registered in the index settings before the index is created, since analyzer configuration cannot be changed on an existing index without recreating it.
{
"settings": {
"analysis": {
"normalizer": {
"sku_normalizer": {
"type": "custom",
"filter": ["lowercase", "asciifolding"]
}
},
"analyzer": {
"autocomplete_analyzer": {
"type": "custom",
"tokenizer": "autocomplete_tokenizer",
"filter": ["lowercase"]
}
},
"tokenizer": {
"autocomplete_tokenizer": {
"type": "edge_ngram",
"min_gram": 3,
"max_gram": 12,
"token_chars": ["letter", "digit"]
}
}
}
}
}
One detail that is easy to miss: the normalizer only applies to keyword fields, the analyzer only to text fields. Mixing the two up either has no effect or triggers an OpenSearch error while applying the index settings. For every custom OpenSearch attribute mapping, a quick test against the _analyze API before going live is time well spent.
6. Making an attribute usable as a layered navigation facet
For an attribute to appear as a facet in layered navigation, the OpenSearch attribute mapping needs an aggregation capable field, usually a keyword type without an analyzer. Magento handles this by default for select and multiselect attributes, but for free text attributes that should become facetable retroactively, an additional keyword subfield needs to be added, for example via fields inside the mapping definition.
A common merchandising request is faceting by fixed, custom price ranges instead of Magento's automatic price tiers. Technically this is solved with a range aggregation on the price field, without touching the OpenSearch attribute mapping itself as long as the field type stays numeric. For category specific facets that should only appear in certain categories, the best approach combines the mapping with attribute set assignment, so irrelevant filters never even reach the aggregation.
{
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50 },
{ "from": 50, "to": 150 },
{ "from": 150, "to": 500 },
{ "from": 500 }
]
}
},
"color_facet": {
"terms": { "field": "color", "size": 20 }
}
}
}
7. Reindex strategy for mapping changes
Any change to the OpenSearch attribute mapping that affects a field type requires a full reindex, since OpenSearch does not allow changing an existing field type on a running index. Magento's indexer already creates a new index with an alias suffix on every full reindex run and only switches the alias once the run completes successfully, which enables zero downtime reindexing out of the box.
For custom mapping changes, this flow should be tested before it runs in production: the new mapping is first applied in staging, a full reindex is triggered, and sample queries are compared to confirm that search results and facets still behave correctly. Only then does the change move to production. An easily underestimated risk: if the OpenSearch attribute mapping changes a field to a different type than an older cached frontend version expects, search requests can fail with a mapping conflict until cache and all frontends consistently point to the new version.
# Full reindex after mapping changes, non-blocking for the storefront
bin/magento indexer:reindex catalogsearch_fulltext
# Verify the new index exists and alias points to it
curl -s "http://opensearch:9200/_cat/indices?v" | grep magento2
# Compare mapping between the live and staging index
curl -s "http://opensearch:9200/magento2_default_product/_mapping" | jq . > /tmp/mapping.json
8. Inspecting the mapping and testing queries directly
When search results misbehave, it always pays to look directly at the actual OpenSearch attribute mapping instead of guessing. The _mapping API returns the full field structure of an index, and the _analyze API shows exactly how a given text is tokenized by a particular analyzer. These two endpoints resolve ninety percent of "why isn't the search finding this" questions faster than any code review.
A reliable approach: first use _analyze to check which tokens are produced for the search term and for the stored attribute value. If the tokens do not match, the problem lives in the analyzer or the OpenSearch attribute mapping, not in Magento's query logic. Only once the tokens match but no hit still appears is it worth digging into the generated Magento query, for example via the built in profiler or by capturing the actual request sent to OpenSearch.
# Show how the analyzer tokenizes a given text
curl -s -X POST "http://opensearch:9200/magento2_default_product/_analyze" \
-H "Content-Type: application/json" \
-d '{"field": "name", "text": "ABC-1234-XL jacket"}' | jq .
# Show the full mapping for a single field
curl -s "http://opensearch:9200/magento2_default_product/_mapping/field/sku" | jq .
9. Default mapping vs. custom mapping compared
The following overview summarizes when the automatically generated OpenSearch attribute mapping is sufficient and when a custom mapping becomes necessary.
| Scenario | Default mapping | Custom mapping | Benefit |
|---|---|---|---|
| Exact product codes | Text analyzer splits into tokens | Keyword with normalizer | Precise code search without noise |
| Autocomplete | No partial string match | edge_ngram analyzer | Matches after three characters |
| Internal note fields | Get indexed along with everything | type: none | Smaller index, less noise |
| Price facets | Fixed Magento price tiers | Custom range aggregation | Merchandising friendly buckets |
| Multilingual text | Generic default analyzer | Language specific analyzer | Better stemming per language |
The trend in the table is clear: whenever an attribute plays a special role in merchandising or search, investing in a custom OpenSearch attribute mapping pays off. For generic text fields without special requirements, the default mapping remains the pragmatic choice.
Mironsoft
Magento search, OpenSearch tuning and merchandising consulting
A search index that actually fits your catalog?
We audit your existing OpenSearch mapping, identify attributes with optimization potential, and build a custom mapping with the right analyzers, facets and a tested reindex strategy.
Mapping audit
Analysis of every attribute and its current field type in the index
Custom mapper
FieldMapperInterface implementation for your special attributes
Reindex without downtime
A tested migration strategy for production catalogs
10. Summary
A custom OpenSearch attribute mapping does not start in code but in the attribute configuration: searchable, search_weight, and Use in Layered Navigation set the frame before a single plugin is written. For attributes with special requirements, exact codes, autocomplete fields or multilingual text, implementing FieldMapperInterface with custom analyzers and normalizers pays off.
Any mapping change that affects field type requires a full reindex, which Magento's alias mechanism handles without downtime in the common case. OpenSearch's _analyze and _mapping APIs are the fastest tools to understand why a search behaves unexpectedly, before hunting for causes in the Magento codebase. Following this order produces an OpenSearch attribute mapping that grows with the catalog instead of holding it back.
OpenSearch Attribute Mapping in Magento 2 — Key Takeaways
Attribute configuration first
Set searchable, search_weight and the layered navigation flag correctly before touching any code.
FieldMapperInterface
Use a plugin or virtualType for attributes that need a different field type than the default derivation.
Analyzers before index creation
Register normalizers and analyzers in the index settings before the index is created.
Debugging with _analyze
Verify tokens directly against OpenSearch instead of guessing about the generated Magento query.