Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple search in Sitecore

Using Sitecore 8.2 with MVC.
I'm trying to implement the search functionality in a MVC view. (with a textbox and submit button)

There is a folder in the content tree called Books which has a list of items. Each item will have these fields - Title, Author, Price

When user searches for a term, it will be checked for a match with any of the 3 fields of the item and return the results.

This method is not working as it returns null Item.

public PartialViewResult GetSearchBooks(string txtSearch)
 {

   string index = string.Format("sitecore_{0}_index", Sitecore.Context.Database.Name);
   List<SearchResultItem> query;
   List<Item> matches = new List<Item>();

   using (var context = ContentSearchManager.GetIndex(index).CreateSearchContext())
   {
     query = context.GetQueryable<SearchResultItem>()
             .Where(p => p.Path.StartsWith("/sitecore/content/Book")).ToList();
   }

   foreach(SearchResultItem sritem in query)
   {
     Item item = sritem.GetItem(); //item is null here

     if(item.Fields["Title"].Value.Contains(txtSearch) ||
        item.Fields["Title"].Value.Contains(txtSearch) ||
        item.Fields["Title"].Value.Contains(txtSearch))
        matches.Add(item);
   }

   return(matches);
}

Is it the right approach. If not please suggest one.

like image 496
Qwerty Avatar asked Dec 15 '22 03:12

Qwerty


1 Answers

Paths

Avoid querying the path like this:

context.GetQueryable<SearchResultItem>()
         .Where(p => p.Path.StartsWith("/sitecore/content/Book"));

Instead use

context.GetQueryable<SearchResultItem>()
             .Where(p => p.Paths.Contains(idOfBookFolderItem));

For more info on why, see http://blog.paulgeorge.co.uk/2015/05/29/sitecore-contentsearch-api-filtering-search-on-folder-path/

Approach

You need to hand the entire query to the search api in one go.

        List<SearchResultItem> matches;

        using (var context = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
        {
            var predicate = PredicateBuilder.True<SearchResultItem>();

            // must have this (.and)
            predicate = predicate.And(p => p.Paths.Contains(bookFolderItem.ID));

            // must have this (.and)
            predicate = predicate.And(p => p.Name == searchTerm);

            matches = context.GetQueryable<SearchResultItem>().Where(predicate).ToList();
        }

This returns SearchResultItems not Items. If you need the item, just call GetItem.

Matches[i].GetItem()

Null items

This may indicate that your index is out of sync with the database. Try re-indexing from control panel, or in the case of the web database, REpublish the expected content.

Searching template fields

This just searches against the item name. You're limited to being able to specify the generic fields in SearchResultItem class. If you want to search specific fields on items, you can inherit from SearchResultItem and add those fields.

public class BookSearchResultItem : SearchResultItem
{
    [IndexField("Book Title")]
    public string BookTitle { get; set; }

    [IndexField("Book Author")]
    public string BookAuthor { get; set; }
}

You can then pass this into the query and search on those fields

        List<BookSearchResultItem> matches;

        using (var context = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
        {
            var predicate = PredicateBuilder.True<BookSearchResultItem>();

            // must have this (.and)
            predicate = predicate.And(p => p.Paths.Contains(bookFolderItem.ID));

            // must have this (.and)
            predicate = predicate.And(
                PredicateBuilder.False<BookSearchResultItem>() // in any of these fields
                .Or(p => p.BookTitle == searchTerm) 
                .Or(p => p.BookAuthor == searchTerm)
                .Or(p => p.Name == searchTerm)); 

            matches = context.GetQueryable<BookSearchResultItem>().Where(predicate).ToList();
        }

Searching all 'content'

If you find that having to specify the explicit fields is an unwanted hassle or you are performing searches across different templates with different fields, you can instead use the special computed 'content' field which combines all the text data from an item into one indexed field. So instead of the original query which did this

predicate = predicate.And(p => p.Name == searchTerm);

You can instead do just use

predicate = predicate.And(p => p.Content == searchTerm);

Which will find results where the searchterm exists in any field on the item.

like image 177
Paul George Avatar answered Dec 28 '22 09:12

Paul George