---
title: Unified Search
slug: cms-12/docs/unified-search
docTags: 
createdAt: 2026-08-10T15:55:57.000Z
---

Unified Search is an integration with Optimizely Content Management System (CMS) that allows you to index and query objects of existing classes, such as `PageData` objects, without needing to map them to an intermediate type in the index.

Unified Search indexes objects so developers can query them in several ways using a fluent API requiring minimal search engine skills. Use Unified Search 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, such as helper methods for displaying results.

**Example of Unified Search query**

```csharp
var result = SearchClient.Instance
  .UnifiedSearchFor("some search term")
  .GetResult();
```

This sample searches for `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 (you can highlight both), and other common search result properties.

## Compare Unified Search and the query API

| Unified Search is useful for...                                                            | .NET Client API is better for...                                                                                   |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| You build standard search pages that do not require filtering on type-specific properties. | Most querying scenarios. That is content retrieval, navigations, or listings that do not involve free text search. |
| You build generic functionality but do not know the content types for which it is used.    | Doing fine-grained and type-specific filtering.                                                                    |

## Key concepts

Unified Search uses two key concepts:

- Indexes the type hierarchy 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.

```csharp
SearchClient.Instance
  .UnifiedSearch()
  .Filter(x => x.SearchTitle.Prefix("A"))
  .GetResult();
```

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 does not return instances of A and B.

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, you must add a SearchTitle field to them. But you do not have to implement the full `ISearchContent` interface. Also, because the .NET API and the search engine do not distinguish between properties and methods, you 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, you 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 search 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 contains several`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 you may search, whose names begin with *Search*. Classes registered in Unified Search do not have to implement `ISearchContent`. A property or extension method with the same name and type as a property in the interface treats the property as if the interface receives it.

- `SearchTitle`
- `SearchSummary`
- `SearchText`
- `SearchHitUrl`
- `SearchTypeName`
- `SearchHitTypeName`
- `SearchSection`
- `SearchSubsection`
- `SearchAuthors`
- `SearchGeoLocation`
- `SearchPublishDate`
- `SearchUpdateDate`
- `SearchAttachment`
- `SearchCategories`
- `SearchFilename`
- `SearchFileExtension`
- `SearchMetaData`

```csharp
public class MySearchableClass {
  public string Heading {
    get;
    set;
  }
  public string Body {
    get;
    set;
  }
  public string Url {
    get;
    set;
  }

  public virtual string SearchTitle {
    get {
      return Heading;
    }
  }
  public virtual string SearchSummary {
    get {
      return Body;
    }
  }
  public virtual string SearchText {
    get {
      return String.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", Heading, Body, Url);
    }
  }
  public virtual string SearchHitUrl {
    get {
      return Url;
    }
  }
  public virtual string SearchSection {
    get {
      return GetType().Name;
    }
  }
}
```

### IUnifiedSearchRegistry

`IUnifiedSearchRegistry`, also residing in `EPiServer.Find.UnifiedSearch` exposes methods for building and configuring types to include 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`).

```csharp
SearchClient.Instance.Conventions.UnifiedSearchRegistry
.Add<MySearchableClass>();
```

### 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 a text snippet to display in search results. Also, it is technically problematic to get instances of `ISearchContent` back from the search engine because the matched object does not implement that interface, or at least it does not have to.

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 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 a free text search. Because `ISearchContent` is a special type that the .NET API knows about; two methods let you 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.

:::hint{type="info"}
**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 because you (hopefully) specified to only search in, or filter on, several 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.

```csharp
SearchClient.Instance.UnifiedSearchFor(searchQuery).GetResult()
```

## Search for content with Unified Search

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

:::hint{type="info"}
**Note**
By default, Unified Search does not return a container page or content folder. To change this behavior, create your own implementation of `IUnifiedSearchSetUp` interface.
:::

1. Create a search query using 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.

```csharp
using EPiServer.Find;
using EPiServer.Find.Framework;
using EPiServer.Find.Cms;
using EPiServer.Find.UnifiedSearch;

var result = SearchClient.Instance
  .UnifiedSearchFor(Query)
  .GetResult();

foreach(UnifiedSearchHit hit in result) {
  Response.Write(hit.Url);
  Response.Write(hit.Title);
  Response.Write(hit.Excerpt);
}
```

To customize what is indexed and returned for content, 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 do not like that or want to modify it, add a property named `SearchSection` to the page type class.

```csharp
public abstract class SitePageData: PageData {
  [Ignore]
  public virtual string SearchSection {
    get {
      var section = this.SearchSection();
      if (!string.IsNullOrWhiteSpace(section)) {
        return section;
      }

      if (ParentLink.CompareToIgnoreWorkID(ContentReference.StartPage)) {
        return PageName;
      }
      return null;
    }
    //Other properties
  }
}
```

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

```csharp
public class NewsPage: StandardPage {
  [Ignore]
  public string SearchText {
    get {
      return MainBody.ToHtmlString(PrincipalInfo.AnonymousPrincipal);
    }
  }
  //Other properties
}
```

## 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 object still stores the full object, which means you can apply additional criteria to objects of some types using type conditional filtering, plus you can filter by type using Optimizely Search & Navigation,

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:

```csharp
SearchClient.Instance
  .UnifiedSearch()
  .Filter(x => x.SearchTitle.Prefix("A")
            & (!x.MatchTypeHierarchy(typeof(PageData))
            | ((PageData)x).CreatedBy.Match("Joel")))
  .GetResult();
```

In the code above, 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 NOT be of type `PageData` *or* if they are, be created by someone named "Joel."

## Customize default projections

You can customize the projection of Unified Search results in several ways. The `UnifiedSearchRegistry` supports the options described in the following, see [Unified Search](docId\:Zk9jo_BnvtkTuJ6yH4qiH).

### ProjectTitleFrom

To register the `Title` for a specific document type.

```csharp
client.Conventions.UnifiedSearchRegistry
  .ForInstanceOf<MySearchableClass>()
  .ProjectTitleFrom(x => x.MyTitleProperty);
```

### ProjectTypeNameFrom

To register the `TypeName` for a specific document type.

```csharp
client.Conventions.UnifiedSearchRegistry
  .ForInstanceOf<MySearchableClass>()
  .ProjectTypeNameFrom(x => x.MyTypeNameProperty);
```

### ProjectUrlFrom

To register the `URL` for a specific document type.

```csharp
client.Conventions.UnifiedSearchRegistry
  .ForInstanceOf<MySearchableClass>()
  .ProjectUrlFrom(x => x.MyUrlProperty);
```

### ProjectImageUriFrom

To register the `ImageUri` for a specific document type.

```csharp
client.Conventions.UnifiedSearchRegistry
  .ForInstanceOf<MySearchableClass>()
  .ProjectImageUriFrom(x => x.MyImageUriProperty);
```

### UseHitType

To register the type of the result item for a specific document type.

```css
client.Conventions.UnifiedSearchRegistry
  .ForInstanceOf<MySearchableClass>()
  .UseHitType<MySearchableHitClass>();
```

### ProjectHighlightedExcerptUsing

To register the highlighted excerpt for a specific document type.

```csharp
client.Conventions.UnifiedSearchRegistry
  .ForInstanceOf<MySearchableClass>()
  .ProjectHighlightedExcerptUsing<ISearchContent>(spec =>
    x => x.MyHighlightedProperty.AsHighlighted(new HighlightSpec 
      { 
        FragmentSize = spec.ExcerptLength, NumberOfFragments = 1 
      }));
```

### AlwaysApplyFilter

Register a filter that is always applied to Unified Search.

```csharp
client.Conventions.UnifiedSearchRegistry
  .ForInstanceOf<MySearchableClass>()
  .AlwaysApplyFilter(f => f.BuildFilter<MyType>().And(x => !x.MyProperty.Match("Something")));
```

### PublicSearchFilter

Register a filter that is applied for public Unified Search.

```csharp
client.Conventions.UnifiedSearchRegistry
  .ForInstanceOf<MySearchableClass>()
  .PublicSearchFilter(f => f.BuildFilter<MySearchableClass>().And(x => !x.MyProperty.Match("Something")));
```

### CustomizeProjection

If the other projection options are inadequate, the `CustomizeProjection` extension provides full access to the `HitProjectionBuilder`.

### Highlight and encode unified search results

For unified search results, Optimizely Search & Navigation enables HTML encoding by default for the `title` and `excerpt` field. Optimizely Search & Navigation encodes text before adding the HTML tags surrounding highlighted content.

```csharp
var hitSpec = new HitSpecification
  {
    HighlightExcerpt = true
    // HighlightTitle = false by default,
    // EncodeExcerpt = true by default,
    // EncodeTitle = true by default
  };

var result = searchClient.UnifiedSearchFor("apples").GetResult(hitSpec);
// result.Hits.First().Document.Excerpt contains I like <em>apples</em>.
```

### Disable HTML encoding

To turn off HTML encoding, set the `EncodeTitle` and `EncodeExcerpt` properties of the `HitSpecification` to "false." If you do, ensure that the implementation handles the encoding of results to ensure normal rendering and avoid script injections. You may also implement custom highlighting.

```csharp
var hitSpec = new HitSpecification
  {
    HighlightExcerpt = true,
    EncodeTitle = false,
    EncodeExcerpt = false
  };

var result = searchClient.UnifiedSearchFor("apples").GetResult(hitSpec);
// result.Hits.First().Document.Excerpt contains I like <em>apples</em>.
```
