Search

Enabling your modelled templates to work with Sitecore 7’s search functionality is relatively straight forward, all it requires are some additional attributes, constructors and parameter to the GetField method. After that you’ll need to add some configuration to your includes which will allow the Sitecore search to understand the modelled templates you’ve created.

Modelled Templates

First let’s take the templates we used in the model templates step, where we modelled our example templates, and make the changes required to enable them to be used in search queries.

  • Page
    • Title – Single Line Text
    • Body – Rich Text
    • Image – Image
  • Home Page : Page
    • Related Pages – Multilist

Key points

  • Fortis uses the existing attributes used for searching with Sitecore. This means you can use attributes like “PredefinedQuery”.
  • Using search with your models introduces the notion of lazy loading item data. When running a search query Fortis won’t load the actual item until you do something that requires the item itself.
  • The extra parameter being passed into the GetField method is the name of the field stored in the index.
  • Searching against a list field currently requires an extra property on your modelled template which directly accesses the Value property of the normal property you would use. This is explained in more detail at the end of the example.

Page Template

[TemplateMapping("{AF49395C-74BB-4ACF-8E01-F2B5BEECA8FE}", "InterfaceMap")]
public partial interface IPage : IItemWrapper
{
	[IndexField("title")]
	ITextFieldWrapper Title { get; }
	[IndexField("body")]
	IRichTextFieldWrapper Body { get; }
	[IndexField("image")]
	IImageFieldWrapper Image { get; }
}

[PredefinedQuery("TemplateId", ComparisonType.Equal, "{AF49395C-74BB-4ACF-8E01-F2B5BEECA8FE}", typeof(Guid))]
[TemplateMapping("{AF49395C-74BB-4ACF-8E01-F2B5BEECA8FE}")]
public partial class Page : ItemWrapper, IPage
{
	public Page(Item item, ISpawnProvider spawnProvider)
		: base(item, spawnProvider)
	{ }

	public Page(Guid id, Dictionary<string, object> lazyFields, ISpawnProvider spawnProvider)
		: base(id, lazyFields, spawnProvider)
	{ }

	[IndexField("title")]
	public ITextFieldWrapper Title
	{
		get { return GetField<TextFieldWrapper>("Title", "title"); }
	}

	[IndexField("body")]
	public IRichTextFieldWrapper Body
	{
		get { return GetField<RichTextFieldWrapper>("Body", "body"); }
	}

	[IndexField("image")]
	public IImageFieldWrapper Image
	{
		get { return GetField<ImageFieldWrapper>("Image", "image"); }
	}
}

Home Page Template

[TemplateMapping("{02F5002C-325E-4E5A-9C93-A97724ED3400}", "InterfaceMap")]
public partial interface IHomePage : IPage
{
	[IndexField("related pages")]
	IListFieldWrapper RelatedPages { get; }
	[IndexField("related pages")]
	IEnumerable<Guid> RelatedPagesValue { get; }
}

[PredefinedQuery("TemplateId", ComparisonType.Equal, "{02F5002C-325E-4E5A-9C93-A97724ED3400}", typeof(Guid))]
[TemplateMapping("{02F5002C-325E-4E5A-9C93-A97724ED3400}")]
public partial class HomePage : ItemWrapper, IHomePage
{
	public HomePage(Item item, ISpawnProvider spawnProvider)
		: base(item, spawnProvider)
	{ }

	public HomePage(Guid id, Dictionary<string, object> lazyFields, ISpawnProvider spawnProvider)
		: base(id, lazyFields, spawnProvider)
	{ }

	[IndexField("title")]
	public ITextFieldWrapper Title
	{
		get { return GetField<TextFieldWrapper>("Title", "title"); }
	}

	[IndexField("body")]
	public IRichTextFieldWrapper Body
	{
		get { return GetField<RichTextFieldWrapper>("Body", "body"); }
	}

	[IndexField("image")]
	public IImageFieldWrapper Image
	{
		get { return GetField<ImageFieldWrapper>("Image", "image"); }
	}

	[IndexField("related pages")]
	public IListFieldWrapper RelatedPages
	{
		get { return GetField<ListFieldWrapper>("Related Pages", "related pages"); }
	}

	[IndexField("related pages")]
	public IEnumerable<Guid> RelatedPagesValue
	{
		get { return RelatedPages.Value; }
	}
}

As mentioned in the key points there is an issue in that the LINQ to Sitecore is unable to translate the IEnumerable<Guid> Value property on the IListFieldWrapper. For this reason we need to add a property which directly exposes the Value property on the modelled class and interface. Ideally we’d like to write the LINQ query like the following pseudo example.

var itemId = new Guid("");

mySearchQuery.Where(item => item.RelatedPages.Value.Contains(itemId) && item.Title.Value == "My Title");

While the item.Title.Value part is translated without issue unfortunately the LINQ to Sitecore doesn’t understand the item.RelatedPages.Value. We end up having to add the extra property and write our search query like the following.

var itemId = new Guid("");

mySearchQuery.Where(item => item.RelatedPagesValue.Contains(itemId) && item.Title.Value == "My Title");

Querying

Querying the search index is very much like how you would do it through the Sitecore search API. Essentially the only difference is that the item search factory provides a Search method which returns a IQueryable you can use to build your query.

public class MySearchProvider
{
	private readonly IItemSearchFactory _itemSearchFactory;

	public MySearchProvider(IItemSearchFactory itemSearchFactory)
	{
		_itemSearchFactory = itemSearchFactory;
	}

	public void Run()
	{
		List<Page> results = new List<Page>();

		using (var searchContext = ContentSearchManager.GetIndex("<index name>").CreateSearchContext())
		{
			IQueryable<IPage> queryable = searchContext.GetQueryable<IPage>();

			queryable = _itemSearchFactory.FilteredSearch<IPage>(queryable)
						.Where(item => item.TitleValue == "My Page")
						.OrderBy(item => item.TitleValue);

			results = queryable.ToList();
		}
	}
}