How search works in Umbraco
Umbraco keeps your published content in a search index called ExternalIndex, powered by Examine and Lucene. It's updated automatically whenever you publish. You can browse it under Settings › Examine Management.
Step 1: A reusable search service
using Examine;
using Examine.Search;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Infrastructure.Examine;
namespace MySite.Search;
public record SearchHit(IPublishedContent Page, float Score);
public interface ISiteSearch
{
IReadOnlyList<SearchHit> Search(string term, int take = 20);
}
public class SiteSearch(IExamineManager examineManager, IPublishedContentCache contentCache) : ISiteSearch
{
public IReadOnlyList<SearchHit> Search(string term, int take = 20)
{
if (string.IsNullOrWhiteSpace(term)
|| examineManager.TryGetIndex(Constants.UmbracoIndexes.ExternalIndexName, out IIndex? index) == false)
{
return [];
}
ISearchResults results = index.Searcher
.CreateQuery(IndexTypes.Content)
.ManagedQuery(term.Trim())
.Execute(QueryOptions.SkipTake(0, take));
return results
.Select(r => (Result: r, Page: int.TryParse(r.Id, out int id) ? contentCache.GetById(id) : null))
.Where(x => x.Page is not null)
.Select(x => new SearchHit(x.Page!, x.Result.Score))
.ToList();
}
}ManagedQuery searches all the text fields in the index, so you don't need to list them.
Step 2: Register it
builder.Services.AddScoped<ISiteSearch, SiteSearch>();Step 3: The search page
Create a Search document type and page, then use this template:
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@using MySite.Search
@inject ISiteSearch SiteSearch
@{
var term = Context.Request.Query["q"].ToString();
var hits = SiteSearch.Search(term);
}
<form method="get" role="search">
<label for="q">Search</label>
<input id="q" name="q" type="search" value="@term">
<button type="submit">Search</button>
</form>
@if (string.IsNullOrWhiteSpace(term) == false)
{
<p>@hits.Count result(s) for "@term"</p>
<ol>
@foreach (var hit in hits)
{
<li><a href="@hit.Page.Url()">@hit.Page.Name</a></li>
}
</ol>
}Razor encodes @term automatically, so search terms can't inject HTML into your page.
Better results
- Search specific fields with extra weight on titles:
.Field("nodeName", term).Or().Field("bodyText", term). - Only search some document types: add
.And().NodeTypeAlias("blogPost"). - Hide pages from search: give them a
hideFromSearchTrue/false property and filter it out with.Not().Field("hideFromSearch", "1"). - Results missing? Rebuild ExternalIndex in Settings › Examine Management, and check the page is published.
Need a hand?
umbraco.ninja has more than 20 years of Umbraco experience. If you'd rather not do this yourself, we'll set it up, test it and hand it over.
Get a fixed-price quote