Try our conversational search powered by Generative AI!

Lee Crowe
Jul 4, 2011
  5997
(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

Please login to comment.
Latest blogs
Solving the mystery of high memory usage

Sometimes, my work is easy, the problem could be resolved with one look (when I’m lucky enough to look at where it needs to be looked, just like th...

Quan Mai | Apr 22, 2024 | Syndicated blog

Search & Navigation reporting improvements

From version 16.1.0 there are some updates on the statistics pages: Add pagination to search phrase list Allows choosing a custom date range to get...

Phong | Apr 22, 2024

Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog