Azure Search Client Library

azure_search_client_library_logo

Azure Search Client Library covers all the functionalities available within the Azure Search service. Using the Azure Search Client Library you can quickly and easily take advantage of Azure Search within your application using .NET Framework and your favourite programming language, may that be C# or VB.NET.

Before getting started, please keep in mind that Azure Search Client Library is a library client API written around the Azure Search REST API and therefore:

  1. HTTP and HTTPS will be created over your network, when you use Azure Search Client Library
  2. The documentation here contains inserts from the Azure Search REST API documentation available on MSDN here
  3. And last, but especially not least, should you find any differences between the documentation available on MSDN and this client library’s documentation, remember that these might be due to updates done to the REST API which weren’t yet updated in the client library.

Sidenote: I will do my best in keeping the client library up-to-date and in accordance to the REST API’s latest versions. However, also keep in mind that even though updates to the REST API might appear quicker, the client library will still work at all times considering that there’s a special string sent through the HTTP requests which basically selects the REST API syntax version, thus making the API backward compatible.

Getting started

Whenever you want to start using the Azure Search Client library, simply load a new instance of the Service class. This will simply create a working set of credentials which is going to be used whenever a call to the service is made:

var azureSearchService = new Service("[service name]""[query key]""[management key]");

When you call the Service class constructor, you’ll also send the service name and the keys (management and query keys) associated to it. However, keep in mind that if you want to prevent sending out your service’s management key to developers or whoever may query your service, you may simply send an empty string or null for the management key parameter. Moreover, if you’re looking for a cleaner looking code, you can also call the Service constructor without sending out the management key, since both the query and management key parameters are optional. Note that the management key is required for all management operations and sending an incorrect or invalid key will when you instanciate the service will end up in an exception being thrown (for more about the exceptions which the code can through, see the Exceptions section).

Once the service is instanciated, you get the option of loading all your existing indexes directly to the Indexes property of your service. This will help you a lot when it comes to referencing the index you want to manage or query:

await azureSearchService.LoadIndexesAsync();

That’s all, you’re all set now.

Management Operations

Creating and updating an index

When you create a new index, you simply call the CreateIndexAsync method, passing an Index object to the method, like this:

var result = await azureSearchService.Indexes.CreateIndexAsync(eventIndex);

When you create a new index you absolutely have to send the schema of the index, which is required for the indexing system to know how to handle the indexes’ documents. Since your index documents will be nothing else than instances of a specifc class (which, as you’ll later see, have to implement the abstract Document class), a best practice is to have a static method inside your class which returns the schema of that specific type, like this:

public static IEnumerable<Field> GetSearchableEventFields()
{
    return new List<Field>()
    {                
        new Field() { Name = "eventId", Type= FieldDataTypes.String, IsKey = true },
        new Field() { Name = "name", Type = FieldDataTypes.String, IsFilterable = false, IsShownInSuggestions = true}, 
        new Field() { Name = "category", Type = FieldDataTypes.String },
        new Field() { Name = "description", Type = FieldDataTypes.String },
        new Field() { Name = "location", Type = FieldDataTypes.String },
        new Field() { Name = "date", Type = FieldDataTypes.DateTimeOffset },
        new Field() { Name = "tags", Type = FieldDataTypes.StringCollection }
    };
}
// usage:
var eventSearchableFields = SearchableEvent.GetSearchableEventFields(); 
var eventIndex = new Index(eventSearchableFields, "events");

The list of fields will specify the properties an index document will most certainly contain (meaning that a document type might as well specify more properties which aren’t covered by the list of fields). Even though any field can have any of the following types and properties, do keep in mind that at least one field must be marked as a key field:

FieldDataTypes Field properties
String Name
StringCollection Type
Boolean IsSearchable
Int32 IsFilterable
Double IsSortable
DateTimeOffset IsFacetable
GeographyPoint IsShownInSuggestions
GeographyPolygon IsKey
IsRetrievable

 

Should anything go wrong with the CreateIndexAsync call, an exception (of type AzureSearchException) containing information on why the request didn’t end up successfully is thrown.

Get Index

If you want a direct reference to one of your indexes, all you have to do is either enumerate through the collection of indexes inside the Indexes property or directly get a references on an index-basis using the indexing syntax available. Here’s an example:

// method 1
foreach (var index in azureSearchService.Indexes)
{
    // do stuff
}
// method 2
var index = azureSearchService.Indexes["[index name]"];

Remember that if you’re trying to get a reference for an inexistent index, you will simply get a null result.

Note: Don’t worry about having your indexes created from multiple locations. Should your library not have a reference for an index because it was created on a different thread or from a completely different end, the library will try to fetch the index from the service directly. However, please be aware that if you delete your index from a different location/machine etc., since your library references are not synced across each other, your outdated libraries will still contain a reference to the original index in memory.

Get Index Statistics

You have the option of returning the number of documents currently held within an index. In order to retrieve this number, simply call the GetStatisticsAsync() method. Here’s an example:

var result = await azureSearchService.Indexes[tbIndexName.Text].GetStatisticsAsync();

Delete Index

When you want to delete an index, you may call the DeleteIndexAsync method. There are two overloads for DeleteIndexAsync which allow you to either delete an index by using the index name or directly by sending in an index reference. Here’s an example:

var result = await azureSearchService.Indexes.DeleteIndexAsync("[index name]");

Index Document Operations

Adding a Document

Adding a document to an index is as simple as calling the CreateDocumentAsync method. This method takes a document as parameter, as well as an action type (which can be either upload, merge or delete). When you add a document you have to remember that a document must inherit from the Document class, which forces the document’s type to implement the following methods and properties:

  • GetFields() – this method should return the list of fields associated to the document type, as defined when the index was first created. Note: since you’ll need this list both at index creation and when you add documents, I find it a best practice to create a static method which returns the list of fields corresponding to the index schema and simply call that method from within the GetFields() implementation
  • this[string fieldName] – this is an index which will in turn be used return the value of a specific document property according to the index schema

Even though the implementation method I chose does require some additional extra-work on your side (implementing these methods) as opposed to using reflection which would have probably been more comfortable, there are two huge advantages to this specific implementation:

  1. Refactor-friendly: in time, you document type might change, meaning that you might need to change the property name, type or the whole list of properties all-toghether. Using this approach, you don’t have to re-create your index from scratch (delete old, create new – remember that you either have a limited number of indexes you can create in your skew, or you get charged by the number of indexes you’ve create) or loose anny of your old documents listed inside an index
  2. Performance – reflection is slow. This approach is 100% reflection free, which makes is super-faster than any other reflection-based implementation

Besides adding the documents one by one, you also get the option of adding a batch of documents from a single shot, using the method called CreateDocumentsAsync.

Example:

// create a list of sample documents
var documentData1 = new SearchableEvent()
{
    Category = "sport",
    Date = new DateTimeOffset(2015, 06, 30, 19, 0, 0, 0, new TimeSpan(2, 0, 0)),
    Description = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. ...
    Location = "MetLife Stadium",
    Name = "Falcons vs. Jaguars",
    Tags = new string[] { "falcons""jaguars""nfl""atlanta""jacksonville" },
    Key = Guid.NewGuid().ToString()
};
var documentData2 = new SearchableEvent()
{
    Category = "sport",
    Date = new DateTimeOffset(2015, 06, 20, 19, 0, 0, 0, new TimeSpan(2, 0, 0)),
    Description = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. ...
    Location = "Lambeau Field",
    Name = "Colts vs. Bengals",
    Tags = new string[] { "colts""bengals""nfl""indianapolis""cincinnati" },
    Key = Guid.NewGuid().ToString()
};
var documentList = new List<SearchableEvent>()
{
    documentData1,
    documentData2,
    ...
    documentData31,
};
var result = await azureSearchService.Indexes["[index name]"].CreateDocumentsAsync(documentList, ActionTypes.Upload);

Query Operations

Search Index

The query operation is probably the operation you’re going to use most. Using the Azure Search Client Library, querying is done simply by calling the QueryAsync method on one of the indexes. As you can already guess, when you query for one or more documents, you actually a single index which contains all the documents. The query results are based on various inputs, among which the search text is the one which shouldn’t miss.

Here’s an example on how you can use the QueryAsync method:

var queryParams = new QueryParameters() { SearchText = “seahawks”, SearchFields = newList<string>() { “name” } };

var searchResult = await azureSearchService.Indexes[“[index name]”].QueryAsync<SearchableEvent>(queryParams);

Document lookup

Document lookup allows you to quick get a loaded document by its key. In order to lookup a document in a specific index, all you have to do is call the LookupAsync function on the desired index like this:

var document = await azureSearchService.Indexes[“[index name]”].Lookup<SearchableEvent>(“[document key]”, newList<string>() { “name” });

 

Considering that this is still a preview service, that the Azure Search Client library is still in beta phase and that further development is ongoing, I’d like to hear from you what you’d like to see next within the library or even the service.

By the time of this writing, the current library version is 0.5.5355.2536 and is available through NuGet here: https://www.nuget.org/packages/AzureSearchClient/0.5.5355.2536.

 

6 Thoughts on “Azure Search Client Library

  1. Hi Alex, did you consider contributing to the existing repo?
    https://github.com/reddog-io/RedDog.Search

  2. Pingback: What Are Scoring Profiles In Azure Search? | Alex's blog

  3. Pingback: What Is A Facet? | Alex's Blog

  4. Manasvi Srivastava on November 6, 2017 at 11:50 am said:

    Can you please help me add the Scoring profile through .net SDK?
    I am trying to do that through C# code but it is not getting reflected on the portal.

    Thank you in advance.

Leave a Reply