3

I'm trying to figure out why certain code is needed in order for a content search query to work with Azure search. I have a multilist field which is indexed by the default indexer in Sitecore, meaning the index field contains data such as

"search_tags": "{6537B24A-1B80-4EA0-BBA2-3D3923E0ED4F}|{FA5E4033-F48E-4749-A76A-B62C422D4ABA}|{9B61C09C-2029-4AD6-859E-477F2C1AB95B}"

I have my own class called IndexedItem inheriting Sitecore.ContentSearch.SearchTypes.SearchResultItem. It has a field property as follows:

[IndexField("tags")]
public List<string> Tags { get; set; }

Using a query against the Tags property does not work (the clause is not included in the request to azure search).

    private Expression<Func<IndexedItem, bool>> GetPredicatesForTags(
        IEnumerable<string> tagIds)
    {
        var tagsPredicate = PredicateBuilder.False<IndexedItem>();
        foreach (var tagId in tagIds)
        {
            tagsPredicate = tagsPredicate.And(item => item.Tags.Contains(tagId));
        }

        return tagsPredicate;
    }

Using string.Contains against the field shown below also does not work:

    private Expression<Func<IndexedItem, bool>> GetPredicatesForTags(
        IEnumerable<string> tagIds)
    {
        var tagsPredicate = PredicateBuilder.False<IndexedItem>();
        foreach (var tagId in tagIds)
        {                
            tagsPredicate = tagsPredicate.And(item => item["tags"].Contains(tagId));
        }

        return tagsPredicate;
    }

If I add an extra Or predicate with an equals clause, it works:

    private Expression<Func<IndexedItem, bool>> GetPredicatesForTags(
        IEnumerable<string> tagIds)
    {
        var tagsPredicate = PredicateBuilder.False<IndexedItem>();
        foreach (var tagId in tagIds)
        {                
            tagsPredicate = tagsPredicate.And(item => item["tags"].Contains(tagId));
            tagsPredicate = tagsPredicate.Or(item => item["tags"] == tagId);
        }

        return tagsPredicate;
    }

With this code, the query sent to azure search includes:

&search=search_tags:(2a4b1304\-834c\-430a\-9cc6\-d6286abfc4c7) AND search_tags:(25a06d19\-db86\-4759\-a243\-d580354296b6)

Strangely there only seems to be one query clause per tag ID, not two as the code would suggest.

Is this a quirk of Azure Search, or is it common to ContentSearch in general?

This question: Multilist fields and Azure Search describes a similar problem, but the OP is using a simple string type property on their SearchResultItem class, so the scenario is slightly different.

1 Answer 1

3

There were two things wrong here:

  1. Incorrect indexer for the type of field
  2. Bad (default) format for storing IDs.

The default indexing process meant that we were indexing the raw field value of the multilist as a string. The fact that there was a custom IndexedItem class with an array property did not change this and in fact, the raw field text was appearing in the first element of this array. The solution to this was to index the field as a computed index field meaning it would be indexed as an array. I created a custom ComputedIndexField class (implementing Sitecore.ContentSearch.ComputedFields.IComputedIndexField). This ComputedIndexField was patched under sitecore\contentSearch\configuration\indexes\index@id=myindex\configuration\documentOptions\fields@hint=raw:AddComputedIndexField with the element:

<field fieldName="search_tags">MyProject.Foundation.Indexing.Infrastructure.Fields.SearchTagsComputedField, MyProject.Foundation.Indexing</field>

The class is below:

public class SearchTagsComputedField : IComputedIndexField
{
    public string FieldName { get; set; }
    public string ReturnType { get; set; }

    public object ComputeFieldValue(IIndexable indexable)
    {
        var indexItem = indexable as SitecoreIndexableItem;
        if (indexItem == null)
        {
            return null;
        }

        var item = indexItem.Item;
        var tagsFieldValue = item[Templates.ISearchDetails.Fields.Tags];
        var ids = ID.ParseArray(tagsFieldValue);
        var idList = ids.Select(IdHelper.NormalizeGuid).ToList();
        return idList;
    }
}

We read the raw value of the field, then use ID.ParseArray to get a collection of IDs. Sitecore.ContentSearch.Utilities.IdHelper.NormalizeGuid(ID) converts the IDs to strings in the numeric format (≡ guid.ToString("N")).

Note that IdHelper.NormalizeGuid(string) formats IDs with dashes (≡ guid.ToString("D")) which is pretty crazy IMHO!

Indexing IDs in the numeric format is a better idea, especially with Azure search as it does not seem to treat dashes in the same way as Lucene.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.