Skip to the content

Umbraco 9 Examine - Basic Search

Examine Differences Between V8 & V9

I just got a basic search up and running for Umbraco 9 using Examine. The good news is it wasn't massively different from Umbraco 8. If you have written examine queries for V8 then I think you will be fine.

The main differences were just namespaces. I you copy your old code from V8, remove the name spaces and then just use the issue fix recommendations in Visual Studio that will get you 95% of the way.

Umbraco 9 Examine Code Example

The easiest way to show this is to post my code sample.

using System;
using System.Collections.Generic;
using System.Linq;
using Examine;
using Examine.Search;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Infrastructure.Examine;
using Umbraco.Cms.Web.Common.PublishedModels;
using Umbraco.Extensions;
using Website.Core.Models;
using static Umbraco.Cms.Core.Constants;
namespace Website.Core.Services
{
public interface IBlogService
{
BlogSearch Search(BlogSearch blogSearch);
}
public class BlogService : IBlogService
{
private readonly IUmbracoContextFactory _umbracoContextFactory;
private readonly IExamineManager _examineManager;
private readonly ILogger<BlogService> _logger;
public BlogService(IUmbracoContextFactory umbracoContextFactory, IExamineManager examineManager, ILogger<BlogService> logger)
{
_umbracoContextFactory = umbracoContextFactory;
_examineManager = examineManager;
_logger = logger;
}
public BlogSearch Search(BlogSearch blogSearch)
{
try
{
if (!_examineManager.TryGetIndex(UmbracoIndexes.ExternalIndexName, out IIndex index))
throw new InvalidOperationException($"No index found by name {UmbracoIndexes.ExternalIndexName}");
var searcher = index.Searcher;
var criteria = searcher.CreateQuery(IndexTypes.Content, BooleanOperation.And);
var examineQuery = criteria.NodeTypeAlias("blogDetailsPage");
if (!string.IsNullOrEmpty(blogSearch.Keywords))
{
if (blogSearch.Keywords.Contains(" "))
{
string[] terms = blogSearch.Keywords.Split(' ');
examineQuery.And().GroupedOr(new[] { "searchableKeywords" }, terms);
}
else
{
examineQuery.And().Field("searchableKeywords", blogSearch.Keywords.MultipleCharacterWildcard());
}
}
examineQuery.OrderByDescending(new SortableField[] { new SortableField("sortableDate") });
int pageIndex = blogSearch.CurrentPage - 1;
int pageSize = blogSearch.ItemsPerPage;
//###This should be the correct way to do this but there is still a bug with the umbraco core code.
//###For some reason it only brings back results for the first page and nothing above
//QueryOptions queryOptions = new QueryOptions(pageIndex * pageSize, blogSearch.ItemsPerPage);
//ISearchResults searchResult = examineQuery.Execute(queryOptions);
//IEnumerable<ISearchResult> pagedResults = searchResult;
//########This should not make it into production
//this is using link to page the data rather than the correct way which currently has a bug.
ISearchResults searchResult = examineQuery.Execute();
IEnumerable<ISearchResult> pagedResults = searchResult.Skip(pageIndex * pageSize).Take(blogSearch.ItemsPerPage);
int totalResults = Convert.ToInt32(searchResult.TotalItemCount);
blogSearch.TotalItems = totalResults;
blogSearch.TotalPages = (totalResults + blogSearch.ItemsPerPage - 1) / blogSearch.ItemsPerPage;
blogSearch.BlogDetailsPages = GetBlogArticlesFromSearch(pagedResults);
}
catch (Exception e)
{
_logger.LogError(e, "Search: Exception: {0} | Message: {1} | Stack Trace: {2}", e.InnerException != null ? e.InnerException.ToString() : "", e.Message != null ? e.Message.ToString() : "", e.StackTrace);
}
return blogSearch;
}
private List<BlogDetailsPage> GetBlogArticlesFromSearch(IEnumerable<ISearchResult> pagedResults)
{
List<BlogDetailsPage> blogDetailsPages = new List<BlogDetailsPage>();
using (UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext())
{
foreach (ISearchResult result in pagedResults)
{
if (int.TryParse(result.Id, out int nodeId))
{
IPublishedContentCache contentHelper = umbracoContextReference.UmbracoContext.Content;
BlogDetailsPage blogDetailsPage = contentHelper.GetById(nodeId) as BlogDetailsPage;
if (blogDetailsPage != null)
blogDetailsPages.Add(blogDetailsPage);
}
}
}
return blogDetailsPages;
}
}
}

Umbraco 9 Examine - Where has maxResults Gone?

If you have worked with Examine in the past (Umbraco 8) then you may have noticed the way you handled paging is no longer available. In Umbraco 9 it looks as though they have dropped the maxResults configuration

You may have been used to seeing this code in Umbraco 8

ISearchResults searchResult = examineQuery.Execute(maxResults: pageSize * (pageIndex + 1));

This has been replace by QueryOptions which accepts both a skip and a take value. This is then passed into the .Execute.

QueryOptions queryOptions = new QueryOptions('skip-value-here','take-value-here');
ISearchResults searchResult = examineQuery.Execute(queryOptions);

Please note note with the Beta 3 version there is still a bug with this and the paging isn't really working correctly yet. More details below.

Umbraco 9 Beta 3 Query Bug - Important

Please notice my comments in the code. There is currently an ongoing bug with Umbraco 9 when querying with QueryOptions.

You will see that I am using Linq for the skip and take. This is not recommended and will not be good for performance. This code is there only Umbraco Umbraco fix up the bug listing here.

https://github.com/umbraco/Umbraco-CMS/issues/10463

I do not recommend using Examine in production until this issue is fixed.

Umbraco 9 Examine - Creating a Custom Index

Please take a look at my follow up article where I demonstrate how to get up and running with with a custom Index.

Umbraco 9 Examine - Creating a Custom Index

About the author

David Armitage

.Net MVC Developer
.Net Core Developer
Umbraco Certified Master
Recruitment Professional

Hey Peeps,

I'm an entrepreneur and web developer with a passion for coding. I absolutely love working with Umbraco CMS and appreciate the Umbraco community even more.

I've got 10 years+ .Net experience and 7 years+ experience working in the recruitment industry, both coding and marketing job websites. I wanted to use my skills to help give something back to this awesome community and so I decided to build UmbraJobs.com.

My vision & fundamentals

I want this website to be a place where Umbraco Professionals and Umbraco Partners can advertise their services but at the same time I want to filter out all the under-qualified freelancers & agencies that take up the biggest part of other job & freelancer websites. We've all worked hard to gain our Umbraco certifications, partnership status, and help build the Umbraco community to what it is today. I want to see everyone get rewarded for their efforts.

Follow me on social media

If you're an Umbraco professional, partner, employer, recruiter or a passionate Umbraco community builder then I'm more than happy to hear from you. Follow me on my social media channels and please reach out if you have any needs, want help or consultation with anything Umbraco related or just want a general chat.

comments powered by Disqus

Blog Filter


How we can help?

Need help with an Umbraco project?

Need help with a project but not too sure who to approach? Send us your project brief and budget. We will provide a free consultation and can help you gather quotes from the best and most suitable freelancers or agencies.

Looking to hire an Umbraco Professional?

Have you got job vacancy and want to hire an Umbraco professional? Post a job on our website for free. Alternatively let us know your requirements and we will send suitable prospects your way.

Claim your free profile!

Are you an Umbraco Freelance Developer or Umbraco partner that wants to advertise on our site? If you work with Umbraco or registered as an Umbraco partner then you can create a profile for free.

Let's build the Umbraco Community

We're big on building the Umbraco community and we think you guys are awesome! If there's anyway at all we can help then please reach out.