6 minute read

When you work on SharePoint project you often see solutions that are built by using the search engine of SharePoint. To perform queries against the search engine you define 'Metadata properties' these metadata properties need to be mapped to a column within SharePoint. Here comes the first problem when you define those properties through the UI you first have to create an item where in the column is filled in. Then you have to perform all Full Crawl to be able to set the mapping to the Metadata property. When you define the properties within a feature receiver you can create the mapping and you do not have to perform the crawl. Ok, so let's start with creating the properties within a feature receiver. First we will have to define an object in which we can save the mapping like below:

  
    [CLSCompliant(false)]
    public class SearchMapping
    {
        /// <summary>
        /// Gets or sets the category.
        /// </summary>
        /// <value>The category.</value>
        public string Category { get; set; }

        /// <summary>
        /// Gets or sets the type of the variant.
        /// </summary>
        /// <value>The type of the variant.</value>
        public int VariantType { get; set; }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name { get; set; }

        /// <summary>
        /// Gets or sets the property set ID.
        /// </summary>
        /// <value>The property set ID.</value>
        public Guid PropertySetId{get;set;}

        /// <summary>
        /// Gets or sets the type.
        /// </summary>
        /// <value>The type.</value>
        public ManagedDataType MappedType { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="SearchMapping"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="category">The category.</param>
        /// <param name="variantType">Type of the variant.</param>
        /// <param name="propertySetId">The property set ID.</param>
        /// <param name="type">The type.</param>
        public SearchMapping(string name, string category, int variantType, Guid propertySetId, ManagedDataType type)
        {
            Category = category;
            VariantType = variantType;
            Name = name;
            PropertySetId = propertySetId;
            MappedType = type;
        }
    }

 

Within the object you can see that we have all the properties to define a mapping. You can get the values of these properties by creating a metadata property through the UI and click on the field you have mapped to the property. For a custom field within SharePoint the values will look like the following:

  • Category:The category of the field. This will be 'SharePoint'.
  • VariantType:The variant type will represent the datatype. For a text field this will be '31'.
  • Name:The name of the mapping mostly the internal name of the column prefixed with ows_
  • PropertySetId:The id of the property set for SharePoint columns this is: '00130329-0000-0130-c000-000000131346'.
  • MappedType:This is the 'ManagedDataType' of the property.

When we have the mapping we can define the feature activation method.

        /// <summary>
        /// Occurs after a Feature is activated.
        /// </summary>
        /// <param name="properties">An <see cref="T:Microsoft.SharePoint.SPFeatureReceiverProperties"/> object that represents the properties of the event.</param>
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            if (properties == null)
                throw new ArgumentNullException("properties", "Argument 'properties' cannot be 'null'");

            SPWebApplication webApplication = (SPWebApplication)properties.Feature.Parent;
            SPServiceContext serviceContext = SPServiceContext.GetContext(webApplication.ServiceApplicationProxyGroup, SPSiteSubscriptionIdentifier.Default);
            SearchServiceApplicationProxy searchApplicationProxy = serviceContext.GetDefaultProxy(typeof(SearchServiceApplicationProxy)) as SearchServiceApplicationProxy;

            // Service Application Info object to retrieve the application id for the search service.
            SearchServiceApplicationInfo searchApplictionInfo = searchApplicationProxy.GetSearchServiceApplicationInfo();

            // Retrieve the search application instance for the specified id.
            SearchServiceApplication searchApplication = SearchService.Service.SearchApplications.GetValue<SearchServiceApplication>(searchApplictionInfo.SearchServiceApplicationId);
            var searchSchema = new Schema(searchApplication);

            SearchMapping office12 = new SearchMapping("12", "Office", 64, new Guid("f29f85e0-4ff9-1068-ab91-08002b27b3d9"), ManagedDataType.DateTime);
            SearchMapping basic15 = new SearchMapping("15", "Basic", 64, new Guid("b725f130-47ef-101a-a5f1-02608c9eebac"), ManagedDataType.DateTime);
            SearchMapping PageImage = new SearchMapping("ows_PageImageUrl", "SharePoint", 31, new Guid("00130329-0000-0130-c000-000000131346"), ManagedDataType.Text);

            CreateMappedSearchProperty(searchSchema, new List<SearchMapping>() { office12, basic15 }, "Created", ManagedDataType.DateTime);
            CreateMappedSearchProperty(searchSchema, new List<SearchMapping>() { PageImage }, "PageImage", ManagedDataType.Text);
        }

Within the feature we get the SearchServiceApplication by getting it from the SearchService. When we have the SearchServiceApplication we can get the schema of the SearchApplcation. With this schema we can begin to create the new Metadata properties. In the code you see that I create three types of 'SearchMapping' object these mapping will be used for two properties. the 'office12' and 'basic15' for a Metadata property called 'Created' and 'PageImage' for a property called 'PageImage'. The 'Created' metadata property was one that was OOTB within SharePoint 2007 and does not exist within SharePoint 2010. After we created the mapping object I call on a method called CreateMappedSearchProperty() this method will create the property and needs the schema, mappings (as a generic list of 'searchmapping') title and type as parameters.

private static void CreateMappedSearchProperty(Schema searchSchema, List<SearchMapping> mappings, string propertyName, ManagedDataType type)
{
   ManagedProperty managedProperty = CreateProperty(searchSchema, propertyName, type);
   var mappingCollection = CreateNewMappingCollection(searchSchema, mappings, managedProperty);
   managedProperty.SetMappings(mappingCollection);
   managedProperty.Update();
}

We create or get the managed property if it already exist by calling the method CreateProperty() that needs the schema, property name and type as parameters. When we have the property we create a new mapping collection by calling the method CreateNewMappingCollection() that needs the schema, list of mappings, and the property as parameters. This will return a 'MappingCollection' object that we will pass into the managedProperty. To save the property we call the Update() method on the managed property. The methods that are described above look like.

private static MappingCollection CreateNewMappingCollection(Schema searchSchema, List<SearchMapping> mappings, ManagedProperty prop)
        {
            var retVal = new MappingCollection();

            foreach(SearchMapping mapping in mappings){
                //get the property categories
                CategoryCollection categories = searchSchema.AllCategories;
                
                if (categories.Contains(mapping.Category))
                {
                    var category = categories[mapping.Category];
                    CrawledProperty crawledProperty = category.CreateOrGetCrawledProperty(mapping.Name, false, mapping.PropertySetId, mapping.VariantType);
                    retVal.Add(new Mapping(crawledProperty.Propset, mapping.Name, mapping.VariantType, prop.PID)); 
                }
            }

            return retVal;
        }
            
        private static ManagedProperty CreateProperty(Schema searchSchema, string propertyName, ManagedDataType managedDataType)
        {
            //if the managedproperty is available only update the mapping else create the managedProperty
            if (!searchSchema.AllManagedProperties.Contains(propertyName))