Skip to content

Instantly share code, notes, and snippets.

@abjerner
Created December 23, 2014 14:42
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save abjerner/bdd89e0788d274ec5a33 to your computer and use it in GitHub Desktop.
Save abjerner/bdd89e0788d274ec5a33 to your computer and use it in GitHub Desktop.

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);
            
            }

        }

    }

}
@pbne04
Copy link

pbne04 commented Dec 2, 2015

Hello,

I am trying to index my grid data, but after updating to Umbraco 7.3.3 (from 7.2.2), I am getting some exceptions when using GridDataModel.Deserialize(e.Fields["myGrid"])

The error + stacktrace is as follows:

Object reference not set to an instance of an object.
   at Skybrud.Umbraco.GridData.Howdy.ReplaceEditorObjectFromConfig(GridControl control)
   at Skybrud.Umbraco.GridData.GridControl.Parse(GridArea area, JObject obj)
   at Skybrud.Umbraco.GridData.GridArea.<>c__DisplayClass4.<Parse>b__2(JObject x)
   at Skybrud.Umbraco.GridData.Extensions.Json.JObjectExtensionMethods.<>c__DisplayClass1`1.<GetArray>b__0(JObject child)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Skybrud.Umbraco.GridData.Extensions.Json.JObjectExtensionMethods.GetArray[T](JObject obj, String propertyName, Func`2 func)
   at Skybrud.Umbraco.GridData.GridArea.Parse(GridRow row, JObject obj)
   at Skybrud.Umbraco.GridData.GridRow.<>c__DisplayClass14.<Parse>b__13(JObject x)
   at Skybrud.Umbraco.GridData.Extensions.Json.JObjectExtensionMethods.<>c__DisplayClass1`1.<GetArray>b__0(JObject child)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Skybrud.Umbraco.GridData.Extensions.Json.JObjectExtensionMethods.GetArray[T](JObject obj, String propertyName, Func`2 func)
   at Skybrud.Umbraco.GridData.GridRow.Parse(GridSection section, JObject obj)
   at Skybrud.Umbraco.GridData.GridSection.<>c__DisplayClass2.<Parse>b__1(JObject x)
   at Skybrud.Umbraco.GridData.Extensions.Json.JObjectExtensionMethods.<>c__DisplayClass1`1.<GetArray>b__0(JObject child)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Skybrud.Umbraco.GridData.Extensions.Json.JObjectExtensionMethods.GetArray[T](JObject obj, String propertyName, Func`2 func)
   at Skybrud.Umbraco.GridData.GridSection.Parse(GridDataModel model, JObject obj)
   at Skybrud.Umbraco.GridData.GridDataModel.<>c__DisplayClass2a.<Deserialize>b__29(JObject x)
   at Skybrud.Umbraco.GridData.Extensions.Json.JObjectExtensionMethods.<>c__DisplayClass1`1.<GetArray>b__0(JObject child)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Skybrud.Umbraco.GridData.Extensions.Json.JObjectExtensionMethods.GetArray[T](JObject obj, String propertyName, Func`2 func)
   at Skybrud.Umbraco.GridData.GridDataModel.Deserialize(String json)

The griddata which produced this error is the following

"{\r\n  \"name\": \"1 column layout\",\r\n  \"sections\": [\r\n    {\r\n      \"grid\": 12,\r\n      \"rows\": [\r\n        {\r\n          \"name\": \"Headline\",\r\n          \"areas\": [\r\n            {\r\n              \"grid\": 12,\r\n              \"editors\": [\r\n                \"headline\"\r\n              ],\r\n              \"controls\": [\r\n                {\r\n                  \"value\": \"<p>test headline</p>\",\r\n                  \"editor\": {\r\n                    \"name\": \"Rich text editor\",\r\n                    \"alias\": \"rte\",\r\n                    \"view\": \"rte\",\r\n                    \"icon\": \"icon-article\"\r\n                  }\r\n                }\r\n              ]\r\n            }\r\n          ],\r\n          \"id\": \"782155a3-775e-640e-5998-de9f8d677cc6\"\r\n        },\r\n        {\r\n          \"name\": \"Content\",\r\n          \"areas\": [\r\n            {\r\n              \"grid\": 12,\r\n              \"allowAll\": true,\r\n              \"controls\":
 [\r\n                {\r\n                  \"value\": \"<p>test content</p>\",\r\n                  \"editor\": {\r\n                    \"name\": \"Rich text editor\",\r\n                    \"alias\": \"rte\",\r\n                    \"view\": \"rte\",\r\n                    \"icon\": \"icon-article\"\r\n                  }\r\n                }\r\n              ]\r\n            }\r\n          ],\r\n          \"id\": \"0d570fba-c971-499b-32c6-80cf6bbc8c6b\"\r\n        }\r\n      ]\r\n    }\r\n  ]\r\n}"

Do you have a suggestion as to what might have gone wrong here?

Regards Peter

EDIT:
I downloaded the source code, built the dll and used it in my project - works like a charm. I guess maybe the Nuget package is not quite up to date?

EDIT#2:
Looks like the package has now been updated, https://github.com/skybrud/Skybrud.Umbraco.GridData/releases/tag/v1.5.1

@darrenst
Copy link

darrenst commented May 6, 2016

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:

                            case "paragraph":
                            case "headline_centered":

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.

@naepalm
Copy link

naepalm commented Dec 2, 2016

Just want to say I used this in 7.5.4 and it works like a charm. Thanks so much @abjerner :)

@slseverance
Copy link

I'm having an issue with the basic installation. I read the instruction on the grid.skybrud.dk site that says simply drop the dlls in the bin folder. So I have downloaded the ZIP and pulled the xmls and dlls out of the latest release folder and dropped them into the bin folder. I restarted the app pool. It's not working so I assume the installation is more complicated than that? I have built a template using your example code and my aliases so i feel confident I have simply not installed it correctly. Must this be built and recompiled before operation or will a simple restart of service pick up the new packages? I don't have a viz studio solution set up. Thank you.

@zipswich
Copy link

Thank you for the example. I have largely copied the code, and made sure ExamineIndexer() is called. Unfortunately, OnExamineGatheringNodeData is never invoked. Could you provide a tip on how to debug this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment