This is the documentation for Recommendations AI, Retail Search, and the new Retail console.

Filter and order results

This page describes filtering and ordering with Retail Search.

Filtering tutorial

This tutorial shows you some examples of product filtering.


For step-by-step guidance on this task directly in Cloud Shell Editor, click Guide me:

Guide me


The following sections take you through the same steps as clicking Guide me.

Ordering tutorial

This tutorial shows you how to order items in a search response.


For step-by-step guidance on this task directly in Cloud Shell Editor, click Guide me:

Guide me


The following sections take you through the same steps as clicking Guide me.

Example dataset

This page uses the following dataset as an example. Only the fields necessary for the example are included.

Filter

Java

public static SearchResponse searchFilteredProducts(String query, int pageSize,
    String filter) throws IOException, InterruptedException {
  SearchServiceClient searchClient = getSearchServiceClient();

  SearchRequest searchRequest = SearchRequest.newBuilder()
      .setPlacement(DEFAULT_SEARCH_PLACEMENT_NAME)
  .setBranch(DEFAULT_BRANCH_NAME)
      .setVisitorId(VISITOR_ID)
      .setQuery(query)
      .setPageSize(pageSize)
      .setFilter(filter)
      .build();

  SearchResponse response = searchClient.search(searchRequest).getPage().getResponse();

  searchClient.shutdownNow();
  searchClient.awaitTermination(2, TimeUnit.SECONDS);

  return response;
}

The filter expression syntax can be summarized by the following EBNF:

  # A single expression or multiple expressions that are joint by "AND" or "OR".
  filter = expression, { " AND " | "OR", expression };

  # A expressions can be prefixed with "-" or "NOT" to express a negation.
  expression = [ "-" | "NOT " ],
                    # A parenthesized expression
                    | "(", expression, ")"
                    # A simple expression applying to a textual field.
                    # Function "ANY" returns true if the field contains any of the literals.
                    ( textual_field, ":", "ANY", "(", literal, { ",", literal }, ")"
                    # A simple expression applying to a numerical field. Function "IN" returns true
                    # if a field value is within the range. By default, lower_bound is inclusive and
                    # upper_bound is exclusive.
                    | numerical_field, ":", "IN", "(", lower_bound, ",", upper_bound, ")"
                    # A simple expression applying to a numerical field and compares with a double value.
                    | numerical_field, comparison, double );

  # A lower_bound is either a double, or "*" which represents negative infinity.
  # Specify inclusive bound with character 'i' for inclusive or exclusive bound
  # with character 'e', explicitly.
  lower_bound = ( double, [ "e" | "i" ] ) | "*";

  # An upper_bound is either a double, or "*" which represents infinity.
  # Specify inclusive bound with character 'i' for inclusive or exclusive bound
  # with character 'e', explicitly.
  upper_bound = ( double, [ "e" | "i" ] ) | "*";

  # Supported comparison operators.
  comparison = "<=" | "<" | ">=" | ">" | "=";

  # A literal is any double quoted string. You must escape backslash (\) and
  # quote (") characters.
  literal = double quoted string;

  textual_field = see the table below;

  numerical_field = see the table below;

The supported textual fields are summarized in the following table.

field description
"productId" The product ID (the last segment of Product.name).
"brands" The Product.brands.
"categories" The Product.categories.
"genders" The Audience.genders.
"ageGroups" The Audience.age_groups.
"availability" The Product.availability. Value is one of "IN_STOCK", "OUT_OF_STOCK", PREORDER", "BACKORDER".
"colorFamilies" The ColorInfo.color_families.
"colors" The ColorInfo.colors.
"sizes" The Product.sizes.
"materials" The Product.materials.
"patterns" The Product.patterns.
"conditions" The Product.conditions.
"attributes.key" The textual custom attribute in Product object. Key can be any key in the Product.attributes map, if the attribute values are textual.
"pickupInStore" The FulfillmentInfo.place_ids for type "pickup-in-store".
"shipToStore" The FulfillmentInfo.place_ids for type "ship-to-store".
"sameDayDelivery" The FulfillmentInfo.place_ids for type "same-day-delivery".
"nextDayDelivery" The FulfillmentInfo.place_ids for type "next-day-delivery".
"customFulfillment1" The FulfillmentInfo.place_ids for type "custom-type-1".
"customFulfillment2" The FulfillmentInfo.place_ids for type "custom-type-2".
"customFulfillment3" The FulfillmentInfo.place_ids for type "custom-type-3".
"customFulfillment4" The FulfillmentInfo.place_ids for type "custom-type-4".
"customFulfillment5" The FulfillmentInfo.place_ids for type "custom-type-5".
"inventory(place_id,attributes.key)" The textual custom attribute in Inventory.

The supported numerical fields are summarized in the following table.

field description
"price" The PriceInfo.price.
"discount" The discount. Computed by (original_price - price) / original_price.
"rating" The Rating.average_rating.
"ratingCount" The Rating.rating_count.
"attributes.key" The numerical custom attribute in Product object. Key can be any key in the Product.attributes map, if the attribute values are numerical.
"inventory(place_id,price)" The inventory price.
"inventory(place_id,original_price)" The original inventory price.
"inventory(place_id,attributes.key)" The numerical custom attribute in Inventory.

At most, 10 nested conjunctions or disjunctions are allowed.

For example, to search for a Google product in the following situations respectively, you could set query as "Google" and set filter as the values shown in the following table:

scenario filter
not a Pixel accessory "NOT categories: ANY(\"Pixel > featured accessories\")"
"cheaper than 100 dollars" "price: IN(*, 100.0e)"
"Nest speaker not cheaper than 80 dollars" "(categories: ANY(\"Nest > speakers and displays\")) AND (price: IN(80.0i, *))"

Order

Java

public static SearchResponse searchOrderedProducts(String query, int pageSize,
    String orderBy) throws IOException, InterruptedException {
  SearchServiceClient searchClient = getSearchServiceClient();

  SearchRequest searchRequest = SearchRequest.newBuilder()
      .setPlacement(DEFAULT_SEARCH_PLACEMENT_NAME)
      .setBranch(DEFAULT_BRANCH_NAME)
      .setVisitorId(VISITOR_ID)
      .setQuery(query)
      .setPageSize(pageSize)
      .setOrderBy(orderBy)
      .build();

  SearchResponse response = searchClient.search(searchRequest).getPage().getResponse();

  searchClient.shutdownNow();
  searchClient.awaitTermination(2, TimeUnit.SECONDS);

  return response;
}

The supported orderable fields are summarized in the following table.

field description
"productId" The product ID (the last segment of Product.name).
"title" The Product.title.
"brands" The Product.brands.
"categories" The Product.categories.
"genders" The Audience.genders.
"ageGroups" The Audience.age_groups.
"price" The PriceInfo.price.
"discount" The discount. Computed by (original_price - price) / price.
"rating" The Rating.average_rating.
"ratingCount" The Rating.rating_count.
"attributes.key" The custom attribute in Product object. Key can be any key in the Product.attributes map.
"inventory(place_id,price)" The inventory price.
"inventory(place_id,original_price)" The original inventory price.
"inventory(place_id,attributes.key)" The numerical or textual custom attribute in Inventory.

By default, the order is ascending. Descending order can be specify by "desc" suffix, such as, "rating desc".

Ordering by multiple fields is supported through the use of comma-separated fields in order of priority. The lower priority fields are used to order items with equal values for higher priority fields. For example, "rating desc, price" orders items with the same rating by price.