How to make a console application with C# and a linked class library
⌨️

How to make a console application with C# and a linked class library

Let’s write a simple C# program that indexes and searches a data set.

In this guide we will be using BionicSearchLib with a single-file DLL installation. Request Developer Access to obtain your library file.

💡
This tutorial requires Microsoft .NET 6. We recommend Visual Studio or Visual Studio Code as IDEs. This code also requires the NuGet called Microsoft.Extensions.Logging.Abstractions version 6 or greater.

What you will learn

  • Create a simple C# .NET console app
  • Run dotnet from terminal or VSCode
  • Install BionicSearchLib
  • Add and index data
  • Set up a search query

You will not need prior C# knowledge to follow this guide, but a basic understanding of programming is required.

Prerequisites:

Make sure you have a BionicSearchLib.dll file. If you dont, Request Developer Access and we’ll supply you one if your application is approved.

Download the data here:

Download airports.txt (2.4mb)

Source files for tutorial in github

Let’s begin 👩🏾‍💻🧑🏼‍💻👨🏻‍💻👩🏽‍💻👩🏻‍💻

In this tutorial we will be using 💻 VSCode for Mac.

1 ) Create a new C# console application

Navigate to the directory where you would like to create a new app, and create a new folder with the name of your app. We will call our example SearchConsole

mkdir SearchConsole

Create a new Console App with the dotnet CLI from your shell or terminal

dotnet new console
image

If your terminal looks like the one above, you are ready to open your project in VSCode.

Open VSCode and choose Open Folder → Select your project folder (SearchConsole)

If you get a prompt to install extensions, go ahead and choose Yes

image

Open a Terminal from VSCode by choosing Terminal → New Terminal

Test that everything is working by running your app

dotnet run
image

All right!

Let’s proceed with installing Bionic Search

2) Add Microsoft.Extensions.Logging.Abstractions as NuGet

Add a NuGet package manager to VSCode. We recommend this one:

Name: NuGet Package Manager Id: jmrog.vscode-nuget-package-manager VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=jmrog.vscode-nuget-package-manager

Before installing the package, make sure you are working in the directory of your C# project.

  • Open the command pallette (Shift-CTRL/CMD-P) and type “nuget”.
image
  • Choose NuGet Package Manager: Add Package and search for Microsoft.Extensions.Logging.Abstractions.
image
  • Choose the latest stable version (number 6.0.0 or higher).
image

3) Include BionicSearchLib as assembly reference

Place your BionicSearchLib.dll file in your project directory

image

In VSCode, open your .csproj file (SearchConsole.csproj)

Add a new ItemGroup with a reference to the dll just before the </Project> tag

<ItemGroup>
	<Reference Include="BionicSearchLib">
	  <HintPath>BionicSearchLib.dll</HintPath>
  </Reference>
</ItemGroup>
image

4) Prepare your app

Go back to Program.cs → Remove the default “Hello, World!” lines from your app.

To get started we’ll need a simple structure with a using and a namespace argument

using BionicSearchLib;
namespace SearchConsole
{
    internal class Program
    {
        private static void Main()
        {
            //write your code here
        }
    }
}

Then we’ll go on to add Bionic.

5) Create an instance of BionicSearchEngine

Inside your Main() function add the following code snippet. In this example we do not need the logger functions, so we’ll add null statements. Also, we will use configuration number 103.

var SearchEngine = new  BionicSearchEngine(null, null, 103);

6) Prepare and add your data

Add your airports.txt file to the root folder of your project

image

The airports.txt file contains 40.000+ lines with airports including information about regions and countries. We will divide it into a string per line, and then add it to an array in our app.

Reference the data file in the code. Continue inside your Main() function.

//
// Prepare data
//

string fileName = "airports.txt";
string path = Path.Combine(Environment.CurrentDirectory, @"../..", fileName);

var lines = File.ReadAllLines(path);
image

7) Insert data into a list with the Document class

To index the data we will add the strings from our lines to our Document class. This class also takes arguments like a key, segment number, and some extra metadata for the client. We will not use these in this simple example.

Insert by looping through the lines we read from airports.txt. Add key 0,1,2 .. and up to the entries.

Lastly, run Insert on our instance of the engine with our documents as an array.

//
// Insert 
//

var key = 0; // foreign key
var documents = new List<Document>();
foreach (var item in lines)
{
    var doc = new Document(key, 0, item, "");
    documents.Add(doc);
    key++;
}

SearchEngine.Insert(documents.ToArray());
image

8) Index the documents and check status with a progress bar

Index by simply running

SearchEngine.IndexAsync();

And check for status by running the status function. We will do this with a console log to create a progress loader when indexing.

var status = SearchEngine.Status;
while (!status.SearchIsAllowed) // Check and print status while indexing
{
    Console.WriteLine(status.IndexProgressPercent);
    System.Threading.Thread.Sleep(5); // log every 5ms
}

This prints out the index progress as percentage in the console:

image

9) Create a search query

The SearchQuery class takes a number of arguments. To create a structure for this, we will add variables with explanations.

In this example we will search for Frankfurt airport. We will use the Bionic RelevancyRanking algorithm, which is the best algorithms for most use cases. We will not use logger functions so this will be an empty string.

Feel free to search for another airport.

💡
The search function of BionicSearch is so fast that you can run multiple calls in a chain without the user noticing delay.
var text = "frankfrt"; // pattern to be searched for 
var algorithm = Algorithm.ProprietaryRelevancyRanking; // Default algorithm
var numRecords = 10; // Records to be returned
var timeOutLimit = 1000; // Timeout if cpu overload in milliseconds
var rmDuplicates = true; // remove duplicates with same key
var logPrefix = ""; // logger prefix per search

var query = new SearchQuery(text, algorithm, numRecords, timeOutLimit, rmDuplicates, logPrefix);

10) Reading our search results

The Search function answers with an array of SearchRecords.

Loop through the records to display them. The class has multiple properties, but we are mostly interested in the DocumentTextToBeIndexed as it displays the text of the result.

var result = SearchEngine.Search(query);
for (int i = 0; i < result.SearchRecords.Length; i++)
{
	Console.WriteLine(result.SearchRecords[i].DocumentTextToBeIndexed);
}

Run your app with “dotnet run” to display your results

💡
Psst, if you run “dotnet watch run” the CLI will listen for your changes and rebuild the app when you save. This is useful if you want to try different search queries
image

That’s it! You have now built an app with Bionic Search 🎉

11) (Optional) Filtering out results with a low score

By default, the pattern recognition of the Search function will almost always answer with results, even if the metric score is very low.

The Bionic Search Engine uses pattern recognition on the whole search string, and not just single words.

Read about the Core principles to learn more.

If you want to filter out low-ranking results, we can use the MetricScore property of SearchRecords.

One way to do this is to create an if state that only displays results if the score is higher than a set threshold.

var result = SearchEngine.Search(query);
for (int i = 0; i < result.SearchRecords.Length; i++)
{
    if(result.SearchRecords[i].MetricScore >= 100)
    {
        Console.WriteLine(result.SearchRecords[i].DocumentTextToBeIndexed);
        Console.WriteLine(result.SearchRecords[i].MetricScore);
    }
}