Views: 5064
Number of votes: 1
Average rating:

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

hiep.to
(By hiep.to, 7/5/2011 1:46:59 PM)

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 Sahoo
(By Sandeep Sahoo, 9/21/2011 12:03:55 AM)

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.