Unified Search
Describes the Unified Search functionality for Optimizely Search & Navigation.
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 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
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.
Unified Search and the query API
Unified Search is generally useful if:
- You build standard search pages that do not require filtering on type-specific properties.
- You build generic functionality but do not know the content types for which it is used.
The regular .NET Client API is better for:
- Most querying scenarios. That is content retrieval, navigations, or listings that do not involve free text search.
- 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.
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 theIClient.Conventions.UnfiedSearchRegistry
property. - Classes for results returned when searching for
ISearchContent:Â UnifiedSearchResults
, which contains severalUnifiedSearchHit
. - 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
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
).
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.
Note
The
GetResult
method modifies search queries by searching for objects that implementISearchContent
and for all types added to theUnifiedSearchRegistry
. TheGetResult
method also adds a projection fromISearchContent
toUnifiedSearchHit
with sensible defaults, along with any type-specific projections added to theUnifiedSearchRegistry
. Finally, before executing a search query (such as theGetResult
method), Unified Search adds type-specific filters that were added to theUnifiedSearchRegistry
.
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.
SearchClient.Instance.UnifiedSearchFor(searchQuery).GetResult()
Use Unified Search
This example shows how to search for Optimizely Content Management System (CMS) content, like pages and uploaded files.
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.
- Create a search query using the
UnifiedSearch
orUnifiedSearchFor
method invoked on theSearchClient
. - Execute the query using
GetResult
. - Do what you want with the result. Typically, you iterate over each hit in a view.
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 don't like that or want to modify it, add a property named SearchSection
to the page type class.
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.
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:
SearchClient.Instance
.UnifiedSearch()
.Filter(x => x.SearchTitle.Prefix("A")
& (!x.MatchTypeHierarchy(typeof(PageData))
| ((PageData)x).CreatedBy.Match("Joel")))
.GetResult();
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 NOT be of type PageData
or if they are, be created by someone named "Joel."
Updated 8 months ago