Second Life of a Hungarian SharePoint Geek

February 3, 2010

Some words about TaxonomyFieldValue and its WssId property

Filed under: SP 2010, Taxonomies — Tags: , — Peter Holpar @ 11:42

In these weeks I’m busy with planning and POCing a custom social site solution built on top of SP 2010. Lot of the requirements are related to the new managed metadata framework, so I’m planning to post some of the most exciting results in my blogs.

In this post I will introduce you the not-so-obvious WssId property of the TaxonomyFieldValue class.

TaxonomyFieldValue has three important properties (at least, for now):

Label (string): It is the Value (string) property of the Label selected by the user from the Labels property of the Term object

TermGuid (string, not Guid): it is the Id (Guid) property of the Term (inherited from TaxonomyItem)

WssId (int): What is that and where its value comes from?

When you assign value a TaxonomyField, an item is created for the selected Term (or if there is already an item for the term, that item is used)  in a hidden list called TaxonomyHiddenList in the root web of the site. The WssId is the Id of that item in the TaxonomyHiddenList .

To track the call chain of this process, see the following internal or private methods of TaxonomyField (if you don’t like boring deep technical content, you can skip this section):

The ValidateTaxonomyFieldValue is to validate the field value. First if the value is not yet resolved it tries to resolve that using the GetOrResolveUnvalidatedTaxonomyFieldvalue method. Both of these methods call the AddTaxonomyGuidToWss method, that calls with elevated privileges the AddTaxonomyGuidToWssCore method. This method calls first the GetLookupList method to get the TaxonomyHiddenList for the current site through the GetLookupListHelper method. There is a private static class of type ThreadSafeDictionary<Guid, Guid> called  lookupListIDs to help this lookup process and make that faster. After creating the new item in the hidden list, the  AddTaxonomyGuidToWssCore method returns with the ID of the new item.

But where should we get the WssId from if we have only a Term object that has not this information?

I’ve found two alternatives. The first – trivial – one is the static GetWssIdsOfTerm method of the TaxonomyField class:

int[] wssIds = TaxonomyField.GetWssIdsOfTerm(site, term.TermStore.Id, term.TermSet.Id, term.Id, false, 1);

wssIds[0] will contain the ID we need.

The other way is a bit more complicated but it illustrates how to work with the TaxonomyHiddenList list:

  1. private int GetWssIdByTermId(SPWeb rootWeb, Guid termId)
  2. {
  3.     int result = –1;
  4.  
  5.     if (rootWeb.Properties.ContainsKey("TaxonomyHiddenList"))
  6.     {
  7.         Guid taxonomyHiddenListId = new Guid(rootWeb.Properties["TaxonomyHiddenList"]);
  8.         SPList taxonomyHiddenList = rootWeb.Lists[taxonomyHiddenListId];
  9.         SPQuery query = new SPQuery();
  10.         // we might have included the IdForTermSet in the query but we assume that Guid is really unique
  11.         // so there should not be temrs in other terms sets having the same ID
  12.         query.Query = String.Format(@"<Where><Eq><FieldRef Name='IdForTerm' /><Value Type='Text'>{0}</Value></Eq></Where>", termId);
  13.         SPListItemCollection items = taxonomyHiddenList.GetItems(query);
  14.         if (items.Count == 1)
  15.         {
  16.             result = int.Parse(items[0]["ID"].ToString());
  17.         }
  18.     }
  19.     return result;
  20. }

OK, but why is it good for us to know the WssId of the TaxonomyFieldValue?

There might be several reasons, but one of them is that it can be used to query the list items through the CAML syntax, as illustrated:

  1. private SPListItemCollection GetItemsByTerm(SPList list, String fieldName, Term term)
  2. {
  3.     SPSite site = list.ParentWeb.Site;
  4.     SPWeb rootWeb = site.RootWeb;
  5.  
  6.     int wssId = GetWssIdByTermId(rootWeb, term.Id);
  7.     
  8.     // or we could use this one
  9.     // wssIds[0] will be the WssId we need
  10.     //int[] wssIds = TaxonomyField.GetWssIdsOfTerm(site, term.TermStore.Id, term.TermSet.Id, term.Id, false, 1);
  11.  
  12.     SPQuery query = new SPQuery();
  13.     query.Query = String.Format(@"<Where><Eq><FieldRef Name='Metadata' LookupId='TRUE' /><Value Type='Lookup'>{0}</Value></Eq></Where>", wssId);
  14.     SPListItemCollection items = list.GetItems(query);
  15.     return items;
  16. }

You could use the following format either:

<Where><Contains><FieldRef Name=’YourTaxonomyFieldName’ /><Value Type=’Text’>Title of your term</Value></Contains></Where>

But the above syntax queries the items only as text, not by reference ID.

To call the GetItemsByTerm method:

  1. SPListItemCollection result = GetItemsByTerm(list, "YourTaxonomyFieldName", term);
  2.  
  3. foreach (SPListItem item in result)
  4. {
  5.     Console.WriteLine("Title: {0}", item["Title"]);
  6. }

It is even easier to query items if you have a TaxonomyFieldValue:

  1. private SPListItemCollection GetItemsByTaxonomyFieldValue(SPList list, String fieldName, TaxonomyFieldValue value)
  2. {
  3.     SPSite site = list.ParentWeb.Site;
  4.     SPWeb rootWeb = site.RootWeb;
  5.  
  6.     SPQuery query = new SPQuery();
  7.     query.Query = String.Format(@"<Where><Eq><FieldRef Name='Metadata' LookupId='TRUE' /><Value Type='Lookup'>{0}</Value></Eq></Where>", value.WssId);
  8.     SPListItemCollection items = list.GetItems(query);
  9.     return items;
  10. }

To call the GetItemsByTaxonomyFieldValue method:

  1. SPListItemCollection result = GetItemsByTaxonomyFieldValue(list, "YourTaxonomyFieldName", value);
  2.  
  3. foreach (SPListItem item in result)
  4. {
  5.     Console.WriteLine("Title: {0}", item["Title"]);
  6. }

The code samples here show only a simple CAML query, you can create more complex ones, for example combining multiple conditions using logical And or Or to look up items having all or any of the meta data applied.

21 Comments »

  1. […] since in this case the control is not bound to a site, not like in the case of a TaxonomyField (see my former post about the […]

    Pingback by Build your own user interface components using the taxonomy controls « Second Life of a Hungarian SharePoint Geek — February 15, 2010 @ 04:48

  2. The taxonomy info is very useful.

    One question: you wrote “The ValidateTaxonomyFieldValue is to validate the field value. First if the value is not yet resolved it tries to resolve that using the GetOrResolveUnvalidatedTaxonomyFieldvalue method. Both of these methods call the AddTaxonomyGuidToWss method, …”

    Where is the code of these methods? They are not in your blog neither is the TaxonomyField documentation. I’m dealing with building taxonomy fields and this info is impotant.

    Thanks
    Sruli

    Comment by Sruli Ganor — February 21, 2010 @ 14:51

  3. Hi Sruli,

    As I wrote “see the following internal or private methods of TaxonomyField”. It means you can find these methods using Reflector in the the TaxonomyField class. Since they are not public, you can’t find them in the documentation.

    Peter

    Comment by pholpar — February 23, 2010 @ 00:07

  4. Hi I’m trying the GetWssIdsOfTerm method and it doesn’t return a wssId for a term that has not yet been used in a list item. As soon as I use the newly created term in a list item it returns an Id making the method work but this doesn’t help me. Any suggestion on how to get past this? I need a wssId in order to pre-populate a taxonomy field once a page loads. So even if a term has not been used in any list I need it to have a wssId for me to use.

    thanks

    Comment by Alla — July 13, 2010 @ 04:25

  5. actually nevermind the method for the hidden list solved that problem.

    awesome posts by the way

    Comment by Alla — July 13, 2010 @ 04:59

  6. First of all thanks for the great post.

    I have setup a content hub site (http://sp2010/). I have defined term store as follows:

    Global MetaData
    ABCCompany
    Branch
    London
    Paris
    Newyork

    I am using the terms defined as metadata field in another site (http://sp2010/sites/finance). The user Selects (Company-Address-Branch) combination in a drop down. I need to populate the Company Name, Branch Name in the metadata fields of a document library based on the selection. I am able to separate the Company, Address, Branch etc and then when I lookup for metadata field to update the values – I am not able to get the wssid for the Branch. Note – I have not used the values in the libarary yet.

    When I use the function of yours, I get only -1. However, when I add the values in the document library manually – say London for Branch – Then I get the values for wssid when i go through it thru code again.

    Is there a way to get the wssid for the term selected branch from the dropdown when the branch has not been entered in the library?

    Thanks for the time.

    Comment by Ramesh — November 1, 2010 @ 18:14

  7. An update to my previous question:

    Basically, I need to find out how to get wssid from the terms defined in the content hub term store – whether it is used in the sites or not.

    thanks for your time.

    Thanks,

    Comment by Ramesh — November 1, 2010 @ 18:27

    • Hi Ramesh,

      Sorry for the late answer! As far as I understand your issue, I have a solution for that and I hope I will have time soon to blog about that and other metadata related things.

      Peter

      Comment by Peter Holpar — December 2, 2010 @ 22:25

  8. I have term sets defined as Company Code, Company Name

    When the user pickups the Company Code – Name – Address combination in the drop down, I should separate the Code, Name and Add it to the metadata fields of the list. I am planning to add those fields through work flow, when the user creates the document.

    The user may select a Company Code, Name value which was not used in the document list, however defined in term set. How to get wssid of the Code, Name in this case? The GetWssIdsOfTerm method returns value only if the term is used already in the document list.

    Could you please let me know?

    Thanks.

    ramesh

    Thanks,

    Ramesh

    Comment by ramesh — November 9, 2010 @ 17:55

    • See my reply for your other comment. I hope my forthcoming posts will answer your question.

      Peter

      Comment by Peter Holpar — December 2, 2010 @ 23:43

  9. Thanks for very good info.

    I need to ask, because I am stuck with the problem as Alla initially had above.
    Starting with a clean termstore, I csv-import my entire taxonomy tree. After this, I use a small application that extracts the entire tree with belonging information. However; the wssId field are non existing. They remain so until I manually set them as values to my taxonomy columns. After that they will have a wssId, as you explained above.

    Is there a way to force a wssId generation without having to manually use them first? The ValidateTaxonomyFieldValue, AddTaxonomyGuidToWss etc are not exposed in TaxonomyField.
    I assume wssId is mandatory to set a managed metadata column? I would have hoped the TermGuid would be sufficient. (Using web services, UpdateListItem for this.)

    Alla seems to have found a solution for this, but I did not understand how.

    Comment by Tom — November 23, 2010 @ 15:17

    • I hope my forthcoming posts will answer your question too.

      Peter

      Comment by Peter Holpar — December 2, 2010 @ 23:47

  10. I implemented your whole solution only to find out exactly what other people have discovered.

    The problem I have with your solution is that it only works IF the term was used previously in the site. When a term is used it populates the hidden list, before this the WSSID is not in the hidden list.

    I am also not entirely sure what you are talking about when you say some private method exists called : AddTaxonomyGuidToWss.

    If it really does exist – post an example on how its done.

    Comment by MichaelL — December 21, 2010 @ 18:02

    • Hi Michael,

      To answer your last question first, you can use Reflector to check methods (either public or private / internal) of SharePoint (or any other -NET) assemblies. If you don’t know this tool, you should either learn to use it first, or simply ignore this part of the post.

      As you can see from the post and from your own experience, WssId is a kind of shortcut for the terms used on the site. This way it is the expected behavior that ID is created only for terms in use, and not all of the terms in the store.

      Could you write a few lines about why it is a problem for you? I illustrated WssId through an example that searches list items of the site for a given term reference. In this case it means no problem at all that non-used terms have no WssId, as they are not used in the list, so I wouldn’t like them to appear in the results.

      For a more detailed example, see this post: How to create WssId for terms that are not yet referenced from the site.

      Peter

      Comment by Peter Holpar — December 21, 2010 @ 21:51

  11. […] a year ago I wrote about TaxonomyFieldValue and its WssId property. In a recent comment I got the question how to get the WssId for terms that are not referenced by […]

    Pingback by How to create WssId for terms that are not yet referenced from the site « Second Life of a Hungarian SharePoint Geek — December 23, 2010 @ 00:45

  12. Just a FYI on the problem I had – not getting the wssId on my taxonomy term. It turned out that only the guid is sufficient to set a value to a taxonomy column (using updatelistitems on web services). I thought I would need the guid as well as the wssId. So the issue with no wssId turned out to be no problem for me.

    /T

    Comment by Tom — January 3, 2011 @ 10:58

    • Hi Tom,

      Nice to know your problem is “solved” this way. I thought you face a more difficult issue. After re-reading your original comment it seems I was a bit careless. Sorry!

      Peter

      Comment by Peter Holpar — January 3, 2011 @ 11:36

  13. Nice article, thanks

    Comment by George — October 27, 2011 @ 00:26

  14. Great post,

    I have a problem that is driving me crazy and i wonder if you can help?

    My problem is that any items that I tag are not having values created in the SSA->Metadata properties section. (the ows_tax_…values)

    I can tag them, confirm that the hidden /Lists/TaxonomyHiddenList is being updated, but after a full crawl the mapped and crawled properties are not being autocreated. (even microsoft are having trouble helping me)

    All these actions are done via the UI as I am not a coder!

    On a different subject i am able to create a “normal” metadata property, mapp it and then get it to display in search refinement.

    Comment by Ben Lloyd — January 13, 2012 @ 11:00

  15. Hi
    Péter,

    i need to set user profile property value of type taxonomy.

    SPServiceContext serviceContext = SPServiceContext.GetContext(site);
    UserProfileManager upm = new UserProfileManager(serviceContext);
    UserProfile up = upm.GetUserProfile(“userloginName”);

    TaxonomySession session = new TaxonomySession(site);
    TermStore termStore = session.TermStores[0];
    TermSet termSet = termsetColl[0];

    Term t = termSet.GetTerms(“MyTerm”);

    UserProfileValueCollection userProfileValues = up[“PropertyName”]; // in my case user profile property is single value allowed

    // BY using following line i can set this profile property value successfully
    userProfileValues.Value = “MyTerm”;
    up.Commit();

    // But in my case Term set structure like as follows
    // Companies (TermSet)
    // – Company 1(First Level Term)
    // – Account (Second Level Term)
    // – Company 2(First Level Term)
    // – Account (second level term)

    // so using above code it always set user profile property value as “Account” of company 2 even if i want to store value as “Account” of company 1

    // I have also tried to use following methods
    userProfileValues.AddTaxonomyTerm(term) // Here i am passing terms which i want to store

    but it is showing exception like can not convert taxonomyterm to string however it is working fine for multi allowed taxonomy user profile property
    i spent almost 3 days to achive this but i can’t get any sucess

    can you help me please?

    Comment by Kalpesh Vaghela — February 4, 2014 @ 20:33


RSS feed for comments on this post. TrackBack URI

Leave a reply to Tom Cancel reply

Blog at WordPress.com.