DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Building REST API Backend Easily With Ballerina Language
  • Composite Requests in Salesforce Are a Great Idea
  • Creating a Secure REST API in Node.js

Trending

  • Building Resilient Networks: Limiting the Risk and Scope of Cyber Attacks
  • Building Resilient Identity Systems: Lessons from Securing Billions of Authentication Requests
  • Unlocking Data with Language: Real-World Applications of Text-to-SQL Interfaces
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit on Kubernetes (Part 3)
  1. DZone
  2. Data Engineering
  3. Databases
  4. Writing Simple REST Clients for Azure Search

Writing Simple REST Clients for Azure Search

How to write a REST client to call Azure Search in Java.

By 
Gunnar Peipman user avatar
Gunnar Peipman
·
Jul. 20, 16 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
5.3K Views

Join the DZone community and get the full member experience.

Join For Free

in my last blog post about rest clients " why azure rest apis and how to prepare for using them? " i explained how to write a simple base class for azure rest apis. in this post, we will implement a simple rest client for azure search. this is the same code that i'm using in my beer store sample application. i also make some notes about how to use search clients in domain models.

planning search indexes is important part of successful search in every system. to get started with planning of search indexes on azure search, feel free to read my blog post planning and creating azure search indexes that uses same product catalogue stuff that i use here.

getting started with the azure search client

first, let's focus on some azure search specifics. to make requests to azure search rest api end-points we need the following information to be available for our client:

  • api version : the version of api we are consuming (if we don’t specify correct version then we cannot be sure if api interface is the one we expect).
  • service name : name of our search service, part of api url.
  • admin key : this is the key to validate that request is coming from valid source.

the code that uses our azure search rest client must provide these values to us because our rest client does'’t deal with configuration settings.

we'll use the rest client base class from my blog post " why azure rest apis and how to prepare for using them? " in its most primitive form, our rest client for azure search looks like this:

public class restclient : restclientbase
{
    private const string _apiversion = "2015-02-28";
    private readonly string _adminkey;
    private readonly string _servicename;

    public restclient(string servicename, string adminkey)
    {
        _servicename = servicename;
        _adminkey = adminkey;
        }

    public override void addheaders(httprequestheaders headers)
    {
        headers.add("api-key", _adminkey);
        }
    }


the addheaders() method is defined in the base class. with this method, we add search-specific headers to the headers collection. this method is called by the base class when it builds http requests for search service calls. the api version and service name are used in search methods later.

updating product index

search is only useful when there is data in search index and therefore we start with product indexing. we create data transfer object (dto) for search index update. by structure this dto is simple  – it has product properties and some additional properties to tell azure search what product it is and what we want to do with it.

[datacontract]
public class productupdatedto
{
    [datamember(name = "@search.action")]
    public string searchaction { get; set; }

    [datamember(name = "id")]
    public string id { get; set; }

    [datamember]
    public string name { get; set; }

    [datamember]
    public string shortdescription { get; set; }

    [datamember]
    public string description { get; set; }

    [datamember]
    public decimal price { get; set; }

    [datamember]
    public int productcategoryid { get; set; }

    [datamember]
    public double? alcvol { get; set; }

    [datamember]
    public string imageurl { get; set; }

    [datamember]
    public string[] alcvoltags { get; set; }

    [datamember]
    public string manufacturername { get; set; }

    public productupdatedto()
    {
        alcvoltags = new string[] { };
        }
    }


and here is the rest client method that sends products to the search index:

public async task<indexitemsresponse> indexproduct(productupdatedto dto)
{
    var url = string.format("https://{0}.search.windows.net/indexes/products/docs/index", _servicename);
    url += "?api-version=" + _apiversion;

    var producttosave = new { value = new productupdatedto[] { dto } };
    var productjson = jsonconvert.serializeobject(producttosave);
    var result = await download<indexitemsresponse>(url, httpmethod.post, productjson);
    return result;
    }


one thing to notice: search api methods that update or maintain indexes will also return a response with data when called. when updating an index, we can update multiple items with the same call, and this is why the response has multiple response values. to represent it in object-oriented code we need two classes — one for response object and one for each products indexing result:

[datacontract]
public class indexitemsresponse
{
    [datamember(name = "value")]
    public indexitemresponse[] items { get; set; }
    }

[datacontract]
public class indexitemresponse
{
    [datamember(name = "key")]
    public string key { get; set; }

    [datamember(name = "status")]
    public bool status { get; set; }

    [datamember(name = "errormessage")]
    public string errormessage { get; set; }
    }


now we have an azure search client with one method that indexes products. i skipped some architectural stuff here to keep the post smaller. in a real scenario, you should have some domain-level service interface for searches and at least one implementation class. this class knows the specifics of a given search client and mapping data from domain classes to search dtos also happens there.

search service interface and implementations

also, you can have more than one implementation if you introduce a search service interface. if you have to switch from one search service to another, then you just have to write a new implementation and make minor modifications to your dependency injection code.

searching from product index

now let's implement searching. we are using same request-and-response interface classes as before, but now we create classes for search. we'll start with a query class. the only purpose of this class is to move query parameters from the client layers to domain services and data layers. i also define abstract query base class because some things are common for almost all queries in a system.

public abstract class basequery
{
    public int page { get; set; }
    public int pagesize { get; set; }
    public string orderby { get; set; }
    public bool ascending { get; set; }

    public basequery()
    {
        page = 1;
        pagesize = 10;
        ascending = true;
        }
    }

public class productquery : basequery
{
    public int? productcategoryid { get; set; }
    public int? producttypeid { get; set; }

    public string term { get; set; }
    public int? alcvolmin { get; set; }
    public int? alcvolmax { get; set; }
    public decimal? minprice { get; set; }
    public decimal? maxprice { get; set; }
    }


as you can see, all queries inheriting from the basequery class have some paging and ordering readiness.

usually, we have some model classes or dtos that are easier to use in client layers, and we often want client layers to communicate with the service layer — where use cases are implemented. here is an example:

public class productdto
{
    public int id { get; set; }
    public string name { get; set; }
    public string shortdescription { get; set; }
    public string description { get; set; }
    public int manufacturerid { get; set; }
    public string manufacturername { get; set; }
    public decimal price { get; set; }
    public int productcategoryid { get; set; }
    public string productcategoryname { get; set; }
    public string imageurl { get; set; }
    public int producttypeid { get; set; }
    public string producttypename { get; set; }
    public bool hasimage { get; set; }
    }


but search response contains some general data, not just an array of items found. for example, if we want to implement paging correctly, then we need to also ask for the total number of results. it's a common attribute that is independent from the results returned. for this, we will define productsearchresult class.

[datacontract]
public class productsearchresponse
{
    [datamember(name = "@odata.count")]
    public int count;

    [datamember(name = "value")]
    public list<productdto> values;
    }


now we have everything we need to make a call to the search service. we add new public method for this to our azure search rest client.


public async task<productsearchresponse> searchproducts(productquery query)
{
    var url = string.format("https://{0}.search.windows.net/indexes/products/docs/", _servicename);
    url += "?api-version=" + _apiversion + "&search=" + webutility.urlencode(query.term);

    var skip = (query.page - 1) * query.pagesize;
    var take = query.pagesize;
    string filter = buildfilter(query);

    if (!string.isnullorempty(filter))
        url += "&$filter=" + webutility.urlencode(filter);

    if (skip > 0)
        url += "&$skip=" + skip;

    url += "&$take=" + take;
    url += "&$count=true";

    var result = await download<productsearchresponse>(url);
    return result;
    }

private static string buildfilter(productquery query)
{
    var filter = "";

    if (query.alcvolmax.hasvalue)
        filter += "alcvol le " + query.alcvolmax;

    if (query.alcvolmin.hasvalue)
    {
        if (!string.isnullorempty(filter))
            filter += " and ";

        filter += "alcvol ge " + query.alcvolmin;
        }

    // apply more filters here

    return filter;
    }


now our rest client supports also searching and paging.

although azure search service can return many properties for a given entity, it is still possible that you have to query the entity later from the database to get another set of properties that are not available in the search index. this kind of "materialization" happens in domain search service classes that hide all those dirty details from calling code.

wrapping up

using azure search without ready-made client classes is not very complex. we wrote a simple rest client that builds calls to azure search service and returns dtos known to our domain. i admit that perhaps we got too many classes for this simple thing, but we still have a valid object model we can use. of course, all search-related classes can be extended by adding more search properties. i also admit that domain part of this story may be a little too general and it's not very easy to catch on for everybody, but those who know domain model world better should understand how to interface the code with their systems. other guys can just take the search client code and roll with it. as a conclusion, we can say that azure search is easy also without ready-made api packages.

azure Database REST Web Protocols

Published at DZone with permission of Gunnar Peipman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • Building REST API Backend Easily With Ballerina Language
  • Composite Requests in Salesforce Are a Great Idea
  • Creating a Secure REST API in Node.js

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!