Indexing blocklist data in Umbraco 9
Published: 2022-03-14 20:16:46

Here is a short example on how to create a new index field and then index specific data to that field from each blocktype.

## Setup

As a test site I've started a fresh v9 site with the [Portfolio starter kit package](https://our.umbraco.com/packages/starter-kits/portfolio-starter-kit/).

The site is not that important, it is just to have some starter content.

Next we create a new indexer and hook it up in the startup.cs class:

BlocklistIndexer.cs:
```csharp
using Examine;
using System;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using static Umbraco.Cms.Core.Constants;

namespace BlocklistIndexing.Indexing
{
    public class BlocklistIndexer : INotificationHandler<UmbracoApplicationStartingNotification>
    {
        private readonly IExamineManager _examineManager;

        public BlocklistIndexer(IExamineManager examineManager)
        {
            _examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager));
        }

        public void Handle(UmbracoApplicationStartingNotification notification)
        {
            if (!_examineManager.TryGetIndex(UmbracoIndexes.ExternalIndexName, out IIndex index))
                throw new InvalidOperationException($"No index found by name {UmbracoIndexes.ExternalIndexName}");

            if (index is not BaseIndexProvider indexProvider)
                throw new InvalidOperationException("Could not cast");

            indexProvider.TransformingIndexValues += IndexProvider_TransformingIndexValues;

        }

        private void IndexProvider_TransformingIndexValues(object sender, IndexingItemEventArgs e)
        {
            // TODO
        }
    }
}
```

startup.cs:
```csharp
services.AddUmbraco(_env, _config)
    .AddBackOffice()
    .AddWebsite()
    .AddComposers()
    .AddNotificationHandler<UmbracoApplicationStartingNotification, BlocklistIndexer>()
    .Build();
```

This adds a new BlockListIndexer which runs whenever one or more fields in the index is updated. We also hook it up to the configure services in startup.cs on the ApplicationStarting notification so it's added on the site when it starts up.

## Add blocklist indexing logic

Next step is to add some steps where we check if the node being indexed has a blocklist property, if it does we get the blocklist and pass it on to a helper function to extract all searchable text then save that into a new search field.

```csharp
private void IndexProvider_TransformingIndexValues(object sender, IndexingItemEventArgs e)
{
    if (!int.TryParse(e.ValueSet.Id, out int id)) return;

    using var context = _umbracoContextFactory.EnsureUmbracoContext();
    var content = context.UmbracoContext.Content.GetById(id);

    // Exit if content can't be found or doesn't have the blocklist property
    if (content == null || !content.HasProperty("mainContent")) return;

    var blockList = content.Value<BlockListModel>("mainContent");

    var searchableText = SearchableText(blockList);
    if (string.IsNullOrWhiteSpace(searchableText)) return;

    e.ValueSet.Add("blockListSearch", searchableText);
}
```

Finally we add the helper function, in this we loop through all the blocks in the blocklist, check their types (based on the generated modelsbuilder models), then we add different properties or even nested blocks to one text string that we in the end add to the field:

```csharp
private string SearchableText(BlockListModel blocklist)
{
    var searchableText = new List<string>();

    foreach (var block in blocklist)
    {
        if(block.Content is CallToAction)
        {
            var typedBlock = block.Content as CallToAction;
            searchableText.Add(typedBlock.Text);
        }

        if(block.Content is CardsRow)
        {
            var typedBlock = block.Content as CardsRow;
            searchableText.Add(typedBlock.Title);

            // This block has a blocklist with blocks of the type IconCard below, we recurse through it
            var blocks = typedBlock.Cards;
            searchableText.Add(SearchableText(blocks));
        }

        if(block.Content is ProjectPreview)
        {
            var typedBlock = block.Content as ProjectPreview;
            searchableText.Add(typedBlock.Title);
            searchableText.Add(typedBlock.Description);
            searchableText.Add(typedBlock.Image.Name);
        }

        if(block.Content is IconCard)
        {
            var typedBlock = block.Content as IconCard;
            searchableText.Add(typedBlock.Title);
            searchableText.Add(typedBlock.Description);
        }
    }

    return string.Join(" ", searchableText.ToArray());
}
```

## Comparing search fields

That's all it takes. At this point we can rebuild the index, and now we can compare the auto generated search field with our new one:

Auto generated one:
```json
{
    "layout": {
        "Umbraco.BlockList": [
            {
                "contentUdi": "umb://element/649184f94d1340ce959c0cefcd005acd",
                "settingsUdi": "umb://element/1dc5f7680cb84520a2e7145a1c349be4"
            },
            {
                "contentUdi": "umb://element/7a2243381cb74d56a53a370c22e18731",
                "settingsUdi": "umb://element/915b74c12c564a228ff31e716119181e"
            },
            {
                "contentUdi": "umb://element/8ee0bd6662d24961b0882f1da940b50e",
                "settingsUdi": "umb://element/f31f872400324fb4bb266a4356c395a6"
            }
        ]
    },
    "contentData": [
        {
            "contentTypeKey": "0ff0a31a-3727-4cba-b45b-4ba8519499bd",
            "udi": "umb://element/7a2243381cb74d56a53a370c22e18731",
            "title": "Special Skills",
            "cards": {
                "layout": {
                    "Umbraco.BlockList": [
                        {
                            "contentUdi": "umb://element/ac777ec30f5446acb4c2cf3ba7955818",
                            "settingsUdi": "umb://element/4e7e112c781549c2b89dda81c51334d6"
                        },
                        {
                            "contentUdi": "umb://element/81b42c2e8f39415b8b1c6aa656c45e0f",
                            "settingsUdi": "umb://element/4ccd9e7eb32d46dab99670c6bce28b5d"
                        },
                        {
                            "contentUdi": "umb://element/0d402062e033403788f1498d13f1d625",
                            "settingsUdi": "umb://element/002ad580c3f940a29890f17204727d57"
                        }
                    ]
                },
                "contentData": [
                    {
                        "contentTypeKey": "3f6a6761-efa8-426e-9b4b-344c596f8aae",
                        "udi": "umb://element/ac777ec30f5446acb4c2cf3ba7955818",
                        "iconClass": [
                            "Ios Star Outline"
                        ],
                        "title": "Umbraco Master",
                        "description": "I am an Umbraco Certified Master. I love everything about Umbraco, especially v9"
                    },
                    {
                        "contentTypeKey": "3f6a6761-efa8-426e-9b4b-344c596f8aae",
                        "udi": "umb://element/81b42c2e8f39415b8b1c6aa656c45e0f",
                        "iconClass": [
                            "Social Github Outline"
                        ],
                        "title": "Git Expert",
                        "description": "I am an expert at Git. Talk to me if you need some help with rebasing or merging with confilicts."
                    },
                    {
                        "contentTypeKey": "3f6a6761-efa8-426e-9b4b-344c596f8aae",
                        "udi": "umb://element/0d402062e033403788f1498d13f1d625",
                        "iconClass": [
                            "Trophy"
                        ],
                        "title": "Umbraco MVP",
                        "description": "I have been awarded an Umbraco MVP award for my contributions to the Umbraco Community"
                    }
                ],
                "settingsData": [
                    {
                        "contentTypeKey": "0c9bd42b-0bd4-4b98-a899-95d27494e348",
                        "udi": "umb://element/4e7e112c781549c2b89dda81c51334d6",
                        "hide": ""
                    },
                    {
                        "contentTypeKey": "0c9bd42b-0bd4-4b98-a899-95d27494e348",
                        "udi": "umb://element/4ccd9e7eb32d46dab99670c6bce28b5d",
                        "hide": ""
                    },
                    {
                        "contentTypeKey": "0c9bd42b-0bd4-4b98-a899-95d27494e348",
                        "udi": "umb://element/002ad580c3f940a29890f17204727d57",
                        "hide": ""
                    }
                ]
            }
        },
        {
            "contentTypeKey": "8728e9e8-749f-4719-a3f3-6ed780b42141",
            "udi": "umb://element/649184f94d1340ce959c0cefcd005acd",
            "text": "Like what you see?",
            "link": [
                {
                    "name": "Hire me",
                    "url": "/contact/"
                }
            ]
        },
        {
            "contentTypeKey": "96dd9b3c-04ae-47fb-b610-d546b05740bb",
            "udi": "umb://element/8ee0bd6662d24961b0882f1da940b50e",
            "title": "Online Blogging Site",
            "description": "I was fortunate enough to be involved in building the popular blog called codeshare.co.uk. I even migrated it from Umbraco 7 to Umbraco 8.",
            "image": [
                {
                    "key": "e5d880bc-55f4-4201-9373-843372bb39ec",
                    "mediaKey": "121e4357-98a2-4d70-ac44-d87e1732f2e4",
                    "crops": [],
                    "focalPoint": {
                        "left": 0.5,
                        "top": 0.5
                    }
                }
            ]
        }
    ],
    "settingsData": [
        {
            "contentTypeKey": "e2ec53a3-602d-4134-a56e-7ff7fe92a587",
            "udi": "umb://element/915b74c12c564a228ff31e716119181e",
            "itemName": "",
            "hide": "0"
        },
        {
            "contentTypeKey": "e1d2ae73-a9e2-42f5-85ac-c2a1d4c27c3d",
            "udi": "umb://element/1dc5f7680cb84520a2e7145a1c349be4",
            "itemName": "",
            "hide": "0"
        },
        {
            "contentTypeKey": "40c5160c-386f-42cf-9101-b4c816fc8a2d",
            "udi": "umb://element/f31f872400324fb4bb266a4356c395a6",
            "itemName": "",
            "hide": "0"
        }
    ]
}
```

Our new field:
```
Like what you see? Special Skills Umbraco Master I am an Umbraco Certified Master. 
I love everything about Umbraco, especially v9 Git Expert I am an expert at Git. 
Talk to me if you need some help with rebasing or merging with confilicts. 
Umbraco MVP I have been awarded an Umbraco MVP award for my contributions to the Umbraco Community Online Blogging Site 
I was fortunate enough to be involved in building the popular blog called codeshare.co.uk. 
I even migrated it from Umbraco 7 to Umbraco 8. 
Codeshare
```

That's all it takes!