FTC to CAM – Setting indexed property bag keys using CSOM


Related on to my previous post on storing metadata values for sites using CSOM, I got quite few questions on how would we then use these property bag keys or their values in aggregations. In general with SP2013 easiest solution would be probably use search to crawl the property bag values and then use those as filters for the listing the sites based on end user selection.

Steve Peschka has explained in his blog post how we can set property bag key to be indexed using server side code, but this SPWeb.IndexedPropertyKeys property is not available for CSOM, so you can’t directly use same pattern to set the property bag values to be indexed and therefore we’ll need to look for alternative route for achieving the same end result, like in so many times with CSOM or CAM model in general.

After however analysing slightly more detailed how IndexPropertyKeys property works (using ILSpy), we can come up with solution also using CSOM. Server side implementation works in a way that property bag values which are set to be index by search are actually stored to property bag as well, using key ‘vti_indexedpropertykeys’. This list is encoded and property bag entries are separated with pipe character. This means that as long as we use exact same format in our provider hosted app and store the list to host web in similar ways as using server side code, we are fine.

To demonstrate this, I have updated the reference code which was also shared in previous blog as follows.

1. We need to have the function, which formats our list of property bag keys to proper format, which is then stored to SPWeb.Properties using CSOM


/// <summary>
/// Used to convert the list of property keys is required format for listing keys to be index
/// </summary>
/// <param name="keys">list of keys to set to be searchable</param>
/// <returns>string formatted list of keys in proper format</returns>
internal static string GetEncodedValueForSearchIndexProperty(List<string> keys)
{
    StringBuilder stringBuilder = new StringBuilder();
    foreach (string current in keys)
    {
        stringBuilder.Append(Convert.ToBase64String(Encoding.Unicode.GetBytes(current)));
        stringBuilder.Append('|');
    }
    return stringBuilder.ToString();
}

2. Few additional lines to actually get the keys to be stored and to save it to property bag using ‘vti_indexedpropertykeys’ key.


// Confirm that both property bag values are indexed by search
List<string> keys = new List<string>();
keys.Add(DeployManager.ProjectCodeBagID);
keys.Add(DeployManager.ProjectPhaseBagID);
SetPropertyValue(clientContext, props, "vti_indexedpropertykeys", GetEncodedValueForSearchIndexProperty(keys));

That’s it and now those custom properties stored in the property bag of the site (spweb) are being indexed by search and we can build search driven content based on the information entered to these property bag entries. This solution obviously works in both on-premises and in cloud.

Disclaimer. Since this functionality is not currently exposed as public property in CSOM, there is a risk that implementation might be changing in future. This is valid, but relative small risk, but you should be aware of it. I’ll be personally using this pattern as well with few of my customers related on the FTC to CAM migration work.

References

Here’s just the collected list of referenced locations and materials

Comments (6)

  1. manoj.faria says:

    Its good to know that property bag key values can be updated using CSOM. One follow-up question…is there an O365 security event handler (especially an event which is raised when users are added/deleted from the site) wherein i can update spweb custom property. My goal is to query and retrieve the list of users of a site (who have contribute permissions) using OOTB Content By Search webpart.

    Thanks in advance.

  2. sonofthesun says:

    Hi Manoj.Faria

    Technically you could attach remote event receiver to GroupUserAdded or GroupUserDeleted events of the host web. This would give you change to process some code when users are added or removed from groups.

  3. Sven says:

    Thanks for sharing Vesa

  4. Travis says:

    Thanks for sharing, Vesa!  This is exactly what I am hoping to accomplish, but I seem to be having some difficulty.  I have a SPWeb with the following property bag keys/values:

    TestProperty001 = TestValue001

    TestProperty002 = TestValue002

    TestProperty003 = TestValue003

    vti_indexedpropertykeys = VGVzdFByb3BlcnR5MDAx|VGVzdFByb3BlcnR5MDAy|VGVzdFByb3BlcnR5MDAz|

    I then perform a full crawl, but I do not see my properties surfaced as managed properties.  Is there something I may be missing?

  5. Travis says:

    I figured it out!  I was trying to do this JavaScript instead of .NET, and I didn't realize I needed to convert to Unicode before base64 encoding.

    Now that I am converting to unicode, everything seems happy.

    Thanks!  

  6. Jasper says:

    Hi travis, how exactly did you fix this in javascript? I'm having troubles converting to unicode and base64