Build A Simple Java Console App


Build A Simple Java Console App

Previously we used the Azure Portal’s Data Explorer to query an Azure Cosmos DB container. You are now going to use the Java SDK to issue similar queries.

If this is your first lab and you have not already completed the setup for the lab content see the instructions for Account Setup before starting this lab.

Open the CosmosLabs Maven Project Template

  1. Open Visual Studio Code.

  2. If you are completing this lab through Microsoft Hands-on Labs, the CosmosLabs folder will be located at the path: your\home\directory\Documents\CosmosLabs. In Visual Studio Code, go to File > Open Folder > to get an Open Folder dialog and and use the dialog to open the CosmosLabs folder.

    Open with Visual Studio Code

  3. To see where Java source for the Labs is located, go to Visual Studio Code and click the Explorer pane. Expand the contents of the CosmosLabs folder in Visual Studio Code; expand down to src\main\java\com\azure\cosmos\handsonlabs\common\datatypes\ and double-check that you still have datatype Java files for use in the Labs.

    Expand datatypes in Visual Studio Code

  4. Looking now two directories above, expand the handsonlabs\lab05\ folder. This directory is where you will develop code for this Lab. You should see only a Lab05Main.java file - this is the main class for the project.

  5. Open Lab05Main.java in the editor by clicking on it in the Explorer pane.

    Open Lab05Main.java in editor

  6. In the Visual Studio Code window, in the Explorer pane, right-click the empty space in pane and choose the Open in Terminal menu option.

    Open in terminal

  7. Let’s start by building the template code. In the open terminal pane, enter and execute the following command:

     mvn clean package
    

    This command will build the console project.

  8. Click the 🗙 symbol to close the terminal pane.

  9. For the endpointUri variable, replace the placeholder value with the URI value and for the primaryKey variable, replace the placeholder value with the PRIMARY KEY value from your Azure Cosmos DB account. Use these instructions to get these values if you do not already have them:

    For example, if your uri is https://cosmosacct.documents.azure.com:443/, your new variable assignment will look like this: private static String endpointUri = "https://cosmosacct.documents.azure.com:443/";.

    For example, if your primary key is elzirrKCnXlacvh1CRAnQdYVbVLspmYHQyYrhx0PltHi8wn5lHVHFnd1Xm3ad5cn4TUcH4U0MSeHsVykkFPHpQ==, your new variable assignment will look like this: private static String primaryKey = "elzirrKCnXlacvh1CRAnQdYVbVLspmYHQyYrhx0PltHi8wn5lHVHFnd1Xm3ad5cn4TUcH4U0MSeHsVykkFPHpQ==";.

    We are now going to implement a sample query to make sure our client connection code works.

Read a single Document in Azure Cosmos DB Using readItem()

readItem() allows a single item to be retrieved from Cosmos DB by its ID. In Azure Cosmos DB, this is the most efficient method of reading a single document.

  1. Locate the client-create/client-close block within the main method:

     CosmosAsyncClient client = new CosmosClientBuilder()
                 .endpoint(endpointUri)
                 .key(primaryKey)
                 .consistencyLevel(ConsistencyLevel.EVENTUAL)
                 .contentResponseOnWriteEnabled(true)
                 .buildAsyncClient();
    
     database = client.getDatabase("NutritionDatabase");
     container = database.getContainer("FoodCollection");            
    
     client.close();
    
  2. Add the following lines of code to use the readItem function to retrieve a single item from your Cosmos DB by its id and write its description to the console.

     container.readItem("19130", new PartitionKey("Sweets"), Food.class)
         .flatMap(candyResponse -> {
         Food candy = candyResponse.getItem();
         logger.info("Read {}",candy.getDescription());
         return Mono.empty();
     }).block();
    
  3. Save all of your open editor tabs.

  4. In the Explorer pane, right-click Lab05Main.java and choose the Run menu option.

    Run Lab05Main.java

    This command will build and execute the console project.

  5. Within the logger output to the terminal, you should see the following line output in the console, indicating that readItem completed successfully:

    Read Candies, HERSHEY''S POT OF GOLD Almond Bar
    
  6. Click the 🗙 symbol to close the terminal pane.

Execute a Query Against a Single Azure Cosmos DB Partition

  1. Find the last block of code you wrote

     container.readItem("19130", new PartitionKey("Sweets"), Food.class)
             .flatMap(candyResponse -> {
             Food candy = candyResponse.getItem();
             logger.info("Read {}",candy.getDescription());
             return Mono.empty();
     }).block();
        
     // <== New code goes here
    

    and add this section’s code after the block above.

  2. Define the following SQL Query string:

     String sqlA = "SELECT f.description, f.manufacturerName, f.servings FROM foods f WHERE f.foodGroup = 'Sweets' and IS_DEFINED(f.description) and IS_DEFINED(f.manufacturerName) and IS_DEFINED(f.servings)";
    

    In the following section we will run this query against the nutrition dataset. This query will select all food where the foodGroup is set to the value Sweets. It will also only select documents that have description, manufacturerName, and servings properties defined. You’ll note that the syntax is very familiar if you’ve done work with SQL before. Also note that because this query has the partition key in the WHERE clause, this query can execute within a single partition.

  3. Add the following code to execute the query and page through the Flux<FeedResponse<Food>> which is returned:

     CosmosQueryRequestOptions optionsA = new CosmosQueryRequestOptions();
     optionsA.setMaxDegreeOfParallelism(1);
     container.queryItems(sqlA, optionsA, Food.class).byPage()
             .flatMap(page -> {
    
             return Mono.empty();
     }).blockLast();    
    

    Notice that the Reactive Stream operation starting with .flatMap(page -> { is empty, so really nothing is being done with the query response pages. Above return Mono.empty();, add the following lines to process the query response pages:

     for (Food fd : page.getResults()) {
         String msg="";
         msg = String.format("%s by %s\n",fd.getDescription(),fd.getManufacturerName());
    
         for (Serving sv : fd.getServings()) {
             msg += String.format("\t%f %s\n",sv.getAmount(),sv.getDescription());
         }
         msg += "\n";
         logger.info(msg);
     }
    

    Your code for the query should now look like this:

     String sqlA = "SELECT f.description, f.manufacturerName, " + 
                     "f.servings FROM foods f WHERE f.foodGroup = " + 
                     "'Sweets' and IS_DEFINED(f.description) and " + 
                     "IS_DEFINED(f.manufacturerName) and IS_DEFINED(f.servings)";
    
     CosmosQueryRequestOptions optionsA = new CosmosQueryRequestOptions();
     optionsA.setMaxDegreeOfParallelism(1);
     container.queryItems(sqlA, optionsA, Food.class).byPage()
             .flatMap(page -> {
             for (Food fd : page.getResults()) {
                 String msg="";
                 msg = String.format("%s by %s\n",fd.getDescription(),fd.getManufacturerName());
    
                 for (Serving sv : fd.getServings()) {
                     msg += String.format("\t%f %s\n",sv.getAmount(),sv.getDescription());
                 }
                 msg += "\n";
                 logger.info(msg);
             }
    
             return Mono.empty();
     }).blockLast();
    
  4. Save all of your open editor tabs.

  5. In the Explorer pane, right-click Lab05Main.java and choose the Run menu option.

  6. The code will loop through each result of the SQL query. Within the logger output in the terminal, you should see a message similar to the following:

     ...
    
     Puddings, coconut cream, dry mix, instant by
         1 package (3.5 oz)
         1 portion, amount to make 1/2 cup
    
     ...
    
  7. Click the 🗙 symbol to close the terminal pane.

Execute a Query Against Multiple Azure Cosmos DB Partitions

  1. In this section we will run another query. Duplicate your query code from the last section as shown below:

     String sqlA = "SELECT f.description, f.manufacturerName, " + 
                     "f.servings FROM foods f WHERE f.foodGroup = " + 
                     "'Sweets' and IS_DEFINED(f.description) and " + 
                     "IS_DEFINED(f.manufacturerName) and IS_DEFINED(f.servings)";
    
     CosmosQueryRequestOptions optionsA = new CosmosQueryRequestOptions();
     optionsA.setMaxDegreeOfParallelism(1);
     container.queryItems(sqlA, optionsA, Food.class).byPage()
             .flatMap(page -> {
             for (Food fd : page.getResults()) {
                 String msg="";
                 msg = String.format("%s by %s\n",fd.getDescription(),fd.getManufacturerName());
    
                 for (Serving sv : fd.getServings()) {
                     msg += String.format("\t%f %s\n",sv.getAmount(),sv.getDescription());
                 }
                 msg += "\n";
                 logger.info(msg);
             }
    
             return Mono.empty();
     }).blockLast();
    
     String sqlA = "SELECT f.description, f.manufacturerName, " + 
                     "f.servings FROM foods f WHERE f.foodGroup = " + 
                     "'Sweets' and IS_DEFINED(f.description) and " + 
                     "IS_DEFINED(f.manufacturerName) and IS_DEFINED(f.servings)";
    
     CosmosQueryRequestOptions optionsA = new CosmosQueryRequestOptions();
     optionsA.setMaxDegreeOfParallelism(1);
     container.queryItems(sqlA, optionsA, Food.class).byPage()
             .flatMap(page -> {
             for (Food fd : page.getResults()) {
                 String msg="";
                 msg = String.format("%s by %s\n",fd.getDescription(),fd.getManufacturerName());
    
                 for (Serving sv : fd.getServings()) {
                     msg += String.format("\t%f %s\n",sv.getAmount(),sv.getDescription());
                 }
                 msg += "\n";
                 logger.info(msg);
             }
    
             return Mono.empty();
     }).blockLast();    
    

    In the next section we will change this code so that it does not cause errors; for the moment you may see some errors in your IDE.

  2. Within the duplicate query code find where you define String sqlA and replace it with a new query String sqlB as shown below:

     string sqlB = "SELECT f.id, f.description, f.manufacturerName, f.servings FROM foods f WHERE IS_DEFINED(f.manufacturerName)";
    
  3. For this query, we will run with greater concurrency and allow a max item count of 100. Find this section of the duplicate query code

     CosmosQueryRequestOptions optionsA = new CosmosQueryRequestOptions();
     optionsA.setMaxDegreeOfParallelism(1);
     container.queryItems(sqlA, optionsA, Food.class).byPage()
    

    and replace it with

     CosmosQueryRequestOptions optionsB = new CosmosQueryRequestOptions();
     optionsB.setMaxDegreeOfParallelism(5);
     container.queryItems(sqlB, optionsB, Food.class).byPage(100)
    

    Take note of the differences in this call to queryItems as compared to the previous section. maxDegreeOfParallelism is set to 5 and we are limiting the maxItemCount to 100 items. This will result in paging if there are more than 100 items that match the query.

  4. Now we will look at handling the paged response to this query. First, find the variable declarations at the top of the Lab05Main class definition and add another one:

     private static AtomicInteger pageCount = new AtomicInteger(0);
    

    which creates a thread-safe page count variable initialized to zero.

  5. In the duplicate query code find the portion which pages through the query response

     for (Food fd : page.getResults()) {
         String msg="";
         msg = String.format("%s by %s\n",fd.getDescription(),fd.getManufacturerName());
    
         for (Serving sv : fd.getServings()) {
             msg += String.format("\t%f %s\n",sv.getAmount(),sv.getDescription());
         }
         msg += "\n";
         logger.info(msg);
     } 
    

    and modify it to read as follows:

     String msg="";
    
     msg = String.format("---Page %d---\n",pageCount.getAndIncrement());
    
     for (Food fd : page.getResults()) {
         msg += String.format("\t[%s]\t%s\t%s\n",fd.getId(),fd.getDescription(),fd.getManufacturerName());
     }
     logger.info(msg);    
    
  6. Save all of your open editor tabs.

  7. In the Explorer pane, right-click Lab05Main.java and choose the Run menu option.

  8. Within the logger output, you should see a number of new results, each separated by the a line indicating the page, as follows:

         ---Page #0016---
         [19065] Candies,ALMOND JOY Candy Bar   Hershey Food Corp.
         [19067] Candies, TWIZZLERS CHERRY BITES Hershey Food Corp.        
    

Note that the results are coming from multiple partitions.

  1. Click the 🗙 symbol to close the terminal pane.

If this is your final lab, follow the steps in Removing Lab Assets to remove all lab resources.