in Magento instead of trusting the default
A new product attribute does not automatically end up where it is needed for search and filtering in the Elasticsearch index. Only a deliberate custom attribute mapping with dedicated FieldMapper classes ensures that searchable, filterable and the right field type fit together, instead of relying on Magento's default derivation.
Table of Contents
- 1. Why the default mapping does not fit every attribute
- 2. The FieldMapper mechanism in di.xml
- 3. searchable, filterable, used_for_sort_by in detail
- 4. Field type derivation: from EAV backend type to ES type
- 5. Implementing a custom FieldProvider for a custom attribute
- 6. Detecting and avoiding mapping conflicts
- 7. Reindex strategy after mapping changes
- 8. Testing: inspecting and validating the mapping
- 9. Practical example: a boolean attribute with a custom analyzer
- 10. Summary
- 11. FAQ
1. Why the default mapping does not fit every attribute
Magento's custom attribute mapping works automatically by default: a new EAV attribute is created, marked as "searchable" or "filterable," and shows up in the Elasticsearch mapping on the next reindex. For most simple attributes such as color, size or material, this automatic path is entirely sufficient. It becomes problematic as soon as an attribute has requirements the default derivation does not cover: a custom analyzer for better full-text search, a special numeric format, or a field structure assembled from several source attributes.
Custom attribute mapping in Magento is based on a chain of FieldMapper and FieldType resolver classes configurable via di.xml. This configurability is deliberately designed so developers do not have to touch Magento core to change behavior for individual attributes. Understanding at which point in this chain a custom attribute mapping hooks in lets you precisely control how an attribute ends up in the index, without affecting the default logic for every other attribute.
This article shows step by step how to build a custom attribute mapping: from the relevant interfaces, through control via attribute flags, to a complete example with a custom analyzer for a boolean attribute.
2. The FieldMapper mechanism in di.xml
The central building block for any custom attribute mapping is the interface Magento\Elasticsearch\Model\Adapter\FieldMapperInterface. Its default implementation is assembled in the di.xml of Magento_Elasticsearch as a virtual type that in turn combines a list of FieldProvider classes. Each field provider is responsible for a specific category of attributes: one for static attributes such as sku and price, one for dynamic EAV attributes, another for system fields such as visibility and status.
This structure allows two fundamental extension paths for a custom attribute mapping. The first is additive: an extra field provider is added to the existing list and supplies field names and types for attributes the default providers do not cover. The second is decorative: a plugin on an existing FieldMapper changes the behavior for individual, already covered attributes, for example to force a different field name or type.
<!-- app/code/Mironsoft/SearchExtension/etc/di.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<!-- Register a custom field provider for attributes with special mapping needs -->
<type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface">
<plugin name="mironsoft_custom_field_provider"
type="Mironsoft\SearchExtension\Plugin\CustomFieldProviderPlugin"
sortOrder="20"/>
</type>
<!-- Virtual type: extend the field type resolver array with a custom entry -->
<virtualType name="Magento\Elasticsearch\Model\Adapter\FieldType\Text" shared="false"/>
<type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\CustomAttribute">
<arguments>
<argument name="fieldTypeConverter" xsi:type="object">
Mironsoft\SearchExtension\Model\Adapter\FieldType\CustomTypeResolver
</argument>
</arguments>
</type>
</config>
3. searchable, filterable, used_for_sort_by in detail
Three attribute flags significantly control how an attribute is treated in the Elasticsearch index as part of custom attribute mapping. is_searchable includes the attribute in full-text search: its value flows into the combined multi_match query that searches across multiple fields at once when a search request is issued. is_filterable respectively is_filterable_in_search makes the attribute usable for layered navigation and influences whether a keyword sub-field is created for exact aggregations.
The third flag, used_for_sort_by, decides whether an attribute is available as a sort criterion in the product list. For text fields, sorting is only sensibly possible via the additional keyword sub-field, which is why Magento automatically creates this sub-field when the used_for_sort_by flag is active. Anyone planning a custom attribute mapping for an attribute with all three flags needs to deliberately reflect this combination in the field type, usually as a text field with an additional fields.keyword sub-field for filtering and sorting.
{
"properties": {
"material": {
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 }
}
}
}
}
4. Field type derivation: from EAV backend type to ES type
Without explicit control, Magento derives the Elasticsearch field type in custom attribute mapping from the attribute's EAV backend type. This derivation happens in a series of FieldType classes registered per backend type: varchar and text become text, int becomes integer, decimal becomes float, datetime becomes date. This default derivation fits most cases but fails for attributes with special requirements, for example a numeric attribute stored as text because leading zeros need to be preserved.
For such cases, custom attribute mapping allows registering a custom field type resolver that supplies a different type mapping for specific attribute codes. This class implements the same interface as the default resolvers and is placed via di.xml ahead of the default chain, so it takes effect for the affected attributes before the generic backend type derivation is even reached.
5. Implementing a custom FieldProvider for a custom attribute
A dedicated field provider is the cleanest way to implement a custom attribute mapping that goes beyond simple type overrides. The class must implement FieldProviderInterface and supplies a list of fields with name, type, and additional Elasticsearch mapping parameters such as analyzer or copy_to. These fields are subsequently merged with the fields from the default providers before the complete mapping is generated for the PUT request to Elasticsearch.
<?php
declare(strict_types=1);
namespace Mironsoft\SearchExtension\Model\Adapter\FieldMapper;
use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface;
use Magento\Eav\Model\Config as EavConfig;
/**
* Provides an explicit Elasticsearch field definition for the custom
* "warranty_months" attribute, mapped as an integer with a fixed name.
*/
class WarrantyFieldProvider implements FieldProviderInterface
{
private const ATTRIBUTE_CODE = 'warranty_months';
/**
* @param EavConfig $eavConfig
*/
public function __construct(private readonly EavConfig $eavConfig)
{
}
/**
* Returns the custom field definition for the warranty attribute.
*
* @param array $context
* @return array
*/
public function getFields(array $context = []): array
{
$attribute = $this->eavConfig->getAttribute('catalog_product', self::ATTRIBUTE_CODE);
if (!$attribute->getAttributeId()) {
return [];
}
return [
self::ATTRIBUTE_CODE => [
'type' => 'integer',
],
];
}
}
6. Detecting and avoiding mapping conflicts
A common mistake in a custom attribute mapping is a conflict between two field providers that supply different field types for the same attribute code. Elasticsearch accepts exactly one type definition per field name; if the same field name is merged with different types from two sources, the order in the provider list decides which type is actually used, without any visible error. This leads to hard-to-trace bugs where a filter suddenly stops working after a second module maps the same attribute code.
The reliable way to avoid such conflicts is a consistent naming scheme for custom attributes and an explicit check of the generated mapping after every change. A look at GET /magento2_product_1_v1/_mapping/field/warranty_months immediately shows which type is actually active, regardless of which provider order took effect in the background.
7. Reindex strategy after mapping changes
Every change to custom attribute mapping that affects an existing field type requires a full reindex, because Elasticsearch cannot change field types after the fact. New fields for new attributes, on the other hand, can be added additively without rebuilding the existing index, as long as the field name did not exist before. This distinction is crucial for planning the rollout of a custom attribute mapping: a pure addition is low risk, a type change to an existing field requires a controlled reindex with an alias swap.
In practice it is advisable to first test new field providers in a staging environment with a fresh index before rolling out the custom attribute mapping to production. This allows the generated mapping to be fully validated before triggering a production full reindex, which can take several hours for large catalogs.
| Extension Point | Purpose | Change Risk | Reindex Required |
|---|---|---|---|
| New FieldProvider | Add a new field for a custom attribute | Low | No, additive possible |
| FieldMapper plugin | Change field name of an existing attribute | Medium | Yes, effectively a new field |
| FieldType resolver | Override an attribute's field type | High | Yes, always |
| Attribute flag change | Toggle searchable or filterable | Medium | Recommended |
8. Testing: inspecting and validating the mapping
Before every production rollout of a custom attribute mapping, a systematic test in an isolated environment pays off. The first step is querying the generated mapping directly via the Elasticsearch API and comparing it against the expected structure. The second step is a test document with realistic values, loaded via the bulk API and then verified with a targeted query against the new field.
# Verify the generated mapping for a custom field after reindex
curl -s -X GET "https://localhost:9200/magento2_product_1_v1/_mapping/field/warranty_months?pretty"
# Test filtering on the new field with a real query
curl -s -X GET "https://localhost:9200/magento2_product_1_v1/_search?pretty" \
-H "Content-Type: application/json" \
-d '{"query": {"range": {"warranty_months": {"gte": 24}}}}'
9. Practical example: a boolean attribute with a custom analyzer
A concrete example of extended custom attribute mapping is a boolean attribute "sustainably_produced" that should be both filterable in layered navigation and searchable with a custom synonym analyzer for related search terms such as "eco" or "environmentally friendly." A plain boolean field covers the filter use case but not the synonym search, which is why two fields are combined here: a boolean field for the exact filter and an additional text field with a custom analyzer, populated only when "true," containing the synonym search terms.
This combination shows that custom attribute mapping does not always mean a one-to-one relationship between EAV attribute and Elasticsearch field. A single attribute can well produce multiple fields in the index, when different use cases such as exact filtering and extended full-text search need to be served at the same time. The field provider for this attribute accordingly supplies two entries instead of a single one.
Mironsoft
Custom attribute mapping and Elasticsearch field strategy
Want attributes that do exactly what they should in the search index?
We design field providers and FieldMapper extensions for your custom attributes, check for mapping conflicts, and plan the reindex so search and filters stay stable.
FieldProvider design
Clean field strategy for custom attributes with multiple use cases
Mapping audit
Uncovering conflicts between modules and provider orderings
Reindex planning
Low-risk rollouts for mapping changes with alias swap
10. Summary
A deliberate custom attribute mapping in Magento goes beyond simply setting is_searchable and is_filterable. Only custom FieldProvider and FieldMapper classes, registered via di.xml, give full control over the name, field type, and analyzer an attribute ends up with in the Elasticsearch index. This control becomes especially important for attributes with special requirements such as synonym search, deviating numeric formats, or multiple simultaneous use cases.
Anyone planning a custom attribute mapping should actively check for mapping conflicts between modules, distinguish type changes to existing fields from additive extensions, and validate every change in an isolated environment before the production rollout. This discipline prevents the typical, hard-to-trace bugs where a filter or search suddenly behaves differently after a seemingly unrelated module update.
Custom attribute mapping in Magento: the essentials at a glance
FieldProvider
Cleanest path for new fields, additive without affecting existing attributes.
Attribute flags
searchable, filterable and used_for_sort_by jointly control the field structure including the keyword sub-field.
Conflict avoidance
Consistent naming scheme and regular mapping checks prevent silent type conflicts.
Reindex discipline
Type changes always require a full reindex with alias swap, additive fields are low risk.