Skip to content

Commit 4ee91d6

Browse files
authored
Merge pull request #7611 from umbraco/cms/update-create-custom-examine-index
Update Examine custom index examples
2 parents 9401e90 + ef23b58 commit 4ee91d6

File tree

2 files changed

+389
-20
lines changed

2 files changed

+389
-20
lines changed

16/umbraco-cms/reference/searching/examine/indexing.md

Lines changed: 197 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,14 @@ Remember to register `ConfigureMemberIndexOptions` in your composer.
144144

145145
## Creating your own index
146146

147-
The following example will show how to create an index that will only include nodes based on the document type _product_.
147+
### A custom Umbraco content index
148+
149+
The following example will show how to create an index that will only include nodes based on the **Product**.
148150

149151
{% hint style="info" %}
150-
We always recommend that you use the existing built in ExternalIndex. You should then query based on the NodeTypeAlias instead of creating a new separate index based on that particular node type. However, should the need arise, the example below will show you how to do it.
152+
It is recommended to use the existing built-in `ExternalIndex`. You should then query based on the `NodeTypeAlias` instead of creating a new separate index based on that particular node type. However, should the need arise, the example below will show you how to do it.
151153

152-
Take a look at our [Examine Quick Start](quick-start.md) to see some examples of how to search the ExternalIndex.
154+
Take a look at the [Examine Quick Start](quick-start.md) guide to see some examples of how to search the ExternalIndex.
153155
{% endhint %}
154156

155157
To create this index we need five things:
@@ -161,7 +163,7 @@ To create this index we need five things:
161163
5. An `INotificationHandler` implementation that updates the index when content changes.
162164
6. A composer that adds all these services to the runtime.
163165

164-
### ProductIndex
166+
#### ProductIndex
165167

166168
```csharp
167169
using Examine.Lucene;
@@ -190,7 +192,7 @@ public class ProductIndex : UmbracoExamineIndex
190192
}
191193
```
192194

193-
### ConfigureProductIndexOptions
195+
#### ConfigureProductIndexOptions
194196

195197
```csharp
196198
using Examine;
@@ -238,7 +240,7 @@ public class ConfigureProductIndexOptions : IConfigureNamedOptions<LuceneDirecto
238240
}
239241
```
240242

241-
### ProductIndexValueSetBuilder
243+
#### ProductIndexValueSetBuilder
242244

243245
```csharp
244246
using Examine;
@@ -271,7 +273,7 @@ public class ProductIndexValueSetBuilder : IValueSetBuilder<IContent>
271273
}
272274
```
273275

274-
### ProductIndexPopulator
276+
#### ProductIndexPopulator
275277

276278
```csharp
277279
using Examine;
@@ -326,7 +328,7 @@ This is only an example of how you could do indexing. In this example, we're ind
326328
In certain scenarios only published content should be added to the index. To achieve that, you will need to implement your own logic to filter out unpublished content. This can be somewhat tricky as the published state can vary throughout an entire structure of content nodes in the content tree. For inspiration on how to go about such filtering, you can look at the [ContentIndexPopulator in Umbraco](https://github.com/umbraco/Umbraco-CMS/blob/c878567633a6a3354c1414ccd130c9be518b25f0/src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs#L115).
327329
{% endhint %}
328330

329-
### ProductIndexingNotificationHandler
331+
#### ProductIndexingNotificationHandler
330332

331333
The index will only update its content when you manually trigger an index rebuild in the Examine dashboard. This is not always the desired behavior for a custom index.
332334

@@ -452,7 +454,7 @@ public class ProductIndexingNotificationHandler : INotificationHandler<ContentCa
452454
You can find further inspiration for implementing notification handlers (_for example, for media updates_) in the [UmbracoExamine.PDF package](https://github.com/umbraco/UmbracoExamine.PDF).
453455
{% endhint %}
454456

455-
### ExamineComposer
457+
#### ExamineComposer
456458

457459
```csharp
458460
using Examine;
@@ -462,6 +464,7 @@ using Umbraco.Cms.Infrastructure.Examine;
462464

463465
namespace Umbraco.Docs.Samples.Web.CustomIndexing;
464466

467+
[ComposeAfter(typeof(AddExamineComposer))]
465468
public class ExamineComposer : IComposer
466469
{
467470
public void Compose(IUmbracoBuilder builder)
@@ -483,8 +486,192 @@ public class ExamineComposer : IComposer
483486
The order of these registrations matters. It is important to register your index with `AddExamineLuceneIndex` before calling `ConfigureOptions`.
484487
{% endhint %}
485488

486-
### Result
489+
#### Result
487490

488491
![Custom product index](images/examine-management-product-index.png)
489492

490493
![Product document](images/examine-management-product-document.png)
494+
495+
### A custom index for non-Umbraco data
496+
497+
If you have a need, you can also use an Examine index for other data, that you aren't managing as Umbraco content.
498+
499+
As an illustrative example, consider a collection of books. This example uses a hardcoded collection. In a real-world scenario, the data would more likely come from a database.
500+
501+
```csharp
502+
namespace Umbraco.Docs.Samples.Web.CustomIndexing;
503+
504+
public class Book
505+
{
506+
public int Id { get; set; }
507+
508+
public string Title { get; set; } = string.Empty;
509+
510+
public string Author { get; set; } = string.Empty;
511+
512+
public int PublishedYear { get; set; }
513+
}
514+
515+
public static class BookData
516+
{
517+
public static List<Book> GetBooks() =>
518+
[
519+
new() { Id = 1, Title = "The Great Gatsby", Author = "F. Scott Fitzgerald", PublishedYear = 1925 },
520+
new() { Id = 2, Title = "To Kill a Mockingbird", Author = "Harper Lee", PublishedYear = 1960 },
521+
new() { Id = 3, Title = "1984", Author = "George Orwell", PublishedYear = 1949 },
522+
new() { Id = 4, Title = "Pride and Prejudice", Author = "Jane Austen", PublishedYear = 1813 },
523+
new() { Id = 5, Title = "The Catcher in the Rye", Author = "J.D. Salinger", PublishedYear = 1951 }
524+
];
525+
}
526+
```
527+
528+
As with the previous example, define an index. At this time, Umbraco data isn't being indexed; the implementation inherits directly from `LuceneIndex`:
529+
530+
```csharp
531+
using Examine.Lucene;
532+
using Examine.Lucene.Providers;
533+
using Microsoft.Extensions.Options;
534+
535+
namespace Umbraco.Docs.Samples.Web.CustomIndexing;
536+
537+
public class BookIndex : LuceneIndex
538+
{
539+
public BookIndex(
540+
ILoggerFactory loggerFactory,
541+
string name,
542+
IOptionsMonitor<LuceneDirectoryIndexOptions> indexOptions)
543+
: base(loggerFactory, name, indexOptions)
544+
{
545+
}
546+
}
547+
```
548+
549+
The index is customized and fields are defined as before via `IConfigureNamedOptions`:
550+
551+
```csharp
552+
using Examine;
553+
using Examine.Lucene;
554+
using Microsoft.Extensions.Options;
555+
556+
namespace Umbraco.Docs.Samples.Web.CustomIndexing;
557+
558+
public class ConfigureBookIndexOptions : IConfigureNamedOptions<LuceneDirectoryIndexOptions>
559+
{
560+
public void Configure(string? name, LuceneDirectoryIndexOptions options)
561+
{
562+
if (name?.Equals("BookIndex") is false)
563+
{
564+
return;
565+
}
566+
567+
options.FieldDefinitions = new(
568+
new("id", FieldDefinitionTypes.Integer),
569+
new("title", FieldDefinitionTypes.FullText),
570+
new("author", FieldDefinitionTypes.FullText),
571+
new("publishedYear", FieldDefinitionTypes.Integer)
572+
);
573+
}
574+
575+
public void Configure(LuceneDirectoryIndexOptions options)
576+
=> Configure(string.Empty, options);
577+
}
578+
```
579+
580+
And once again, a composer is required to register the necessary components:
581+
582+
```csharp
583+
using Examine;
584+
using Umbraco.Cms.Core.Composing;
585+
using Umbraco.Cms.Infrastructure.Examine;
586+
587+
namespace Umbraco.Docs.Samples.Web.CustomIndexing;
588+
589+
[ComposeAfter(typeof(AddExamineComposer))]
590+
public class ExamineComposer : IComposer
591+
{
592+
public void Compose(IUmbracoBuilder builder)
593+
{
594+
builder.Services.AddExamineLuceneIndex<BookIndex, ConfigurationEnabledDirectoryFactory>("BookIndex");
595+
596+
builder.Services.ConfigureOptions<ConfigureBookIndexOptions>();
597+
}
598+
}
599+
```
600+
601+
With this in place, the details of the index will be available under the **Settings** > **Examine Management** > **Indexes** screen.
602+
603+
To verify indexing and querying, a controller can be used:
604+
605+
```csharp
606+
using Examine;
607+
using Microsoft.AspNetCore.Mvc;
608+
609+
namespace Umbraco.Docs.Samples.Web.CustomIndexing;
610+
611+
[ApiController]
612+
[Route("/umbraco/api/books")]
613+
public class BooksController : ControllerBase
614+
{
615+
private readonly IExamineManager _examineManager;
616+
617+
public BooksController(IExamineManager examineManager) => _examineManager = examineManager;
618+
619+
[HttpPost("populateIndex")]
620+
public async Task<IActionResult> PopulateIndex()
621+
{
622+
if (_examineManager.TryGetIndex("BookIndex", out IIndex? index) == false)
623+
{
624+
throw new InvalidOperationException("Book index not found");
625+
}
626+
627+
List<Book> books = BookData.GetBooks();
628+
foreach (Book book in books)
629+
{
630+
index.IndexItems(
631+
[
632+
ValueSet.FromObject(
633+
book.Id.ToString(),
634+
"Book",
635+
book),
636+
]);
637+
}
638+
639+
return Ok("Done");
640+
}
641+
642+
[HttpGet("query")]
643+
public async Task<IActionResult> Query(string q)
644+
{
645+
if (_examineManager.TryGetIndex("BookIndex", out IIndex? index) == false)
646+
{
647+
throw new InvalidOperationException("Book index not found");
648+
}
649+
650+
var results = index.Searcher.Search(q)
651+
.Select(x => new BookDto(x.Values["title"], x.Values["author"], int.Parse(x.Values["publishedYear"])))
652+
.ToList();
653+
return Ok(results);
654+
}
655+
656+
private class BookDto
657+
{
658+
public BookDto(string title, string author, int publishedYear)
659+
{
660+
Title = title;
661+
Author = author;
662+
PublishedYear = publishedYear;
663+
}
664+
665+
public string Title { get; }
666+
667+
public string Author { get; }
668+
669+
public int PublishedYear { get; }
670+
}
671+
}
672+
```
673+
674+
675+
676+
677+

0 commit comments

Comments
 (0)