Using Optimizely Search & Navigation with Ektron
Optimizely Search & Navigation is a powerful and scalable query platform that lets you index and query large amounts of structured or unstructured content. Using Optimizely Search & Navigation, you can create customized website search functionality or build advanced navigation for non-hierarchical content. Other search providers use a crawl to update site content, which can delay the updating of search results. Optimizely Search & Navigation, on the other hand, uses push technology, which means that search results on your website are updated in near real-time.
Optimizely Search & Navigation provides search capabilities for all Ektron content, including HTML content, HTML forms, Smart Forms, assets, and PageBuilder. When using Optimizely Search & Navigation with Ektron, Ektron content and documents are initially pushed to a Find index. from then on, the Find index is updated whenever Ektron content is edited, deleted, or added. For example, if content is scheduled to go live at a future date and time, Optimizely Search & Navigation can retrieve the content after the go-live date and time. Similarly, content scheduled to be archived and removed from site is no longer available in search results as of the scheduled date and time. To query the index and return search results, use Find APIs.
Because Find does not impact existing search capabilities, it can co-exist with your current search provider.
This section explains how to set up Optimizely Search & Navigation as a search provider for your Ektron website.
Prerequisite
Ektron version 9.10 SP1 or higher
Upon creating a developer account, you can use a free development index for testing. This index has a 10,000 document limit, a 90-day lifespan, and a maximum file size of 5 MB.
After completing testing, if you want to move into a production environment, obtain a production index. See http://www.episerver.com/orderfind.
http://find.episerver.com/
.Tip — Save this page with your browser favorites so you can quickly open it in the future.
web.config
file with snippets from the Create Service screen.web.config
snippet within the <configSections>
tag.siteroot
\web.config
file.configSections
tag (</configSections>
).</configSections>
tag.web.config
snippet below the closing </configSections>
tag and above the closing </configuration>
tag.siteroot
\web.config
file, find the closing configuration tag (</configuration>
).http://nuget.episerver.com/feed/packages.svc/
Ektron.cms.find
.Global.asax
file with a reference to bootstrapper file.yoursiteroot\Global.asax
.NOTE: If your siteroot
folder does not contain a Global.asax
file, create one.
void Application_Start(object sender, EventArgs e) { }
{}
), insert the following code.Ektron.Cms.Find.AspNetBootstrapper.EktronFindBootstrapper.Start();
C:\Program Files (x86)\Ektron\EktronWindowsService40
folder. C:\Program Files (x86)\Ektron\EktronWindowsService40\objectFactory.config
file.<add name=”ESyncNotification”> <strategies> </strategies> </add>
<Strategies>
tags, insert the following line.<add name="FindStrategy" type="Ektron.Cms.Find.EsyncNotification.FindStrategy, Ektron.Cms.Find.EsyncNotification"/>
objectFactory.config
file.After installing Optimizely Search & Navigation, push your website content to the Find index.
Prerequisites
- You completed all installation steps. See Installing Optimizely Search & Navigation.
- You are a member of the Ektron Administrators Group.
yoursiteroot/episerverfind/
. The following screen appears.Global.asax
file includes the correct reference to the bootstrapper file. (This step was covered in Installing Optimizely Search & Navigation.)web.config
file was updated with snippets from the Create Service screen. (This step was also covered in Installing Optimizely Search & Navigation.)IMPORTANT: When you edit the bootstrapper file or code that it references, you must restart IIS to push the latest changes to Find. Then, clear the Find index and reindex. These actions prevent mismatches between the Find index and custom types that use it.
Optimizely Search & Navigation returns search results for all searchable content on a published PageBuilder page. Text that appears on such a PageBuilder page is indexed, along with the page's title. For example, if a PageBuilder page includes a collection widget, and each collection item's teaser is displayed, the teaser text is searchable.
Pagelayout pages are indexed. Master layouts are not indexed, but PageBuilder pages based on a master layout are indexed. See also: Creating a PageBuilder master layout.
You may want to suppress certain page elements from search results. For example, you want to exclude navigation menus or footers. The following Knowledge Base article explains how to do this: How to hide an element on a PageBuilder page from showing up in search results.
NOTE: Any text that is not visible to the builtin user is not indexed.
NOTE:
* If you want a Smart Form to be indexed, you must select its Content Searchable checkbox.
* The Indexed checkbox on Smart Form configuration field's dialog has no effect on whether the field’s content is searchable. Any property specified in your custom Smart Form class and populated by serialization logic is searchable.
You can query Smart FormAn Ektron-defined Web page that contains XML (hidden from the end user) to display content, and receive, verify, and save user input.s using the content type Smartform
. For example: SearchResults<Ektron.Cms.Find.Models.SmartForm> results;
If you use this approach, Episerver Find returns Smart Form content as a single string. To extract field data, developers must use a string split technique.
If you want to query data stored in Smart Form fields, use a type mapper to store a custom content type. This type lets you convert each field in Smart Form configuration to a field in a custom object. You can then use Find to query that object using this syntax.
var results = client.Search<customclassname>().Filter(…).GetResult();
For example, if the class created for your Smart Form configuration is OurTeam, you would use this.
var results = client.Search<OurTeam>().Filter(…).GetResult();
To learn how to use a type mapper to store a custom content type, see Converting a Smart Form configuration to a class.
Complete these steps for each Smart Form configuration that you want to convert into a class. After you complete the procedure, Optimizely Search & Navigation indexes registered configurations when a Smart Form is edited or added.
NOTE: To learn about updating Find upon deleting, see Updating Find index upon deletion of a Smart Form.
This Smart Form configuration has the following structure. You use this information in later steps
Root class: Ourteam
|
bin\Debug
folder. For example: C:\Users\jedit\Documents\Visual Studio 2012\Projects\MySmartFormClassLibrary\MySmartFormClassLibrary\bin\Debug.
The file name matches the Smart Form configuration name.
findbootstrapper.cs
file, located in C:\Users\user name\Documents\Visual Studio version\Projects\yoursitename\packages\Ektron.Cms.Find.1.0.0\content\App_Code\CSCode\
.Find these comments.
// Register type mappings below to translate Smart Forms // to custom models. // Example: // ektronMessaging.Types.Register(content => new MySmartFormModel { ... }) // .For(content => content.XmlConfiguration.Id == ...);
ektronMessaging.Types.Register(SmartForm configuration name.Create) .For(SmartForm configuration name.CanCreate);
For example:
ektronMessaging.Types.Register(OurTeam.Create).For(OurTeam.CanCreate);
NOTE:
* The Ektron Messaging class’s Types
property configures how Ektron indexes content.
* Register
converts Ektron content data to an object that Optimizely Search & Navigation can query.
* As an argument, the Register
method takes a delegate, which receives content data and returns an object. This object is pushed to the Find index.
* The .For
statement enables you to map only content data that satisfy a criterion. .For
takes a delegate, which returns a Boolean. Content is only converted to the registered class if Ektron content data matches this criterion. Without a .For
statement, the type mapper applies to all content passed through the system.
* The CanCreate
method is defined in your configuration's .cs file. See step 10 later in this section.
namespace MySmartFormClassLibrary { public class Image { public img img { get; set; } } public class EmployeeProfile { public string Name { get; set; } public string Title { get; set; } public string Summary { get; set; } public string LinkedIn { get; set; } public string Twitter { get; set; } public string Bio { get; set; } public Image Image { get; set; } }
OurTeam
configuration.[XmlRoot("root")] public class OurTeam {
public long Id { get; set; } public int LanguageId { get; set; } public string Title { get; set; }
IMPORTANT: Repeat steps 8 through 10 for each subclass directly below /root/
in your Smart Form configuration.
public EmployeeProfile EmployeeProfile { get; set; }
NOTE: Ektron Smart Forms store rich text HTML as valid XML. This code deserializes rich text HTML into an encoded XML string, which Find can consume.
public static OurTeam Create(ContentData content) { XmlSerializer serializer = new XmlSerializer(typeof(OurTeam)); OurTeam oTeam = (OurTeam)serializer.Deserialize(new StringReader (content.Html)); oTeam.Id = content.Id; oTeam.LanguageId = content.LanguageId; oTeam.Title = content.Title; return oTeam; }
CanCreate
method, which is referenced in the findbootstrapper.cs
file's registration method, which returns the config ID for registering the type (see step 4 above). public static bool CanCreate(ContentData content) { return content.XmlConfiguration.Id == 8; }
Here is the complete sample file, OurTeam.cs.
using Ektron.Cms; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; namespace MySmartFormClassLibrary { public class Image { public img img { get; set; } } public class EmployeeProfile { public string Name { get; set; } public string Title { get; set; } public string Summary { get; set; } public string LinkedIn { get; set; } public string Twitter { get; set; } public string Bio { get; set; } public Image Image { get; set; } } [XmlRoot("root")] public class OurTeam { public long Id { get; set; } public int LanguageId { get; set; } public string Title { get; set; } public EmployeeProfile EmployeeProfile { get; set; } public static OurTeam Create(ContentData content) { XmlSerializer serializer = new XmlSerializer(typeof(OurTeam)); OurTeam oTeam = (OurTeam)serializer.Deserialize(new StringReader(content.Html)); oTeam.Id = content.Id; oTeam.LanguageId = content.LanguageId; oTeam.Title = content.Title; return oTeam; } public static bool CanCreate(ContentData content) { return content.XmlConfiguration.Id == 8; } } }
If you delete a Smart Form configuration, its content remains in the Find index. To remove the custom type entries of a Smart Form from the index, remove the type mapper by not registering it. Then, clear the index and reindex. These actions index the Smart Form contents as the default Smart Form type.
Alternatively, you can delete individual documents from the Find index using the following article: http://world.episerver.com/documentation/Items/Developers-Guide/Episerver-Find/9/DotNET-Client-API/Deleting-documents/.
When you create a Smart Form configuration, no indexing occurs because there is nothing to index at this point. If Smart Form content is published before the configuration is converted to a class, it is indexed as HTML content. If you later convert the configuration to a class, you should clear the index and reindex your site to make Smart Form content available to Optimizely Search & Navigation.
Similarly, if you modify a class, you should clear the index and reindex your site.
Taxonomy and category information is updated in the Find index whenever any of these events occurs.
Find divides taxonomy paths into individual taxonomies, indexes each category, and returns the following string-based properties.
For example:
Root taxonomy
"Id$$string":"154", "Path$$string":"\Food", "IdPath$$string":"\154",
Child taxonomy
"Id$$string":"155" "Path$$string":"\Food\Chinese" "IdPath$$string":\154\155
IMPORTANT: All properties are stored as strings.
When a search query uses a taxonomy filter, the results include content assigned to child categories even if the query only specifies the root taxonomy. Similarly, if a query specifies a child category, parent taxonomy categories are available, so users can find content by searching any category in the tree.
Consider the following example.
\Food
\Food\Chinese
Search query 1
IClient client = Client.CreateFromConfig(); //A client retrieved from config or injected into the method
client.Search<Ektron.Cms.Find.Models.Content>()
.Filter<Ektron.Cms.Find.Models.Content>(c =>
c.Categories.MatchContained<TaxonomyCategory>(tax => tax.Path, @"\Food"))
.GetResult();
When executed, this code returns content assigned to the Food
and Chinese
categories. So, content IDs 68 and 231 are returned even though 231 is not assigned to the Food
category.
Search query 2
client.Search<Ektron.Cms.Find.Models.Content>()
.Filter<Ektron.Cms.Find.Models.Content>(c =>
c.Categories.MatchContained<TaxonomyCategory>(tax => tax.Path, @"\Food\Chinese"))
.GetResult();
When executed, this code returns content assigned to the Chinese
and Food
categories. So, content IDs 231 and 68 are returned.
Episerver Find can retrieve search results from assets as it does with HTML content.
NOTE: Indexed files are sent base 64 encoded, which increases their size by roughly 1/3.
If you want to query data stored in Ektron metadataInformation about a content item, such as its title and language. Some types of metadata are
[Searchable.] Can be found by the search.
[Meta tag.] Resides in a content’s source code, helping search engines find it.
[HTML tag.] Information about content used by a Web browser.
[Content tags.] Keywords that can be assigned to content and library items.
* Simple Dublin Core. 15 standard fields that cover useful information about content., use a type mapper to store a custom content type. This type lets you convert selected metadata definitions to a field in a custom object. You then can use Optimizely Search & Navigation to query that object using this syntax.
var results = client.Search<custommetadataclassname>().Filter(…).GetResult();
For example, if the class created for your metadata definition is UnstructuredContentModelWithMetadata
, you would use the following code.
var results = client.Search<UnstructuredContentModelWithMetadata>().Filter(…).GetResult();
To convert a metadata definition to a class, use the following steps.
Prerequisite
Your Ektron CMS has metadata definitions that are applied to content and to which values are entered. The definitions must be of the Searchable Property type, publicly viewable, and active. See also Metadata types.
public class UnstructuredContentModelWithMetadata : UnstructuredContent { public string SearchTextMetadata { get; set; } public List<string> SearchSelectionMetadata { get; set; } public DateTime SearchDateMetadata { get; set; } public int SearchNumberMetadata { get; set; public string mymeta { get; set; } public UnstructuredContentModelWithMetadata() { SearchSelectionMetadata = new List<string>(); } }
This example illustrates the following metadata definitions and their types.
EktronFindBootstrapper
file, located in siteroot\App_Code\CSCode\
. Enter the register statement below applicationBuilder.App.Start();
as shown in the following example:ektronMessaging.Types.Register(CreateMyUnstructuredContentModelWithMetadata) .For(content => (content.XmlConfiguration.Id == 0 && (content.SubType == Common.EkEnumeration.CMSContentSubtype.Content && content.Type == 1) ));
Register
specifies how to convert Ektron ContentData to an instance of the new type created in Step 1. It accepts a condition that selects eligible CMS content to be indexed as this type. The CreateMyUnstructuredContentModelWithMetadata
method is described in step 3..For
is a filter that determines which Ektron ContentData can be mapped. In this example, only HTML content is eligible.ContentData
object and converts it to a type defined in step 1. This type is indexed by Find for content matching the .For
filter condition specified in step 2 . You define the function (CreateMyUnstructuredContentModelWithMetadata
in this example) under your site's AppCode
directory or in a separate class library/dll.
In the following example, the function populates UnstructuredContentModelWithMetadata
with Ektron metadata for the Find index.
private static UnstructuredContentModelWithMetadata CreateMyUnstructuredContentModelWithMetadata( ContentData data) { UnstructuredContentModelWithMetadata contentWithMeta = new UnstructuredContentModelWithMetadata(); if (data.MetaData == null || data.MetaData.Length <= 0) return contentWithMeta; IEnumerable<ContentMetaData> eligibleMetadata = GetMetadataForContent(data.MetaData.ToList(), data.FolderId, data.LanguageId); \\ See note #1 below ContentMetaData cm = eligibleMetadata.ToList().Find(m => m.Name == "SearchText"); if (cm != null) { string metaValue = (string)cm.Text; if (!String.IsNullOrWhiteSpace(metaValue)) { contentWithMeta.SearchTextMetadata = metaValue; } } \\ See note #2 below cm = eligibleMetadata.ToList().Find(m => m.Name == "SearchNumber"); if (cm != null) { int value; if (Int32.TryParse(cm.Text, out value)) contentWithMeta.SearchNumberMetadata = value; } \\ See note #3 below cm = eligibleMetadata.ToList().Find(m => m.Name == "SearchDate"); if (cm != null) { DateTime mydate; if (DateTime.TryParse(cm.Text, out mydate)) contentWithMeta.SearchDateMetadata = mydate; } \\ See note #4 below cm = eligibleMetadata.ToList().Find(m => m.Name == "SearchSelection"); if (cm != null) { string metaValue = (string)cm.Text; if (!String.IsNullOrWhiteSpace(metaValue)) { string[] metaValues = metaValue.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); if (metaValues != null && metaValues.Length > 0) { contentWithMeta.SearchSelectionMetadata = metaValues.ToList(); } } }
Notes on the above sample
SearchText
metadata from ContentData
. This type is a string, as defined in the class and the Ektron metadata definition. If this metadata exists, apply it to the SearchTextMetadata
property of your class.ContentMetaData cm = eligibleMetadata.ToList().Find(m => m.Name == "SearchText"); if (cm != null) { string metaValue = (string)cm.Text; if (!String.IsNullOrWhiteSpace(metaValue)) { contentWithMeta.SearchTextMetadata = metaValue; } }
SearchNumber
metadata is an integer in the class and the Ektron metadata definition.cm = eligibleMetadata.ToList().Find(m => m.Name == "SearchNumber"); if (cm != null) { int value; if (Int32.TryParse(cm.Text, out value)) contentWithMeta.SearchNumberMetadata = value; }
SearchDate
metadata is a DateTime object in the class and the Ektron metadata definition.cm = eligibleMetadata.ToList().Find(m => m.Name == "SearchDate"); if (cm != null) { DateTime mydate; if (DateTime.TryParse(cm.Text, out mydate)) contentWithMeta.SearchDateMetadata = mydate; }
SearchSelection
metadata is a list of strings (List<string>
) in the class, and multiple selections in the Ektron metadata definition. Ektron stores values as a single string, and each value is separated by a delimiter (in this case, a semicolon (;)). For example, if metadata definition stores transportation methods, its value might look like this: train;bus;automobile
.
cm = eligibleMetadata.ToList().Find(m => m.Name == "SearchSelection");
if (cm != null)
{
string metaValue = (string)cm.Text;
if (!String.IsNullOrWhiteSpace(metaValue))
{
string[] metaValues = metaValue.Split(new string[] { ";" },
StringSplitOptions.RemoveEmptyEntries);
if (metaValues != null && metaValues.Length > 0)
{
contentWithMeta.SearchSelectionMetadata = metaValues.ToList();
}
}
}
The string[ ]
line separates the multiple selection string into individual values that Find can query.
Content that has expired and is set to Archive and remove from site or Archive and remain on site is not automatically removed from the index. Such content is removed upon the next full reindexing. Until then, to exclude expired content from search results, use the Find Client API's EndDate
and EndDateAction
properties to filter results.
NOTE: Content that has expired but is set to Add to CMS Refresh reports is considered live content, so remains in the Find index.
Usage of the Find Service and API is outside the scope of this document.
Use the Ektron.Cms.Find.Models
namespace to query and filter Ektron content. A search page's .cs file typically contains a Searchresults
command like this:
SearchResults<Ektron.Cms.Find.Models.Content> results;
The namespace contains these classes.
Content
. Returns Ektron content types. See Fields that you can query and filter using the content class.PageBuilder
. Returns PageBuilder content. It inherits all properties from the content
class.UnstructuredContent
. Returns HTML content. It inherits properties from the content
class.SmartForm
. Returns content from Smart Forms, as explained in Converting a Smart Form configuration to a class. It inherits all properties from the content
class.asset
. Returns content from assetAn external file, such as a Microsoft Word document or image, stored in one of these Ektron siteroot folders: assets, privateassets, uploadedfiles and uploadedimages.
An asset can be managed like native Ektron content.s. You can filter them using their summary or path. It inherits properties from the content
class.Taxonomy
. Returns content assigned to selected categories. It inherits properties from the content
class.namespace Ektron.Cms.Find.Models { public class Content : IHaveReferences { public Content(); public string Body { get; set; } public IEnumerable<TaxonomyCategory> Categories { get; set; } public DateTime CreationDate { get; set; } public Author Editor { get; set; } public Folder Folder { get; set; } public long Id { get; set; } public int Language { get; set; } public DateTime ModificationDate { get; set; } public string Quicklink { get; set; } public DocumentReference Reference { get; set; } public string Summary { get; set; } public string Title { get; set; } public DateTime EndDate { get; set; } public EndDateAction EndDateAction { get; set; } } }
If you want the Find index to be updated whenever eSync moves content changes between servers, follow Step 7 in the installation instructions.
It takes longer for changes triggered by eSync to appear in search results than other content changes. This is because, after an eSync is completed, a full reindex is launched.
Optimizely Search & Navigation provides extensive capabilities for monitoring search activity and customizing search functionality on your website. See the following related information.