By using my Skybrud.Umbraco.GridData package (which introduces a strongly typed model for the Grid), indexing the new Grid in Umbraco 7.2 is quite easy.
At Skybrud.dk we typically create a class named ExamineIndexer
that takes care of the Examine related events. This class should be initalized during Umbraco startup like this:
using Umbraco.Core;
namespace FanoeTest {
public class Startup : ApplicationEventHandler {
private static ExamineIndexer _examineIndexer;
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) {
// Register events for Examine
_examineIndexer = new ExamineIndexer();
}
}
}
The ExamineIndexer
class it self will now look like the example below. The example is very specific, since only certain document types where we know that the content
property holds a Grid value are handled.
The example could be modified to use the Content Service for finding all properties that holds a Grid value. But since the Content Service uses the database, it may slow the performance when building your index.
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Examine;
using Examine.Providers;
using Skybrud.Umbraco.GridData;
using Skybrud.Umbraco.GridData.Values;
using Umbraco.Core.Logging;
namespace FanoeTest {
public class ExamineIndexer {
public ExamineIndexer() {
BaseIndexProvider externalIndexer = ExamineManager.Instance.IndexProviderCollection["ExternalIndexer"];
externalIndexer.GatheringNodeData += OnExamineGatheringNodeData;
}
private void OnExamineGatheringNodeData(object sender, IndexingNodeDataEventArgs e) {
try {
string nodeTypeAlias = e.Fields["nodeTypeAlias"];
LogHelper.Info<ExamineIndexer>("Gathering node data for node #" + e.NodeId + " (type: " + nodeTypeAlias + ")");
if (nodeTypeAlias == "Home" || nodeTypeAlias == "LandingPage" || nodeTypeAlias == "TextPage" || nodeTypeAlias == "BlogPost") {
string value;
if (e.Fields.TryGetValue("content", out value)) {
LogHelper.Info<ExamineIndexer>("Node has \"content\" value\"");
GridDataModel grid = GridDataModel.Deserialize(e.Fields["content"]);
StringBuilder combined = new StringBuilder();
foreach (GridControl ctrl in grid.GetAllControls()) {
switch (ctrl.Editor.Alias) {
case "rte": {
// Get the HTML value
string html = ctrl.GetValue<GridControlRichTextValue>().Value;
// Strip any HTML tags so we only have text
string text = Regex.Replace(html, "<.*?>", "");
// Extra decoding may be necessary
text = HttpUtility.HtmlDecode(text);
// Now append the text
combined.AppendLine(text);
break;
}
case "media": {
GridControlMediaValue media = ctrl.GetValue<GridControlMediaValue>();
combined.AppendLine(media.Caption);
break;
}
case "headline":
case "quote": {
combined.AppendLine(ctrl.GetValue<GridControlTextValue>().Value);
break;
}
}
}
e.Fields["content"] = combined.ToString();
} else {
LogHelper.Info<ExamineIndexer>("Node has no \"content\" value\"");
}
}
} catch (Exception ex) {
LogHelper.Error<ExamineIndexer>("MAYDAY! MAYDAY! MAYDAY!", ex);
}
}
}
}
The trick is to split the controls of the grid into different fields depending on the type of the control.
In the example below, values of
rte
andheadline
are added to thecontentFocus
field, while remaining values are added to thecontentNormal
field.Then your search query could then look something like
contentFocus:"hest"^2 contentNormal:"hest"
. ThecontentFocus
field will then have twice the importance of thecontentNormal
field.That should do the job ;)