In this article, you will learn how to leverage your C# skills to build a search application using Blazor and Elasticsearch. We are going to use the Elasticsearch .NET client to run full text, semantic and hybrid search queries.
NOTE If you are familiar with the older version of the Elasticsearch C# client NEST, read this blog post about NEST client deprecation and new features. NEST was the previous generation of the .NET client which was replaced with the Elasticsearch .NET Client.
What is Blazor?
Blazor is an open source HTML, CSS, and C# based web framework created by Microsoft to allow developers to build web applications that run on the client or the server. Blazor also allows you to make reusable components to build applications faster; it enables developers to build the HTML view and actions in C# within the same file, which helps maintain readable and clean code. Additionally, with Blazor Hybrid you can build native mobile apps accessing the native platform capabilities via .NET code.
Some of the features that makes Blazor a great framework to work with:
- Server-side and client-side rendering options
- Reusable UI components
- Real-time updates with SignalR
- Built-in state management
- Built-in routing system
- Strong typing and compile-time checks
Why Blazor?
Blazor offers several advantages over others frameworks and libraries: it allows developers to use C# for both client and server code, providing strong typing and compile-time checking which enhances reliability. It integrates seamlessly with the .NET ecosystem, enabling the reuse of .NET libraries and tools, and offers robust debugging support.
What is ESRE?
Elasticsearch Relevance Engine™ (ESRE) is a set of tools to build search applications using machine learning and and artificial intelligence on top of the powerful Elasticsearch search engine.
To learn more about ESRE, you can read our insightful blog post located here
Configuring ELSER
To leverage Elastic's ESRE capabilities, we are going to use ELSER as our model provider.
Note to use the ELSER models of Elasticsearch, you must to have a Platinum or Enterprise license, and have a minimum dedicated Machine Lerning (ML) node of 4GB of size. Read more about here.
Start by creating the inference endpoint:
If this is your first time using ELSER, you may encounter a 502 Bad Gateway error as the model loads in the background. You can check the model's status in Machine Learning > Trained Models
in Kibana. Once it is deployed, you can proceed to the next step.
Indexing data
You can download the dataset here and then import the data using Kibana. To do this, go to the homepage and click "Upload data". Then, upload the file and click Import
. Finally, go to the Advanced
tab and paste the following mappings:
We are going to create an index capable of running semantic and full text queries. The semantic_text field type will take care of data chunking and embedding. Note we are indexing longDescription
as semantic_text
, you can use copy_to if you want to index a field as both semantic_text
and text`.
Building the app with Blazor & Elasticsearch
API key
The first thing we need to do is create an API key to authenticate our requests to Elasticsearch. The API key should be read-only and allowed only to query the books-blazor
index.
You will see something like this:
Save the value of the encoded
response field as it's needed later. If you are running on Elastic Cloud, you will also need your Cloud ID. (You can find it here).
Creating the Blazor project
Start by installing Blazor and creating a sample project following the official instructions.
One you have the project created, the folder structure and files should look like this:
The template application includes Bootstrap v5.1.0 for styling.
Finish the project setup by installing Elasticsearch .NET client:
Once you finish this step, your page should look like this:
Folders structure
Now, we are going to organize our folders as follows:
Files explained:
- Components/Pages/Search.razor: main page containing the search bar, the results, and the filters.
- Components/Pages/Search.razor.css: page styles.
- Components/Elasticsearch/SearchBar.razor: search bar component.
- Components/Elasticsearch/Results.razor: results component.
- Components/Elasticsearch/Facet.razor: filters component.
- Components/Svg/GlassIcon.razor: search icon.
- Components/_Imports.razor: this will import all the components.
- Models/Book.cs: this will store the book field schema.
- Models/Response.cs: this will store the response schema, including the search results, facets and total hits.
- Services/ElasticsearchService.cs: Elasticsearch service. It will handle the connection and queries to Elasticsearch.
Initial Configuration
Let's start with some clean-up.
Delete the files:
- Components/Pages/Counter.razor
- Components/Pages/Weather.razor
- Components/Pages/Home.razor
- Components/Layout/NavMenu.razor
- Components/Layout/NavMenu.razor.css
Check the /Components/_Imports.razor
file. You should have the following imports:
Integrating Elastic into the project
Now, let’s import the Elasticsearch components:
We are going to remove the default sidebar to have more space for our application by removing it from the /Components/Layout/MainLayout.razor
file:
Now let's enter the Elasticsearch credentials for the user secrets:
Using this approach, .Net 8 stores sensitive data in a separate location, outside of the project folder and makes it accessible using the IConfiguration
interface. These variables will be available to any .Net project that uses the same user secrets.
Then, let's modify the Program.cs
file to read the secrets and mount the Elasticsearch client:
First, import the necessary libraries:
- BlazorApp.Services: contains the Elasticsearch service.
- Elastic.Clients.Elasticsearch: imports the Elasticsearch client .Net 8 library.
- Elastic.Transport: imports the Elasticsearch transport library, which allows us to use the ApiKey class to authenticate our requests.
Second, insert the following code before the var app = builder.Build()
line:
This code will read the Elasticsearch credentials from the user secrets and create an Elasticsearch client instance.
After the ElasticSearch client initialization, add the following line to register the Elasticsearch service:
The next step will be to build the search logic in the /Services/ElasticsearchService.cs
file:
First, import the necessary libraries and models:
Second, add the class ElasticsearchService
, constructor and variables:
Configuring search
Now, let's build our search logic:
BuildFilters
will build the filters for the search query using the selected facets by the user.BuildHybridQuery
will build a hybrid search query that combines full text and semantic search.
Next, add the search method:
SearchBooksAsync
: will perform the search using the hybrid query and return the results included aggregations for building the facets.FormatFacets
: will format the aggregations response into a dictionary.ConvertFacetDictionary
: will convert the facet dictionary into a more readable format.
The next step is to create the models that will represent the data returned in the hits
of the Elasticsearch query that will be printed as the results in our search page.
We start by creating the file /Models/Book.cs
and adding the following:
Then, setting up the Elastic response in the /Models/Response.cs
file and adding the following:
Configuring a basic UI
Next, add the SearchBar component. In the file /Components/Elasticsearch/SearchBar.razor
and add the following:
This component contains a search bar and a button to perform the search.
Blazor provides great flexibility by allowing generating HTML dynamically using C# code within the same file.
Afterwards, in the file /Components/Elasticsearch/Results.razor
we will build the results component that will display the search results:
Finally, we will need to create facets to filter the search results.
Note: Facets are filters that allow users to narrow down search results based on specific attributes or categories, such as product type, price range, or brand. These filters are typically presented as clickable options, often in the form of checkboxes, to help users refine their search and find relevant results more easily. In Elasticsearch context, facets are created using aggregations.
We set up facets by putting the following code In the file /Components/Elasticsearch/Facet.razor
:
This component reads from a terms
aggregation on the author
, categories
, and status
fields, and then produces a list of filters to send back to Elasticsearch.
Now, let's put everything together.
In /Components/Pages/Search.razor
file:
Our page is working!
As you can see, the page is functional but lacks styles. Let's add some CSS to make it look more organized and responsive.
Let's start replacing the layout styles. In the Components/Layout/MainLayout.razor.css
file:
Add the styles for the search page in the Components/Pages/Search.razor.css
file:
Our page starts to look better:
Let's give it the final touches:
Create the following files:
- Components/Elasticsearch/Facet.razor.css
- Components/Elasticsearch/Results.razor.css
And add the styles for Facet.razor.css
:
For Results.razor.css
:
Final result:
To run the application you can use the following command:
dotnet watch
You did it! Now you can search for books in your Elasticsearch index by using the search bar and filter the results by author, category, and status.
Performing full text and semantic search
By default our app will perform a hybrid search using both full text and semantic search. You can change the search logic by creating two separate methods, one for full text and another for semantic search, and then selecting one method to build the query based on the user's input.
Add the following methods to the ElasticsearchService
class in the /Services/ElasticsearchService.cs
file:
Both methods work similarly to the BuildHybridQuery
method, but they only perform full text or semantic search.
You can modify the SearchBooksAsync
method to use the selected search method:
You can find the complete application here
Conclusion
Blazor is an effective framework that allows you to build web applications using C#. Elasticsearch is a powerful search engine that allows you to build search applications. Combining both, you can easily build robust search applications, leveraging the power of ESRE to create a semantic search experience in a short time.
Want to get Elastic certified? Find out when the next Elasticsearch Engineer training is running!
Elasticsearch is packed with new features to help you build the best search solutions for your use case. Dive into our sample notebooks to learn more, start a free cloud trial, or try Elastic on your local machine now.