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);
}
}
}
}
I have manged to get this going. Thought I'd share my notes for newbee's as the instructions need a degree of modification for your own websites.
Create a new class that inherits from ApplicationEventHandler
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { // Register events for Examine <NamespaceToClass>.ExamineIndexer _examineIndexer = new ExamineIndexer(); Log.Info("Examine SkyBrid Indexer is starting"); }
This will run a class ExamineIndexer() on startup.
Next Create your ExamineIndexer() class
Copy the example above but amend to account for your property names.
In the ExamineIndexer Class the example uses the externalIndexer but amend if you are rolling your own as I am (MySearchIndexer).
BaseIndexProvider externalIndexer = ExamineManager.Instance.IndexProviderCollection["MySearchIndexer"];
In the OnExamineGatheringNodeData method only include the Document Type ALIAS names you want to search
if (nodeTypeAlias == "Landing" || nodeTypeAlias == "StandardPage")
You only want to include document types that actually have the grid property included
Next amend the if statement to include the name of the grid property alias. Mine is called "BodyGrid"
if (e.Fields.TryGetValue("bodyGrid", out value))
is also here on the deserialize method
GridDataModel grid = GridDataModel.Deserialize(e.Fields["bodyGrid"]);
next the method is looking for Grid editor alias matching. If you have changed or added new editors you will need to include in the case statement else it won't be included in the index. The additions i made were:
These values are rolled up into the existing field "bodyGrid"
e.Fields["bodyGrid"] = combined.ToString();
OK that's it Build and test
NOTES:
Ensure your custom indexer is actually indexing your customer property (bodyGrid)
In your Search View ensure your search criteria is selecting your grid property. This is the code that handles my search page:
var Searcher = Examine.ExamineManager.Instance.SearchProviderCollection["MySearchSearcher"];
var SearchCriteria = Searcher.CreateSearchCriteria(Examine.SearchCriteria.BooleanOperation.Or);
var Query = SearchCriteria.Field("heroTitle", searchTerm.Boost(3)) .Or() .Field("title", searchTerm.Fuzzy().Value.Boost(2)) .Or() .Field("subtitle", searchTerm.Fuzzy().Value.Boost(1)) .Or() .Field("bodyGrid", searchTerm.Fuzzy()) ;
That should do it.