Skip to content

Query by secondary index

In this step you implement the two query methods that power the application UI. You first build a single-condition secondary-index query (query), then a multi-condition query (advancedSearch). Along the way you use Voyager to construct an AEL filter visually and paste it into your code.

The previous step left the products set with 200 records, but the home page still renders the same Oops... looks like that page doesn't exist screen you saw before, because the listing query is unimplemented:

Spring Boot retail application showing the category dropdowns at the top and an Oops error page in place of the product grid

The product grid is populated by the query method, which you implement now. Every code change in this step lives in spring-server/src/main/java/com/aerospikeworkshop/service/KeyValueServiceNewClient.java.

Use Voyager to build an AEL filter

The application defines five secondary indexes on the products set: articleType, subCategory, brandName, usage, and category. The home-page dropdowns issue queries through query(index, filterValue, count). Before writing code, build a sample query in Voyager so you can confirm the result and copy the generated AEL.

  1. In Voyager, drill into the products set and expand any record by selecting Show 15 more bins.

    Voyager showing all bins on a product record after Show 15 more bins is selected

    Note the subCategory bin. You filter on it next.

  2. Select the filter (funnel) icon at the top of the set view.

  3. In the filter dialog, enter subCategory as the field and Shoes as the value, then select Apply.

    Voyager filter dialog with subCategory equals Shoes

    The set view updates to show only products whose subCategory is Shoes.

  4. Open the filter again and look at the bottom of the dialog.

    The dialog shows the corresponding AEL expression: $.subCategory == 'Shoes'.

    Voyager filter dialog with the AEL expression dollar dot subCategory equals quote Shoes quote at the bottom

    AEL is the Aerospike Expression Language. It looks similar to JSONPath and supports both bin values and record metadata such as TTL or record size. You use the visually built expression as a starting point in code.

  5. Select the copy button to the right of the expression.

You can use the same workflow in reverse to debug expressions you write by hand. Whenever you are not sure that an AEL expression is correct, paste it into Voyager’s filter dialog (use the Expression tab to enter AEL directly) and confirm that Voyager returns the rows you expect before you put the expression into Java code.

Implement the single-condition query

  1. In KeyValueServiceNewClient.java, find the query(String index, String filterValue, int count) method.

    The current body returns one hard-coded product:

    List<Product> products = List.of(getProduct("41213").get());
  2. Replace the entire List<Product> products = List.of(getProduct("41213").get()); statement with the following. Delete the original line in full; do not paste the new code on top of part of the old line, or the file no longer compiles.

    List<Product> products = session.query(productDataSet)
    .where("$.subCategory == 'Shoes'")
    .execute()
    .toObjectList(productMapper);

    The chain reads: query the records in productDataSet, filter them with the AEL expression, run the request, and turn the result stream into a List<Product> using productMapper. Leave the existing return new KeyValueServiceInterface.QueryResult(...) line as it is.

  3. Stop the Spring Boot application with Ctrl+C (if it is running), then rebuild and rerun it from the spring-server directory.

    Terminal window
    cd spring-server
    mvn clean package -DskipTests
    mvn spring-boot:run -Dspring-boot.run.profiles=new-client

    Wait for BUILD SUCCESS from the first command before you start the second. Run them one at a time.

  4. Reload http://localhost:8080.

    The home page now shows shoes everywhere because the filter is hard-coded.

    Spring Boot home page with every category populated by shoe products
  5. Replace the entire List<Product> products = ...; statement again, this time with one that uses the index and filterValue parameters.

    List<Product> products = session.query(productDataSet)
    .where("$.%s == '%s'", index, filterValue)
    .readingOnlyBins("id", "name", "images", "brandName", "price")
    .limit(count)
    .execute()
    .toObjectList(productMapper);

    Three changes from the previous version:

    • where accepts a printf-style format string. The SDK substitutes the index name and filter value into the AEL expression.
    • readingOnlyBins returns only the bins the home page needs. Returning fewer bins reduces network and memory pressure when the full record is large.
    • limit(count) caps the number of results when the caller asks for a maximum.
  6. Stop the Spring Boot application with Ctrl+C, then rebuild and rerun it.

    Terminal window
    cd spring-server
    mvn clean package -DskipTests
    mvn spring-boot:run -Dspring-boot.run.profiles=new-client
  7. Reload http://localhost:8080.

    Each category and article-type dropdown now populates with the correct items.

The four dropdowns at the top of the home page (Category, Article Type, Usage, Brand Name) all feed into advancedSearch(...). The method combines up to four bin == value predicates with AND.

The pre-existing helper code at the top of advancedSearch already builds the AEL string from the input parameters and prints it to the log, for example:

AEL: $.category == 'Apparel' and $.usage == 'Sports'

The current body returns a single hard-coded product:

List<Product> products = List.of(getProduct("13283").get());

Use Voyager once more to confirm that the multi-predicate AEL is correct, then write the SDK call.

  1. In Voyager, open the filter dialog and add two filter rows: category equals Footwear, and usage equals Sports. Select Apply.

    Voyager filter dialog with two rows: category equals Footwear and usage equals Sports

    The set view shows only sports footwear, and the dialog displays the combined AEL: $.category == 'Footwear' and $.usage == 'Sports'.

  2. Find advancedSearch in KeyValueServiceNewClient.java and replace the entire List<Product> products = List.of(getProduct("13283").get()); statement with the following. Delete the original line in full first; pasting on top of part of it produces a doubled List<Product> products = List<Product> products = ... that fails to compile with cannot find symbol: variable List.

    List<Product> products = session.query(productDataSet)
    .where(ael)
    .limit(count)
    .execute()
    .toObjectList(productMapper);

    The body is almost identical to the single-condition query. The only differences are that where takes the ael string the helper code already built, and readingOnlyBins is omitted because the search results page renders the full record.

    You did not tell the SDK which secondary index to use, even though up to four of the predicates may have one. The SDK parses the AEL expression, picks the most selective indexed predicate, uses that index for the initial scan, and applies the remaining predicates as a filter expression. This is true whether you wrote the AEL by hand or built it visually in Voyager.

Confirm the queries work

  1. Stop the Spring Boot application with Ctrl+C, then rebuild and rerun it.

    Terminal window
    cd spring-server
    mvn clean package -DskipTests
    mvn spring-boot:run -Dspring-boot.run.profiles=new-client
  2. Reload the home page and try several combinations of category, article type, usage, and brand. The product grid updates to match the selected filters.

  3. Select any product to load its detail page. The page renders the product’s full information.

    Spring Boot product detail page showing a single product with full bins

Outcomes

You now have:

  • A single-condition query method that uses any of the five secondary indexes.
  • A multi-condition advancedSearch method that combines up to four predicates with AND.
  • A working pattern for testing AEL expressions in Voyager before putting them in code.

You cannot yet:

  • Add items to the shopping cart. The cart is empty (after getCart is fixed in the next step) because addToCart is not yet implemented.

In the next step you implement the cart methods, including a check-and-set update on a nested map document.

Feedback

Was this page helpful?

What type of feedback are you giving?

What would you like us to know?

+Capture screenshot

Can we reach out to you?