Friday, February 23, 2018

Search Sitecore Item In Azure Search Index

I have created an item in Sitecore with below fields and I pulled it from the Azure search based on item Id. Let see how we can do this:-

1.       Created an entity for LinkBox
public class LinkBox
{
    public string Title { get; set; }
    public string Description { get; set; }
    public string ImageUrl { get; set; }
    public string LinkText { get; set; }
    public string LinkUrl { get; set; }
}

2.       Created a search parameters class.
public class SearchParams
{
    public SearchParams()
    {
        SortBy = new List<SortQuery>();
        FieldsToSearch = new List<string>();
        FieldsToRetrieve = new List<string>();
        SearchFilterQuery = new List<Tuple<string, string, string, string>>();
        Skip = 0;
        Top = 100000;
    }

    public List<Tuple<string, string, string, string>> SearchFilterQuery { get; set; }
    public int Top { get; set; }
    public int Skip { get; set; }
    public IList<SortQuery> SortBy { get; set; }
    public IList<string> FieldsToSearch { get; set; }
    public IList<string> FieldsToRetrieve { get; set; }
    public bool IncludeTotalResultCount { get; set; }
}

3.       Created a LinkBoxSearchResult class to map the fields indexed in a document in azure search index.
public class AzureContentSearch
{
    [JsonProperty(PropertyName = "id_1")]
    public string ItemId { get; set; }
}

public class LinkBoxSearchResult : AzureContentSearch
{
    [JsonProperty(PropertyName = "title")]
    public string Title { get; set; }
    [JsonProperty(PropertyName = "description")]
    public string Description { get; set; }
    [JsonProperty(PropertyName = "imageurlfield")]
    public string ImageUrl { get; set; }
    [JsonProperty(PropertyName = "linktextfield")]
    public string LinkText { get; set; }
    [JsonProperty(PropertyName = "linkurlfield")]
    public string LinkUrl { get; set; }
}
4.       Created a static a client for Azure search. Search service name and Admin API key is residing in the app settings for me. You need to add your Azure search-service name and Admin API Key in the app setting file.
public static class SearchServiceOperations
{
    public static ILogger LoggerHelper { get; set; }
    private static readonly object threadlock = new object();
    private static SearchServiceClient serviceOperations;

    static SearchServiceOperations()
    {
        LoggerHelper = UnityResolver.Resolve<ILogger>();
    }

    public static SearchServiceClient CreateSearchServiceClient()
    {
        LoggerHelper.WriteDebug("CreateSearchServiceClient Method Started.");
        try
        {
            if (serviceOperations == null)
            {
                lock (threadlock)
                {
                    string searchServiceName = ConfigurationManager.AppSettings[Constants.SearchServiceName];
                    string adminApiKey = ConfigurationManager.AppSettings[Constants.SearchServiceAdminApiKey];
                    serviceOperations = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey));
                }
            }
        }
        catch (Exception ex)
        {
            LoggerHelper.WriteDebug("CreateSearchServiceClient Method Exception Caught. Exception Message is: " + ex.Message);
        }
        LoggerHelper.WriteDebug("CreateSearchServiceClient Method Ended.");
        return serviceOperations;
    }

    /// <summary>
    /// Method to Get the Index Client for the Particular Index
    /// </summary>
    /// <param name="IndexName">Index Name</param>
    /// <returns>ISearchIndexClient</returns>
    public static ISearchIndexClient CreateSearchIndexClient(string IndexName = Constants.WebIndex)
    {
        try
        {
            LoggerHelper.WriteDebug("CreateSearchIndexClient Method returning the Index.");
            return serviceOperations.Indexes.GetClient(IndexName);
        }
        catch (Exception ex)
        {
            LoggerHelper.WriteDebug("CreateSearchIndexClient Method Exception Caught. Exception Message is: " + ex.Message);
            return null;
        }
    }
}
5.       Added Content Repository to write logic to pull the data from Azure Search Index. You can analyze and map with the query mentioned below which we use in azure search explorer
search=id_1:d68fcae9c8e34172aeac251f315bd7e5&$select=title,description&$filter=template_1 eq 'a68fcde9c8e34172aeac251f315ca7e5' and language_1 eq 'en'

·         search=id_1:d68fcae9c8e34172aeac251f315bd7e5 Here id_1 is the item id field in azure index and value is the item Guid for our link box item in lower case without any special characters.
·         select=title,description  Fields to retrieve in the below code refers to this section of the query
·         filter: In this section of query we have passed language and template id Guid in the ‘and’ clause.

public enum MultiCriteriaOperation
{
    and = 1,
    or = 2,
}
public class SortQuery
{
    public string FieldName { get; set; }
    public SortOrder Order { get; set; }
    public enum SortOrder { Ascending, Descending, }
}
public class ContentRepository : IContentRepository
{
    public ILogger LoggerHelper { get; set; }

    public ContentRepository(ILogger _logger, ISearch _azureSearch)
    {
        SearchServiceOperations.CreateSearchServiceClient();
        LoggerHelper = UnityResolver.Resolve<ILogger>();
    }

    char[] specialCharacters = new char[] { '+', ',', '.', '!', '(', ')', '{', '}', '[', ']', '^', '~', '*', '?', ':', '\\' };


    public LinkBox GetLinkBoxDetailsById(string id, string language)
    {
        LoggerHelper.WriteDebug("GetLinkBoxDetailsById method: Started");
        var linkBoxItem = new LinkBox();
        try
        {
            var fieldsToretrieve = new List<string>();
            fieldsToretrieve.Add(Constants.Title);
            fieldsToretrieve.Add(Constants.Description);
            fieldsToretrieve.Add(Constants.ImageUrlfield);
            fieldsToretrieve.Add(Constants.LinkUrl);
            fieldsToretrieve.Add(Constants.LinkTextField);

            var results = GetLinkBoxByIdFromAzure(id, Constants.LinkBoxTemplateId, fieldsToretrieve, language);
            if (results != null && results.Results.Count > 0)
            {
                var item = results.Results.First();
                if (item != null)
                {
                    linkBoxItem.Title = item.Document?.Title;
                    linkBoxItem.Description = item.Document?.Description;
                    linkBoxItem.ImageUrl = item.Document?.ImageUrl;
                    linkBoxItem.LinkUrl = item.Document?.LinkUrl;
                    linkBoxItem.LinkText = item.Document?.LinkText;
                }
            }
        }
        catch (Exception exc)
        {
            LoggerHelper.WriteDebug(string.Format("GetLinkBoxDetailsById method Exception occured", exc.Message));
            throw;
        }
        LoggerHelper.WriteDebug("GetLinkBoxDetailsById method: End");
        return linkBoxItem;
    }

    private DocumentSearchResult<LinkBoxSearchResult> GetLinkBoxByIdFromAzure(string id, string templateId,
        List<string> fieldsToretrieve, string language = "en", List<SortQuery> sortQueries = null)
    {
        DocumentSearchResult<LinkBoxSearchResult> response = null;
        if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(templateId))
        {
            string searchText = Constants.IdField + ":" + GenericUtilities.GetAzureFormattedGuid(id).ToLower();

            SearchParams searchParameters = new SearchParams();

            //Filter For language
            searchParameters.SearchFilterQuery.Add(new Tuple<string, string, string, string>(Constants.LanguageField,
            Convert.ToString(Enums.SearchOperator.eq), "'" + language + "'", Enums.MultiCriteriaOperation.and.ToString()));

            //Filter For template
            searchParameters.SearchFilterQuery.Add(new Tuple<string, string, string, string>(Constants.TemplateField,
            Convert.ToString(Enums.SearchOperator.eq), "'" + GenericUtilities.GetAzureFormattedGuid(templateId) + "'", string.Empty));

            ////Field That We want to Show records in View Page
            searchParameters.FieldsToRetrieve = fieldsToretrieve;

            //How many records want to display page at a time
            searchParameters.Top = 1;
            //If Skip Count has any count value
            searchParameters.Skip = 0;

            if (sortQueries != null)
                searchParameters.SortBy = sortQueries;

            response = GetLinkBox(searchText, searchParameters);
        }
        return response;
    }

    private DocumentSearchResult<LinkBoxSearchResult> GetLinkBox(string query, SearchParams searchParams)
    {
        ISearchIndexClient searchIndexClient = SearchServiceOperations.CreateSearchIndexClient();
        SearchParameters parameters = new SearchParameters()
        {
            Filter = QueryBuilder.GenerateFilterQuery(searchParams.SearchFilterQuery),
            OrderBy = QueryBuilder.GetSelectedSort(searchParams),
            QueryType = QueryType.Full,
            SearchFields = searchParams.FieldsToSearch,
            IncludeTotalResultCount = searchParams.IncludeTotalResultCount,
            Select = searchParams.FieldsToRetrieve,
            Skip = searchParams.Skip,
            Top = searchParams.Top
        };
        return searchIndexClient.Documents.Search<LinkBoxSearchResult>(query, parameters);
    }
}

Call the content repository from your controller action by passing item id and context language.

In case you feel any item missing while implementing this on your project, write a comment to me so that I can add the missing piece for you.

Thanks

No comments:

Post a Comment