Jul 4, 2011
visibility 6986
star star star star star
(1 votes)

EPiCommerce and Multiline Dictionary Meta Field Bug

I am in the early stages of a large EPiCommerce build and myself and one of my colleagues have been experiencing issues with Dictionary meta fields that allow multiple selections to be picked.

The Problem

Basically the issue is that the values chosen in these fields do not appear in the faceted navigation.  Some time last week we raised a support ticket with EPiServer to investigate the issue.

Today, on the way home from work I thought I would investigate this issue further and see if it is possible to find a work around to the problem.  It just so happens there is Smile

After using LUKE – Lucene Index Toolbox to analyse the indexes that were being created I could see that the values being stored for my multiline enabled dictionary were always System.String[].

When the CatalogEntry was being indexed the indexer was not sure what to do with the particular field type and just performed a ToString() on the string array of selected values and stored that in the index, that’s s**t Sad smile

The Fix

I last used Mediachase around 3-4 years ago and it seems the documentation and online forum support doesn’t seem much better than it was back then.

So I did some digging around through the various Mediachase configs and found this little beauty Mediachase.Search.config:

   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <Mediachase.Search>
   3:    <SearchProviders defaultProvider="LuceneSearchProvider">
   4:      <providers>
   5:        <!--<add name="SolrSearchProvider" type="Mediachase.Search.Providers.Solr.SolrSearchProvider, Mediachase.Search.SolrSearchProvider" queryBuilderType="Mediachase.Search.Providers.Solr.SolrSearchQueryBuilder, Mediachase.Search.SolrSearchProvider" url="http://localhost:8080/solr" shareCores="true" />-->
   6:        <add name="LuceneSearchProvider" type="Mediachase.Search.Providers.Lucene.LuceneSearchProvider, Mediachase.Search.LuceneSearchProvider" queryBuilderType="Mediachase.Search.Providers.Lucene.LuceneSearchQueryBuilder, Mediachase.Search.LuceneSearchProvider" storage="C:\EPiServer\Sites\EPiServerCommerceManager3\App_Data\SearchIndex" />
   7:      </providers>
   8:    </SearchProviders>
   9:    <Indexers basePath="C:\EPiServer\Sites\EPiServerCommerceManager3\App_Data\SearchIndex">
  10:      <add name="catalog" type="Mediachase.Search.Extensions.Indexers.CatalogIndexBuilder, Mediachase.Search.Extensions" />
  11:    </Indexers>
  12:  </Mediachase.Search>

 

This config allows you to configure a search provider, you will see mine is configured to use the LuceneSearchProvider.  You will also notice that you are able to configure an indexer Surprised smile.

I then decompiled the CatalogIndexBuilder and had a scan around the various methods and decided to implement my own Indexer which inherits the Medicachase.Search.Extensions.Indexers.CatalogIndexBuilder.

There is an AddMetaField method you can override and this contains all of the logic for populating the meta fields within the lucene document.

I then figured out where the problem code was.  Basically there was an if statement like the following:

   1:  if (metaField.DataType == MetaDataType.DictionaryMultiValue)

Which needed to change to:

   1:  if (metaField.DataType == MetaDataType.DictionaryMultiValue || metaField.DataType == MetaDataType.EnumMultiValue)

When one of your meta fields is a dictionary which allows multiple selections it’s MetaDataType is an EnumMutiValue.  This MetaDataType was not handled correctly within the out of the box Medichase CatalogIndexBuilder.

I then created a new class named LeesCatalogIndexBuilder within a new project named LeesSearchExtensions.  I got hold of the decompiled code for the AddMetaField method and fixed the issue above and placed it within my new Indexbuilder class.

The code for the class is below:

 

   1:  namespace LeesSearchExtensions
   2:  {
   3:      using System;
   4:      using System.Collections;
   5:      using System.Collections.Generic;
   6:      using Mediachase.Commerce.Storage;
   7:      using Mediachase.MetaDataPlus;
   8:      using Mediachase.MetaDataPlus.Configurator;
   9:      using Mediachase.Search.Extensions;
  10:      using Mediachase.Search.Extensions.Indexers;
  11:   
  12:      public class LeesCatalogIndexBuilder : CatalogIndexBuilder
  13:      {
  14:   
  15:          protected override void AddMetaField(Mediachase.Search.Extensions.SearchDocument doc, Mediachase.MetaDataPlus.Configurator.MetaField metaField, System.Collections.Hashtable metaObject)
  16:          {
  17:              //base.AddMetaField(doc, metaField, metaObject);
  18:   
  19:              // Code taken from the base class virtual method
  20:              if (metaField.AllowSearch)
  21:              {
  22:                  string name = metaField.Name;
  23:                  List<string> list = new List<string>();
  24:                  if (metaField.Attributes["IndexStored"] != null)
  25:                  {
  26:                      if (metaField.Attributes["IndexStored"].Equals("true", StringComparison.OrdinalIgnoreCase))
  27:                      {
  28:                          list.Add(SearchField.Store.YES);
  29:                      }
  30:                      else
  31:                      {
  32:                          list.Add(SearchField.Store.NO);
  33:                      }
  34:                  }
  35:                  if (metaField.Attributes["IndexField"] != null)
  36:                  {
  37:                      if (metaField.Attributes["IndexField"].Equals("tokenized", StringComparison.OrdinalIgnoreCase))
  38:                      {
  39:                          list.Add(SearchField.Index.TOKENIZED);
  40:                      }
  41:                      else
  42:                      {
  43:                          list.Add(SearchField.Index.UN_TOKENIZED);
  44:                      }
  45:                  }
  46:                  object metaFieldValue = MetaHelper.GetMetaFieldValue(metaField, metaObject[metaField.Name]);
  47:                  string value = string.Empty;
  48:                  if (metaField.DataType == MetaDataType.BigInt || metaField.DataType == MetaDataType.Decimal ||
  49:                      metaField.DataType == MetaDataType.Float || metaField.DataType == MetaDataType.Int ||
  50:                      metaField.DataType == MetaDataType.Money || metaField.DataType == MetaDataType.Numeric ||
  51:                      metaField.DataType == MetaDataType.SmallInt || metaField.DataType == MetaDataType.SmallMoney ||
  52:                      metaField.DataType == MetaDataType.TinyInt)
  53:                  {
  54:                      if (metaFieldValue != null)
  55:                      {
  56:                          value = metaFieldValue.ToString();
  57:   
  58:                          if (!string.IsNullOrEmpty(value))
  59:                          {
  60:                              doc.Add(new SearchField(name, metaFieldValue, list.ToArray()));
  61:                          }
  62:                      }
  63:                  }
  64:                  else
  65:                  {
  66:                      if (metaFieldValue != null)
  67:                      {
  68:                          if (metaField.DataType == MetaDataType.DictionaryMultiValue || metaField.DataType == MetaDataType.EnumMultiValue)
  69:                          {
  70:                              string[] array = (string[])metaFieldValue;
  71:   
  72:                              for (int i = 0; i < array.Length; i++)
  73:                              {
  74:                                  string text = array[i];
  75:                                  doc.Add(new SearchField(name, (text == null) ? string.Empty : text.ToLower(), list.ToArray()));
  76:                              }
  77:                          }
  78:                          else
  79:                          {
  80:                              if (metaField.DataType == MetaDataType.StringDictionary)
  81:                              {
  82:                                  MetaStringDictionary metaStringDictionary = metaFieldValue as MetaStringDictionary;
  83:                                  ArrayList arrayList = new ArrayList();
  84:                                  if (metaStringDictionary != null)
  85:                                  {
  86:                                      foreach (string key in metaStringDictionary.Keys)
  87:                                      {
  88:                                          string text = metaStringDictionary[key];
  89:                                          doc.Add(new SearchField(name, (text == null) ? string.Empty : text.ToLower(), list.ToArray()));
  90:                                      }
  91:                                  }
  92:                              }
  93:                              else
  94:                              {
  95:                                  doc.Add(new SearchField(name, (metaFieldValue == null) ? string.Empty : metaFieldValue.ToString().ToLower(), list.ToArray()));
  96:                              }
  97:                          }
  98:                      }
  99:                  }
 100:              }
 101:   
 102:          }
 103:      }
 104:  }

I then had to update the Mediachase.Search.config to use my new CatalogIndexBuilder:

   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <Mediachase.Search>
   3:    <SearchProviders defaultProvider="LuceneSearchProvider">
   4:      <providers>
   5:        <add name="LuceneSearchProvider" type="Mediachase.Search.Providers.Lucene.LuceneSearchProvider, Mediachase.Search.LuceneSearchProvider" queryBuilderType="Mediachase.Search.Providers.Lucene.LuceneSearchQueryBuilder, Mediachase.Search.LuceneSearchProvider" storage="C:\EPiServer\Sites\EPiServerCommerceManager3\App_Data\SearchIndex" />
   6:      </providers>
   7:    </SearchProviders>
   8:    <Indexers basePath="C:\EPiServer\Sites\EPiServerCommerceManager3\App_Data\SearchIndex">
   9:      <!--<add name="catalog" type="Mediachase.Search.Extensions.Indexers.CatalogIndexBuilder, Mediachase.Search.Extensions" />-->
  10:      <add name="catalog" type="LeesSearchExtensions.LeesCatalogIndexBuilder, LeesSearchExtensions" />
  11:    </Indexers>
  12:  </Mediachase.Search>

 

Conclusion

I am sure you would agree with me that this kind of bug should have been picked up by the Medichase testing process, but we all know bugs like this slip through.  I don’t know whether other people have experienced this but multiple searches on google didn’t return any hits for the issue.

In summary, luckily it turned out there was a simple solution to the problem so myself and my colleagues can carry on with the out of the box faceted search functionality without having to reinvent the wheel Smile.

Thanks for reading Winking smile

Jul 04, 2011

Comments

Jul 5, 2011 11:46 AM

Great post!
It must be a bug.
Beside your fix, I think I found another workaround for it is that when creating the Dictionary metafield, you can enable "Editable" checkbox, then the type of our metafield will be EnumMultiValue and CatalogIndexBuilder can index it correctly

sandeep@r1group.co.uk
sandeep@r1group.co.uk Sep 20, 2011 10:03 PM

HI
Useful post. Would like to confirm that if you mark the dictionary as editable then the values are stored correctly in the lucene index. It also works fine in the faceted search.
Kind Regards
Sandeep

error Please login to comment.
Latest blogs
Composition Over Inheritance: Why Your Content Contract Is Not a Base Class

Inside the structural conformance pattern in SaaS CMS — from server-side inheritance to pipeline governance. A contract in Optimizely SaaS CMS is...

Vipin Banka | Sep 19, 2026

Optimizely Opti ID Integration: Why the API Key Was Missing from the DXP Management Portal

While working on an Optimizely CMS 12 Opti ID integration recently, we ran into an issue that initially appeared to be related to SSO authenticatio...

Madhu | Sep 18, 2026 |

Extending a Contract: What You Can Override

Contracts share fields across content types — but not every field setting can be overridden. Push the same contract onto several components, and CM...

Chirag Khanna | Sep 18, 2026 |

Building a Blazor Server UI Inside the Optimizely CMS 13 Admin Shell

What I learned shipping a Blazor Server UI inside Optimizely CMS 13 — Razor components in a NuGet package, the Blazor hub next to MapContent(),...

Stanisław Szołkowski | Sep 17, 2026 |