Value providers in SAP Commerce(Hybris) Solr

Nuray Fahri
3 min readDec 6, 2020

--

There are two ways for indexing Item Types in Solr. The first way is when data requires no change or transformation. The data can be taken as it is from the database. The second way is when data does require a change and for this SAP Hybris allows us to write some custom logic on that data and send it to Solr for indexing.

The steps for creating Value provider are:

(The example is for new attribute- customName in ProductModel which will index customName if in the database we have value for this attribute else it will index “TEST”):

  1. Create a class by extending the AbstractFieldValueProvider class or implementing FieldValueProvider interface. FieldValueProvider interface exposes a method called getFieldValues with appropriate parameters to facilitate this change. We must implement this interface and override the getFieldValues() method as per our needs.

Let’s first create the attribute in the Product model in items.xml

<itemtype code="Product" autocreate="false" generate="false">
<description>Extending Product type with additional attributes.</description>
<attribute qualifier="customName" type="localized:java.lang.String">
<description>Localized Custom product Name</description>
<modifiers read="true" write="true" search="true" optional="true"/>
<persistence type="property"/>
<custom-properties>
<property name="hmcIndexField"><value>"thefield"</value></property>
</custom-properties>
</attribute>
</attributes>
</itemtype>

Now we can create ValueProvider:

package com.custom.core.search.solrfacetsearch.provider.impl;import de.hybris.platform.core.model.c2l.LanguageModel;
import de.hybris.platform.core.model.product.ProductModel;
import de.hybris.platform.servicelayer.i18n.CommonI18NService;
import de.hybris.platform.servicelayer.i18n.I18NService;
import de.hybris.platform.solrfacetsearch.config.IndexConfig;
import de.hybris.platform.solrfacetsearch.config.IndexedProperty;
import de.hybris.platform.solrfacetsearch.config.exceptions.FieldValueProviderException;
import de.hybris.platform.solrfacetsearch.provider.FieldNameProvider;
import de.hybris.platform.solrfacetsearch.provider.FieldValue;
import de.hybris.platform.solrfacetsearch.provider.FieldValueProvider;
import org.springframework.beans.factory.annotation.Required;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Created by EXQ02122 on 3/10/2017.
*/
public class ProductNameValueProvider implements FieldValueProvider {
private I18NService i18nService;
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 names by language
i18nService.setLocalizationFallbackEnabled(false);
final String customName = getCustomName(product, language);
i18nService.setLocalizationFallbackEnabled(true);
addFieldValues(fieldValues, indexedProperty, language, customName != null && !customName.equals("") ? customName : "TEST");
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 getHybrisName(ProductModel product, LanguageModel language) {
return product.getHybrisName(commonI18NService.getLocaleForLanguage(language));
}
@Required
public void setFieldNameProvider(final FieldNameProvider fieldNameProvider)
{
this.fieldNameProvider = fieldNameProvider;
}
@Required
public void setCommonI18NService(final CommonI18NService commonI18NService)
{
this.commonI18NService = commonI18NService;
}
@Required
public void setI18nService(final I18NService i18nService)
{
this.i18nService = i18nService;
}
}

Note: i18nService.setLocalizationFallbackEnabled(true) is for enable/disable the localization fallback feature.

3. Define the spring bean of the ValueProvider in the spring.xml file:

<bean id="productNameValueProvider"
class="com.custom.core.search.solrfacetsearch.provider.impl.ProductNameValueProvider" >
<property name="fieldNameProvider" ref="solrFieldNameProvider"/>
<property name="commonI18NService" ref="commonI18NService"/>
<property name="i18nService" ref="i18nService" />
</bean>

4. Specify the indexing attribute in Solr.index:

INSERT_UPDATE SolrIndexedProperty;solrIndexedType(identifier)[unique = true];name[unique = true];type(code);sortableType(code);currency[default = false];localized[default = false];multiValue[default = false];useForSpellchecking[default = false];useForAutocomplete[default = false];fieldValueProvider;valueProviderParameter;
;$solrIndexedType;customName ;text ;sortabletext;;true;;true;true;productNameValueProvider;5. To use the attribute on the storefront we create the attribute in ProductData:
<bean class="de.hybris.platform.commercefacades.product.data.ProductData">
<property name="customName" type="String"/>
</bean>

6. Override CustomProductPopulator , to populate unit values retrieved by Solr search.

public class CustomProductPopulator extends ProductPopulator{private I18NService i18nService;@Override
public void populate(final ProductModel source, final ProductData target) {
super.populate(source, target);
i18nService.setLocalizationFallbackEnabled(false);
if(source.getCustomName() != null && !source.getCustomName().equals("")){
target.setCustomName(source.getCustomName());
}
i18nService.setLocalizationFallbackEnabled(true);
}
@Required
public void setI18nService(final I18NService i18nService)
{
this.i18nService = i18nService;
}
}

7. Register newly created populator as a spring bean and link it to the existing populator using alias.

<alias name="customProductPopulator " alias="productPopulator" />
<bean id="customProductPopulator " parent="defaultProducPopulator"
class="com.custom.facades.populators.CustomProductPopulator ">
<property name="i18nService" ref="i18nService" />
</bean>

When To Use a Hybris Value Provider?

Value Providers should only be leveraged when data from the database must be, without exception or work around, altered before entering them in Apache Solr.

Make sure you give this post a clap and follow my blog if you find it helpful.

--

--

Nuray Fahri
Nuray Fahri

Written by Nuray Fahri

Software Development Factory Manager @Coca-Cola HBC

No responses yet