Index a custom product attribute with Solr in Hybris
1. Overview
In Hybris, product list page, search page, product sorting and faceting are powered by Apache Solr.
In this article, I will show you how to index a custom product attribute with Solr and display it on the product list page.
We will index the unit attribute (no need to create a custom attribute) and display it on the PLP.
2.Implementation
1. First of all, create a custom FieldValueProvider
, to convert Unit
to a sample/indexable value.
package com.stackextend.training.core.search.solrfacetsearch.provider.impl;
// imports...
public class ProductUnitValueProvider implements FieldValueProvider, Serializable {
private FieldNameProvider fieldNameProvider;
private CommonI18NService commonI18NService;
@Override
public Collection<FieldValue> getFieldValues(final IndexConfig indexConfig, final IndexedProperty indexedProperty, final Object model) throws FieldValueProviderException
{
if (model instanceof ProductModel)
{
final ProductModel product = (ProductModel) model;
final Collection<FieldValue> fieldValues = new ArrayList<FieldValue>();
// case of the indexed property is localized
if (indexedProperty.isLocalized())
{
// retrieve and iterate over all the configured languages
final Collection<LanguageModel> languages = indexConfig.getLanguages();
for (final LanguageModel language : languages)
{
fieldValues.addAll(createFieldValue(product, language, indexedProperty));
}
}
// case of the indexed property is not localized
else
{
fieldValues.addAll(createFieldValue(product, null, indexedProperty));
}
return fieldValues;
}
throw new FieldValueProviderException("Error: item is not a Product type !");
}
protected List<FieldValue> createFieldValue(final ProductModel product, final LanguageModel language, final IndexedProperty indexedProperty)
{
final List<FieldValue> fieldValues = new ArrayList<FieldValue>();
// get Unit name by language
final String unitName = getUnitName(product, language);
if (unitName != null)
{
// add Unit name value to the fieldValues list
addFieldValues(fieldValues, indexedProperty, language, unitName);
}
return fieldValues;
}
protected void addFieldValues(final List<FieldValue> fieldValues, final IndexedProperty indexedProperty, final LanguageModel language, final Object value)
{
// generate all Solr fields based on different qualifiers
final Collection<String> fieldNames = fieldNameProvider.getFieldNames(indexedProperty, language == null ? null : language.getIsocode());
for (final String fieldName : fieldNames)
{
fieldValues.add(new FieldValue(fieldName, value));
}
}
private String getUnitName(ProductModel product, LanguageModel language) {
return product.getUnit().getName(commonI18NService.getLocaleForLanguage(language));
}
@Required
public void setFieldNameProvider(final FieldNameProvider fieldNameProvider)
{
this.fieldNameProvider = fieldNameProvider;
}
@Required
public void setCommonI18NService(final CommonI18NService commonI18NService)
{
this.commonI18NService = commonI18NService;
}
}
Note that no special value provider is needed if you want just to index a basic attribute (boolean, int, String…), check this article.
2. Register the ProductUnitValueProvider
as a Spring bean.
<!-- ...\trainingecore\resources\trainingecore-spring.xml -->
<bean id="productUnitValueProvider"
class="com.stackextend.training.core.search.solrfacetsearch.provider.impl.ProductUnitValueProvider" >
<property name="fieldNameProvider" ref="solrFieldNameProvider"/>
<property name="commonI18NService" ref="commonI18NService"/>
</bean>
3. Create a SolrIndexedProperty
instance called unit and attach productUnitValueProvider
bean to it, using an impex.
# .../traininginitialdata/import/coredata/stores/hybris/solr.impex
$solrIndexedType=...
INSERT_UPDATE SolrIndexedProperty ;solrIndexedType(identifier)[unique=true] ;name[unique=true] ;type(code) ;localized[default=false] ;fieldValueProvider
;$solrIndexedType ;unit ;string ;true ;productUnitValueProvider
4. Add unit attribute to ProductData
and run ant all to generate it.
<!-- ...\trainingfacades\resources\trainingfacades-beans.xml -->
<bean class="de.hybris.platform.commercefacades.product.data.ProductData">
<property name="unit" type="String"/>
</bean>
5. Override SearchResultVariantProductPopulator
, to populate unit values retrieved by Solr search.
package com.stackextend.training.facades.populators.CustomSearchResultVariantProductPopulator;
// imports...
public class CustomSearchResultVariantProductPopulator extends SearchResultVariantProductPopulator {
@Override
public void populate(SearchResultValueData source, ProductData target) {
super.populate(source, target);
// populate unit property values
target.setUnit(this.<String> getValue(source, "unit"));
}
}
6. Register the CustomSearchResultVariantProductPopulator
as a Spring bean, with commerceSearchResultProductPopulator
as an alias.
<!-- ...\trainingfacades\resources\trainingfacades-spring.xml -->
<alias name="customSearchResultProductPopulator" alias="commerceSearchResultProductPopulator"/>
<bean id="customSearchResultProductPopulator"
class="com.stackextend.training.facades.populators.CustomSearchResultVariantProductPopulator"
parent="variantCommerceSearchResultProductPopulator">
</bean>
7. Display the unit value where ever you want inside productListerItem.tag
.
<!-- ...\trainingstorefront\web\webroot\WEB-INF\tags\desktop\product\productListerItem.tag -->
...
${product.unit}
...
8. Finally, run a full indexation to force Solr to index the unit attribute of all existing products.
Navigate to HMC -> System -> Facet Search -> Indexer operation wizard.
9. Navigate to the PLP, if everything goes well, you will be able to see units.
Software Craftsmanship, Stackextend author and Full Stack developer with 6+ years of experience in Java/Kotlin, Java EE, Angular and Hybris…
I’m Passionate about Microservice architectures, Hexagonal architecture, Event Driven architecture, Event Sourcing and Domain Driven design (DDD)…
Huge fan of Clean Code school, SOLID, GRASP principles, Design Patterns, TDD and BDD.
Hey Mouad,
Great tutorials! Your website is extremely useful.
Looking forward to your further Hybris tutorials.
Personally, I would be interested in the next topics: adding facets to the plp, backoffice customization, promotions, pricing, product boosts in the search.
Just a couple of ideas if you want 🙂
Thank you a lot!
Hi Nick,
Thank you for your kinds words, I really appreciate it, I’ll try to cover these topics in my next publications.
I’ll keep you posted.
KR!
Hi
How does the solr indexes the different language for the attribute as you have provided “localized=true”. Where does it taking the language isocode from – Hybris or the solr.
Regards,
Yashwanth
Hi,
The languages used within the indexation are configured on the:
SolrFacetSearchConfigModel.languages
How Solr index them ? for example for an attribute of type
text
calledname
, Solr is going to indexed it for all the available languages like this :{
name_text_fr : "value fr",
name_text_en : "value en",
name_text_es : "value es"
}
Hi
Thank you for the quick reply. If I need to add new language, how can I configure this from the solr perspective?
Actually, the majority of the languages comes pre-configured in Solr, however if you want to add a new language to Solr (IT for example), go to
hybris\bin\ext-commerce\solrserver\resources\solr\server\solr\configsets\default\conf\solrconfig.xml
And add the snippets bellow :
<lst name="spellchecker">
<str name="name">it</str>
<str name="classname">solr.DirectSolrSpellChecker</str>
<str name="field">spellcheck_it</str>
</lst>
<lst name="spellchecker">
<str name="name">it</str>
<str name="classname">org.apache.solr.spelling.suggest.Suggester</str>
<str name="lookupImpl">org.apache.solr.spelling.suggest.tst.TSTLookup</str>
<str name="field">autosuggest_it</str>
<str name="buildOnCommit">true</str>
<str name="buildOnOptimize">true</str>
<str name="accuracy">0.35</str>
</lst>
Hi Mouad
I am trying to add new Facets. Ranges are coming in solr but not able to see facets on storefront. Could you please let me know on this
im facing the same please help me to do
piece
Hi Mouad EL Fakir;
This tutorial is excellent and help me lot .thanks for providing this.but i have small issue. I am getting solr value in valueprovider class and in populator class but iam not getting this solr custom value in search result page.Can you help me on this issue.
Have you add the field to
productListerItem.tag
?Then also iam not geeting in productListerItem.tag page
same
yes i added in productListerItem.tag
While indexing time I’m getting ERROR could you please help me on here.
“Failed to resolve values for item with PK: 8796096954369, by resolver: wisProductVarientValueProvider, for property: unit, reason: Index: 0, Size: 0
Have you found solution for this?
Do we need the Run Full Index whenever the custom attribute value changes
In this you need juste an Update Indexation
which controller used for productLIterItem.tag
hey, i am indexing a custom table with a collection attribute in it, can u please tell me how to index it in solr indexed property and how to get those custom item type data from solr using controller. i don’t know how to get data from solr for a custom items.
How to implement a resolver for the same use case? How a resolver uses context to resolve the localized attributes? What will be the difference in solr document?
Hi, what if I want to index an attribute other than primitive type like Enum etc.
Could you please tell me how to do it.
If already explained could you send me that link.
Thanks in advance.
https://www.stackextend.com/hybris/index-a-custom-product-property-with-solr-in-hybris/