HomeDev GuideRecipesAPI ReferenceGraphQL
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityDoc feedbackLog In
GitHubNuGetDev CommunityDoc feedback


Unified Search is an integration with Optimizely Content Management System (CMS) that lets you index and query objects of existing classes without having to map them to an intermediate type in the index.

Using Unified Search you can index and query objects of existing classes, such as `PageData` objects. Unified Search indexes objects such that developers can query them in several ways using a fluent API that requires minimal search engine skills. Unified Search is very powerful used with free text search and deterministic querying, such as navigation and listings.

Unified Search provides the benefits of indexing objects using the “dumbed down,” least common denominator approach while maintaining Search & Navigation’s original power of indexing more or less full .NET objects. This approach makes it easier to:

  • Search through different, unrelated types.

  • Build generic functionality for querying and displaying search results, for example helper methods for displaying results.

**Example of Unified Search query**



This sample searches for both `PageData` objects and files in VPPs (`UnifiedFile`). Using a few lines of code, you can include other types in the search. The result object contains hit objects with a title, excerpt (both of which can be highlighted), and other common search result properties.

## Unified Search versus the query API

Unified Search is generally useful if you:

  • Build standard search pages that do not require filtering on type-specific properties.

  • Build generic functionality and do not know the content types for which it will be used.

The regular [.NET Client API](🔗) is better for:

  • Most querying scenarios. That is, content retrieval/navigations/listings that do not involve free text search.

  • Doing fine-grained and type-specific filtering.

## Key concepts

Unified Search uses two key concepts:

  • The type hierarchy is indexed for objects

  • The search engine does not care what type declares a given field (property) as long as it has the expected name and type

For example, you add two classes to the registry (A and B) and use the code below.



This code searches the index for objects that implement `ISearchContent` _or_ are of types A or B. The code filters objects that have a field named `SearchTitle` with a value that starts with “A”.

Objects that implement `ISearchContent` have such a field, but A and B may not. If they do not, the filter does not match, and instances of A and B are not returned.

However, if instances of A and B have such a field, the search engine does not care _why_ they have it. That is, the engine does not care about the fact that A and B do not have `ISearchContent.SearchTitle`. The type filtering is separate, so as long as A and B have a string field named SearchTitle, the search engine can filter on it.

Of course, to include A or B objects in the query result, we must add a SearchTitle field to them. But we do not have to the implement the full `ISearchContent` interface. Also, since the .NET API and the search engine do not distinguish between properties and methods, we can create an extension method for A or B and configure the client’s convention to include it when indexing instances of those types. In this way, we add the `SearchTitle` field without modifying the classes.

## Components

  • A common _interface_, `ISearchContent`, for declaring properties to use when building search (not querying) functionality.

  • An _object_ that maintains a configurable list of types to be searched when searching for `ISearchContent`. Also, optionally, rules for filtering and projecting those types when searching `IUnifiedSearchRegistry`, which is exposed by the `IClient.Conventions.UnfiedSearchRegistry` property.

  • _Classes_ for results returned when searching for `ISearchContent: UnifiedSearchResults`, which contain a number of `UnifiedSearchHit`.

  • Special _methods_ for building and executing search queries: `UnifiedSearch()``UnifiedSearchFor()`, and an overloaded `GetResult()` method.

### ISearchContent

The `ISearchContent` interface resides in the `EPiServer.Find.UnifiedSearch` namespace and is the least common denominator. The interface declares several properties for which you may search, all of whose names begin with _Search_. Classes registered in Unified Search do not have to implement `ISearchContent`. By having a property or extension method with the same name and type as a property in the interface, the property is treated as if it were received from the interface.

  • `SearchTitle`

  • `SearchSummary`

  • `SearchText`

  • `SearchHitUrl`

  • `SearchTypeName`

  • `SearchHitTypeName`

  • `SearchSection`

  • `SearchSubsection`

  • `SearchAuthors`

  • `SearchGeoLocation`

  • `SearchPublishDate`

  • `SearchUpdateDate`

  • `SearchAttachment`

  • `SearchCategories`

  • `SearchFilename`

  • `SearchFileExtension`

  • `SearchMetaData`



### IUnifiedSearchRegistry

`IUnifiedSearchRegistry`, also residing in `EPiServer.Find.UnifiedSearch`, exposes methods for building and configuring types to be included when searching for `ISearchContent`. In addition to methods for adding (the `Add` method) and listing types (the List method), `IUnifiedSearchRegistry` declares methods that let you add rules to filter specific types when searching and projecting found documents to the common hit type (`UnifiedSearchHit`).



### UnifiedSearchResults and UnifiedSearchHit

While `ISearchContent` provides a decent common denominator for fields in which to search, it is not useful for getting back instances of it as the result of a search query. For instance, while you may want to search an indexed object's full text, you typically only want back a text snippet to display in search results. Also, it is technically problematic to get instances of `ISearchContent` back from the search engine, since the matched object does not actually implement that interface, or at least it does not have to.

Therefore, when searching for `ISearchContent` and invoking the `GetResult` method, you do not get back instances of `ISearchContent`. Instead, you get an instance of the `UnifiedSearchResults` class, which contains several `UnifiedSearchHit` objects. A UnifiedSearchHit object contains properties that you would typically want to display for each search result, such as Title, URL and excerpt (text snippet). The `UnifiedSearchHit` object has additional properties, such as `PublishDate`, `ImageUri`, `Section`, `FileExtension`, and `TypeName`.

### Methods for building and executing queries

To search for `ISearchContent`, use the regular Search method, `client.Search()`. When doing so, you determine which fields to search when building free text search. Since `ISearchContent` is a special type that the .NET API knows about, two methods enable you to add sensible defaults: `UnifiedSearch()` and `UnifiedSearchFor()`.

Unified Search also provides a `GetResult` method. Because it has the same name as the regular method for executing search queries, you do not have to do anything special to use the `GetResult` method. Because it has a more specific generic-type constraint than other `GetResult` methods, the compiler uses it.

Note

The `GetResult` method modifies search queries by searching for objects that implement `ISearchContent` and for all types added to the `UnifiedSearchRegistry`. The `GetResult` method also adds a projection from `ISearchContent` to `UnifiedSearchHit` with sensible defaults, along with any type-specific projections added to the `UnifiedSearchRegistry`. Finally, before executing a search query (such as the `GetResult` method), Unified Search adds type-specific filters that were added to the `UnifiedSearchRegistry`.

When you invoke `GetResult`, the search query searches types that may not have implemented `ISearchContent`. But since you (hopefully) specified to only search in, or filter on, a number of fields declared by `ISearchContent`, the search only considers fields with those names, even if the objects do not implement `ISearchContent`. The `GetResult` method has an overload that requires an argument of type `HitSpecification`. This argument lets you control the length of the excerpt, whether to highlight titles and excerpts, and so forth.



## Use Unified Search

This example shows how to search for Optimizely Content Management System (CMS) content like pages and uploaded files.

  1. Create a search query, using either the `UnifiedSearch` or `UnifiedSearchFor` method invoked on the `SearchClient`.

  2. Execute the query using `GetResult`.

  3. Do what you want with the result. Typically, you iterate over each hit in a view.



To customize what is indexed and/or returned for content, either add a property with the same type and name as one of the properties in `ISearchContent` or create and include an extension method matching such a property. For example, the CMS integration includes a default `SearchSection` method to `PageData` objects. If you don’t like that, or want to modify it, add a property named `SearchSection` to the page type class.



The same goes for `SearchText`. The CMS integration includes a default method that you can override by adding your own property.



### Type conditional filtering

You can search for objects that do not implement `ISearchContent` as if they did. If the objects have properties or included methods with matching names as properties declared in `ISearchContent`, they are also returned if you filter by them. Remember that the full object is still stored in the object. This, and the fact you can filter by type using Optimizely Search & Navigation, means you can apply additional criteria to objects of some types using type conditional filtering.

For example, you want to search for everything included as `ISearchContent` (typically `PageData` and `UnifiedFile`) and apply a filter. For `PageData` objects, you want to add additional criteria. You can then do things like this:



In the above code, you first add a filter that requires objects to have a `SearchTitle` field with a value starting with “A”. You then require the objects to either NOT be of type `PageData` _or_ if they are, be created by someone named "Joel".