Creating Managed Properties

Managed properties for search can be created using a feature and they can be mapped to crawled properties. See Creating Crawled Properties for setting up your crawled properties first without needing to run a full crawl during deployment. Managed properties have to be set up before you can query on any metadata field (SharePoint, People, Mail, etc.).

In order to be able to manage the creation of managed properties and how they get mapped to crawled properties we created a manager class. When this class gets instantiated it creates a reference to the specified search service and search service application on the local farm.

image

Once we have a reference to the service application we can store the search schema in a private field. The Schema  holds all of the crawled, managed, and mapped properties needed for search.

image 

From the schema we can get collections of all the crawled and managed properties and store them in private fields in our manager as well.

image

Then, once the manager has been instantiated, the add method can be called repeatedly to add a series of properties. If the property already exists we retrieve it for updating it’s mappings to crawled properties. If the retrieved property already has mappings we remove those that match the current managed property name in order to update them from scratch.

    /// <summary>
    /// A manager for adding and updating managed properties for Enterprise Search.
    /// </summary>
    public class ManagedPropertyManager
    {
        private Schema _schema;
        private IEnumerable<CrawledProperty> _crawledProperties;
        private ManagedPropertyCollection _allManagedProperties;

        public ManagedPropertyManager(string serviceName, string applicationName)
        {
            SPFarm farm = SPFarm.Local;
            SearchService searchService = farm.Services.GetValue<SearchService>(serviceName);
            SearchServiceApplication searchApp =
                searchService.SearchApplications.GetValue<SearchServiceApplication>(applicationName);
            _schema = new Schema(searchApp);
            _crawledProperties = _schema.QueryCrawledProperties(string.Empty, 1000000, Guid.NewGuid(),
                string.Empty, true).Cast<CrawledProperty>();
            _allManagedProperties = _schema.AllManagedProperties;
        }

        /// <summary>
        /// Adds a managed property for search that is mapped to the specified crawled property.
        /// An exception is thrown if the crawled property is not found.
        /// </summary>
        /// <param name="propertyName">The name of the managed property.</param>
        /// <param name="propSet">The crawled property's property set for which a mapping is being added.</param>
        /// <param name="dataType">The data type of the managed property.</param>
        public void AddPropertyWithMapping(string propertyName, PropertySet propSet, ManagedDataType dataType)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName");
            }

            ManagedProperty property = null;

            if (_allManagedProperties.Contains(propertyName))
            {
                property = _allManagedProperties[propertyName];
            }
            else
            {
                try
                {
                    property = _allManagedProperties.Create(propertyName, dataType);
                }
                catch (Exception ex)
                {
                    //log exception
                }
            }

            if (property != null)
            {
                var mappings = property.GetMappings();
                var crawledPropertyName = Settings.GetCrawledPropertyPrefix(propSet) + propertyName;
                var crawledProperty = _crawledProperties.FirstOrDefault(c => c.Name.Equals(crawledPropertyName));

                if (crawledProperty != null)
                {
                    for (int i = 0; i < mappings.Count; i++)
                    {
                        if (string.Compare(mappings[i].CrawledPropertyName, crawledProperty.Name, true) == 0)
                        {
                            mappings.Remove(mappings[i]);
                        }
                    }

                    var mapping = new Mapping(
                        crawledProperty.Propset,
                        crawledProperty.Name,
                        crawledProperty.VariantType,
                        property.PID);

                    if (!mappings.Contains(mapping))
                    {
                        mappings.Add(mapping);
                        property.SetMappings(mappings);
                        property.Update();
                    }
                }
                else
                {
                    //throw exception
                }
            }
            else
            {
                //throw exception
            }
        }
    }

The ManagedPropertyManager can then be used in a feature receiver to provision managed properties on deployment.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    _properties = properties;

    try
    {
        var manager = new ManagedPropertyManager(
            SearchServiceName.EnterpriseSearch,
            SearchServiceApplicationName.Default);

        manager.AddPropertyWithMapping("Property1", PropertySet.SharePoint, ManagedDataType.Text);
        manager.AddPropertyWithMapping("Property2", PropertySet.Mail, ManagedDataType.YesNo);
        manager.AddPropertyWithMapping("Property3", PropertySet.People, ManagedDataType.DateTime);
    }
    catch (Exception ex)
    {
        //log exception
    }
}

After the feature has finished activating, you will see your properties in the Metadata Properties list in Central Administration.

image

And if you click on the property name you can view and change it’s settings. The mappings to the crawled properties will also be displayed on this page (the AboutMe property is displayed below).

image

Creating Crawled Properties

You can create crawled properties for search programmatically without needing to start a full crawl. This can be helpful when adding managed properties that map to crawled properties and you can’t be sure the crawled properties will exist at that time. In the following, we create some crawled properties using a manager class and wrap up the logic in a Feature that can be deployed in a solution package.

The first thing we do is create a manager class that adds crawled properties using the object model:

public class CrawledPropertyManager
{
    private Schema _schema;
    private IEnumerable<CrawledProperty> _crawledProperties;

    public CrawledPropertyManager(string serviceName, string applicationName)
    {
        SPFarm farm = SPFarm.Local;
        SearchService searchService = farm.Services.GetValue<SearchService>(serviceName);
        SearchServiceApplication searchApp =
            searchService.SearchApplications.GetValue<SearchServiceApplication>(applicationName);
        _schema = new Schema(searchApp);
        _crawledProperties = _schema.QueryCrawledProperties(string.Empty, 1000000, Guid.NewGuid(),
            string.Empty, true).Cast<CrawledProperty>();
    }

    public void AddCrawledProperty(string propertyName, PropertySet propSet, VariantType variant)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            throw new ArgumentNullException("propertyName");
        }

        propertyName = Settings.GetCrawledPropertyPrefix(propSet) + propertyName;
        var crawledProperty = _crawledProperties.FirstOrDefault(c => c.Name.Equals(propertyName));

        //only create the crawled property if one doesn't already exist
        if (crawledProperty == null)
        {
            var propSetId = Settings.GetPropertySetId(propSet);
            var category = _schema.AllCategories[propSetId];

            try
            {
                category.CreateCrawledProperty(propertyName, false, propSetId, (int)variant);
                category.Update();
            }
            catch (Exception ex)
            {
                Logging.Logger.Log(ex);
            }
        }
    }
}

Then create a Feature which uses the manager for adding crawled properties one at a time.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    _properties = properties;

    try
    {
        var manager = new CrawledPropertyManager(
            SearchServiceName.EnterpriseSearch,
            SearchServiceApplicationName.Default);

        manager.AddCrawledProperty(“Property1″, PropertySet.People, VariantType.String);
        manager.AddCrawledProperty(“Property2″, PropertySet.People, VariantType.Datetime);
        manager.AddCrawledProperty(“Property3″, PropertySet.People, VariantType.Taxonomic);
    }
    catch (Exception ex)
    {
        //log exception
    }
}

We use a Settings class to factor out the property set and variant types:

public static class Settings
{
    public static Guid GetPropertySetId(PropertySet propSet)
    {
        var retVal = Guid.Empty;
        var propSetIdString = string.Empty;

        switch (propSet)
        {
            case PropertySet.Basic:
                propSetIdString = PropertySetGuid.Basic;
                break;
            case PropertySet.Mail:
                propSetIdString = PropertySetGuid.Mail;
                break;
            case PropertySet.Office:
                propSetIdString = PropertySetGuid.Office;
                break;
            case PropertySet.People:
                propSetIdString = PropertySetGuid.People;
                break;
            case PropertySet.SharePoint:
                propSetIdString = PropertySetGuid.SharePoint;
                break;
        }

        if (!string.IsNullOrEmpty(propSetIdString))
        {
            retVal = new Guid(propSetIdString);
        }

        return retVal;
    }

    public static string GetCrawledPropertyPrefix(PropertySet propSet)
    {
        var retVal = string.Empty;

        switch (propSet)
        {
            case PropertySet.People:
                retVal = CrawledPropertyPrefix.People;
                break;
            case PropertySet.SharePoint:
                retVal = CrawledPropertyPrefix.SharePoint;
                break;
        }

        return retVal;
    }
}

public enum PropertySet
{
    Basic,
    Mail,
    Office,
    People,
    SharePoint
}

internal struct PropertySetGuid
{
    public const string SharePoint = "00130329-0000-0130-c000-000000131346";
    public const string Basic = "0b63e343-9ccc-11d0-bcdb-00805fccce04 ";
    public const string Mail = "aa568eec-e0e5-11cf-8fda-00aa00a14f93";
    public const string Office = "f29f85e0-4ff9-1068-ab91-08002b27b3d9";
    public const string People = "00110329-0000-0110-c000-000000111146";
}

internal struct CrawledPropertyPrefix
{
    public const string SharePoint = "ows_";
    public const string People = "urn:schemas-microsoft-com:sharepoint:portal:profile:";
}

public struct SearchServiceName
{
    public const string EnterpriseSearch = "OSearch14";
}

public struct SearchServiceApplicationName
{
    public const string Default = "Search Service Application";
}

public enum VariantType
{
    String = 31,
    YesNo = 11,
    Binary = 12,
    Integer = 20,
    Datetime = 64,
    Taxonomic = 4127
}

After deploying the solution and activating our feature the new properties will show up in Central Administration > Search Service Application > Crawled Properties:

image

Note: nothing has been indexed yet by the search crawler. We must still do a full crawl and add managed properties that map to our crawled properties before we can search. See Creating Managed Properties for creating and mapping managed properties to crawled properties.

You can do the same thing in PowerShell. Here is a great post that describes how to do this: Creating Enterprise Search Metadata Property Mappings with PowerShell.

Follow

Get every new post delivered to your Inbox.