6

Unfortunately a SharePoint 2013 search result returns something which is not compatible with a TaxonomyFieldValue constructor.

If you pass a search result to a TaxonomyFieldValue ctor you will get the following error message: The given value for a taxonomy field was not formatted in the required ;#| format.

What I do get from a keyword query looks like that:

GP0|#d86df779-4fcc-45b0-a96e-8caaa84b4b1c;L0|#0d86df779-4fcc-45b0-a96e-8caaa84b4b1c|MyTermLabel;GTSet|#4b6fe410-c992-43c9-8149-80851a5b93b0

The last guid is the termset ID
The middle guid is the term's ID (label is "MyTermLabel").
I did not yet figure out what the first ID is. It is neither the Group nor the TermStore nor a reference to the taxonomy hidden list. I'm still in the dark on this one.

Before I come up with something crazy to extract terms from this search result string - is there any built-in functionality?

Update: There is some information on the syntax here: http://technet.microsoft.com/en-us/library/jj613136(v=office.15).aspx But this describes search query options rather than "why is my result looking the way it is".

Update: Stumbled across this thread on SE How to parse the result type containing managed metadata. The suggestion is to use the search result value with the TaxonomyFieldValueCollection ctor(). This is simply wrong! As with the TaxonomyFieldValue ctor() this is NOT working! Even if you strip away all the GP0 and GTSet fragments.

Answered my own question - moved the sample code to a separate answer posting to be able to put a green checkmark on it - for others to see there's a possible solution inside.

2
  • Cool stuff, thanks for posting your helper class. I think that there is something built-in, maybe its internal and you have to use reflection for that. Did you look for something related? Jan 21, 2015 at 10:30
  • I did some digging with ILSpy but I didn't find anything useful. There should be some JavaScript logic for display template rendering somewhere...
    – lapsus
    Jan 21, 2015 at 12:36

3 Answers 3

2

I had same issue. When reviewing the crawled properties, I saw 2 crawled properties were created but only the managed property for the problem result was generated. I manually created the managed property mapped to the 2nd crawled property, added to my display template, and ran a full crawl so now only label displays in result.

The two crawled properties had the same suffix which came through via a search of the suffix in the crawled property administration.

Example:

  • ows_taxId_AlertVoice (this is the one with automatically created managed property)
  • ows_AlertVoice (this is the one which generated just the label when I manually created managed property).
1

Update: I couldn't find any built-in functionality. I came up with a helper class. It is working very well so far. As far as my tests go I couldn't find any issues regarding localization or anything else.

namespace MyCompany.Common.SharePoint.Ecm.Taxonomy
{
    using System;
    using System.Collections.Generic;

    // See http://sharepoint.stackexchange.com/questions/128965/sharepoint-2013-search-result-taxonomyfieldvalue
    public sealed class TaxonomySearchResultValue
    {
        private readonly string _searchResultValue;
        private readonly Dictionary<Guid, string> _terms;

        public TaxonomySearchResultValue(string searchResultString)
        {
            _searchResultValue = searchResultString;
            _terms = new Dictionary<Guid, string>();

            BuildTermsDictionary();
        }

        public Dictionary<Guid, string> Terms
        {
            get
            {
                return _terms;
            }
        }

        private void BuildTermsDictionary()
        {
            // EXAMPLE Input:
            // "GP0|#422f9d65-5b31-4334-a281-16e27b3d5dbd;L0|#0422f9d65-5b31-4334-a281-16e27b3d5dbd|Werkstudententätigkeit;GTSet|#2142cc53-6677-435f-a8ec-f923d6dfec9f;GP0|#a92202ba-92d4-4831-9003-0deaedf0feb7;L0|#0a92202ba-92d4-4831-9003-0deaedf0feb7|Praktikum;GP0|#76a22c22-a3fd-4efc-85d2-cc0dcdb12bf1;L0|#076a22c22-a3fd-4efc-85d2-cc0dcdb12bf1|Duales Studium";

            if (string.IsNullOrEmpty(_searchResultValue)) return;

            string[] split = _searchResultValue.Split(';');

            foreach (string tuple in split)
            {
                if (tuple.StartsWith("L0|")) // NOTE: No clue what L0| even means - appears to be a term.
                {
                    string[] frags = tuple.Split('|');
                    if (frags.Length == 3)
                    {
                        string id = frags[1];
                        Guid termId = new Guid(id.Substring(id.Length - 36, 36)); // Guid is 32bit + 4 dashes

                        _terms.Add(termId, frags[2]);
                    }
                }
            }
        }
    }
}

The resulting object instance contains a property "Terms" which is simply a Dictionary<Guid, string> where Guid is the term's id and string is the term's label.

1

Like you mentioned in the question, MSDN contains the following explanation about the format of search result value for managed metadata field:

Data format for Managed Metadata.
To query for items tagged with a Managed Metadata field, you have to use the >Unique Identifier for each label. You can find the Unique Identifier for each term in a term set in the Term Store Management Tool, on the GENERAL tab. In addition, the data format that is used in the query has to specify from which level in the term set the query should apply. This specification is set by adding one of the following prefixes to the Unique Identifier:

  • To query for all items that are tagged with a term: GP0|#
  • To query for all items that are tagged with a child of term: GPP|#
  • To query for all items that are tagged with a term from a term set: GTSet|#

Having said that, i've come up with the following solution to parse search result value for managed metadata field:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SharePoint.Client.Search.Results
{
    public class TaxonomySearchResultValue
    {
        public TaxonomySearchResultValue()
        {
            TermValues = new List<TermValue>();
            TermSetGuids = new List<Guid>();
        }

        public static TaxonomySearchResultValue Parse(string value)
        {
            var taxVal = new TaxonomySearchResultValue();
            var taxParts = value.Split(';');
            foreach (var taxPart in taxParts)
            {
                if (taxPart.StartsWith("GP0|#")) //Term?
                {
                    var termGuid = new Guid(taxPart.Replace("GP0|#", ""));
                    taxVal.TermValues.Add(new TermValue() { TermGuid = termGuid});
                }
                else if (taxPart.StartsWith("GTSet|#")) //Term Set?
                {
                    taxVal.TermSetGuids.Add(new Guid(taxPart.Replace("GTSet|#", "")));
                }
                else if (taxPart.StartsWith("L0|#")) //Term with label?
                {
                    var termParts = taxPart.Replace("L0|#0", "").Split('|');
                    var termGuid = new Guid(termParts[0]);
                    var termLabel = termParts[1];
                    var termVal = taxVal.TermValues.FirstOrDefault(ti => ti.TermGuid == termGuid);
                    if (termVal == null)
                        taxVal.TermValues.Add(new TermValue() {TermGuid = termGuid, Label = termLabel});
                    else
                        termVal.Label = termLabel;

                }
            }
            return taxVal;
        }

        public IList<TermValue> TermValues { get; set; }

        public IList<Guid> TermSetGuids { get; set; }


    }

    public class TermValue
    {
        public string Label { get; set; }

        public Guid TermGuid { get; set; }
    }
}

Usage

var taxValue = "L0|#09d72d96c-407f-4e45-b2e6-9361faf5808a|Office;L0|#0ac96f075-b7d2-4e90-8dc2-da8875f395fc|Home";
var taxInfo = TaxonomySearchResultValue.Parse(taxValue);
Console.WriteLine(taxInfo.TermValues[0].Label);  //print first term label

Your Answer

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

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