Many modern applications require powerful search mechanisms to become useful and make their users more productive. That is in large part due to the amount of data available to work with. Thankfully there are already powerful tools to index your data and make it searchable.
One of the most well known state-of-the-art solutions is ElasticSearch and it has an API to be used from .NET called NEST. While the documentation is ok I want to give a quick rundown on how to add searching capabilities to your .NET Core application. Some ideas are borrowed from the great post “Using Elasticsearch with ASP.NET Core and Docker“.
Getting ElasticSearch running
The easiest way to get up and running with elasicsearch is to use their docker images and just run the container on your development machine. I like to a docker compose file like the following to get elasticsearch and its tooling application kibana up and running fast:
- type: bind
After you run it with docker compose you can talk to the search service on http://localhost:9200/ and the kibana management GUI on http://localhost:5601/. On the Kibana UI especially the Dev Tools and its console are interesting for experimenting with search queries.
Access ElasticSearch from your .NET Core app
I find it quite elegant to write a extension method for the IServiceCollection to configure the ElasticClient and register it as a Singleton to the dependency injection framework of .NET Core like so
public static class ElasticSearchExtension
public static void AddElasticsearch(
this IServiceCollection services, IConfiguration configuration)
var url = configuration["elasticsearch:url"];
var settings = new ConnectionSettings(new Uri(url))
.DefaultMappingFor<SearchableDevice>(deviceMapping => deviceMapping
.IdProperty(dev => dev.Id)
var client = new ElasticClient(settings);
var response = client.Indices.Create("devices", creator => creator
.Map<SearchableDevice>(device => device
// maybe check response to be safe...
The configuration block looks like following
and allows for future extension.
Our search service has to be registered in
Startup of course:
public void ConfigureServices(IServiceCollection services)
Indexing our objects
In the above example we built a SearchableDevice class whose public properties are going to be indexed by ElasticSearch. The API allows for a much more fined grained control about what and how to index but we want to keep things simple without having to worry about excluding Properties and so on. If you have set it up that way indexing a
SearchableDevice is merely one simple call:
// SearchClient is the injected IElasticClient
// mySearchableDevice is an instance of SearchableDevice
Searching for objects
When developing a search query I like to try it in the Kibana Dev Tools and then transform it to a NEST call. A simple query to look in all device properties if they start with “needle” looks like this:
The nice thing about the Kibana Dev Tools console is that it can display and complete the possible values for fields like “type” in your multi_match query.
That search query can then be translated to a NEST call in a straightforward way and looks this way:
var response = SearchClient.Search<SearchableDevice>(sd => sd
.Query(q => q
.MultiMatch(query => query
The search response contains the hits with their source objects and some metadata like the score and result count.
Bear in mind that ElasticSearch only returns the first/best 10 matches by default, so specifying the result size often might be closer to what you want.
Wrapping it up
Getting started with ElasticSearch in .NET Core does not require too much boiler plate an setup work if you use tools like docker and the NEST library. Making it usable and tuning the indexing and querying may require a lot of work to achieve the best results. On the other hand smaller applications can start-off with a simple search setup like shown above and simply evolve it when need be.