Skip to content

feat(retail): add Vertex AI Search for commerce snippets#10252

Draft
alarconesparza wants to merge 4 commits intoGoogleCloudPlatform:mainfrom
alarconesparza:alarconesparza-vertex-ai-search-502616950
Draft

feat(retail): add Vertex AI Search for commerce snippets#10252
alarconesparza wants to merge 4 commits intoGoogleCloudPlatform:mainfrom
alarconesparza:alarconesparza-vertex-ai-search-502616950

Conversation

@alarconesparza
Copy link
Copy Markdown
Contributor

@alarconesparza alarconesparza commented Apr 21, 2026

Description

Fixes internal b/502616950

Note: Before submitting a pull request, please open an issue for discussion if you are not associated with Google.

Checklist

  • I have followed Sample Format Guide
  • pom.xml parent set to latest shared-configuration
  • Appropriate changes to README are included in PR
  • These samples need a new API enabled in testing projects to pass (let us know which ones)
  • These samples need a new/updated env vars in testing projects set to pass (let us know which ones)
  • Tests pass: mvn clean verify required
  • Lint passes: mvn -P lint checkstyle:check required
  • Static Analysis: mvn -P lint clean compile pmd:cpd-check spotbugs:check advisory only
  • This sample adds a new sample directory, and I updated the CODEOWNERS file with the codeowners for this sample
  • This sample adds a new Product API, and I updated the Blunderbuss issue/PR auto-assigner with the codeowners for this sample
  • Please merge this PR for me once it is approved

@product-auto-label product-auto-label Bot added api: retail Issues related to the Retail API API. samples Issues that are directly related to samples. labels Apr 21, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Java code samples for Vertex AI Search for commerce, covering basic search, pagination, and offset functionalities, along with corresponding integration tests. The review feedback identifies several improvement opportunities: using getTotalSize() instead of getResultsCount() to ensure accurate result counts across pages, simplifying API response handling, and removing redundant output in the samples. Additionally, the feedback suggests optimizing resource management in test utilities by reusing service clients instead of recreating them within loops.

.build();
SearchPagedResponse response = searchServiceClient.search(searchRequest);

return response.getPage().getResponse().getResultsCount();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This method currently returns the number of results on the first page (limited by setPageSize(2)). If the number of products to test is greater than 2, the condition in waitForProductsToBeIndexed will never be met, causing potential test failures or unnecessary delays. Use getTotalSize() to get the total count of matching products.

Suggested change
return response.getPage().getResponse().getResultsCount();
return response.getResponse().getTotalSize();

.build();
SearchPagedResponse response = searchServiceClient.search(searchRequest);

SearchResponse searchResponse = response.getPage().getResponse();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The expression response.getPage().getResponse() can be simplified to response.getResponse(). The SearchPagedResponse object provides direct access to the underlying SearchResponse message.

Suggested change
SearchResponse searchResponse = response.getPage().getResponse();
SearchResponse searchResponse = response.getResponse();
References
  1. For code samples, conventions may allow for practices that favor simplicity and clarity.


SearchResponse searchResponse = response.getPage().getResponse();

System.out.println("Found " + searchResponse.getResultsCount() + " results");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using getResultsCount() only returns the number of items on the current page (limited by pageSize). To show the total number of products matching the query across all pages, use getTotalSize() instead.

Suggested change
System.out.println("Found " + searchResponse.getResultsCount() + " results");
System.out.println("Found " + searchResponse.getTotalSize() + " results");
References
  1. For code samples, conventions may allow for practices that favor simplicity and clarity.


SearchResponse searchResponse = response.getPage().getResponse();

System.out.println("Found " + searchResponse.getResultsCount() + " results");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using getResultsCount() returns the number of items in the current response, which is limited by the page size and doesn't reflect the total matches. Use getTotalSize() to display the total number of matching products.

Suggested change
System.out.println("Found " + searchResponse.getResultsCount() + " results");
System.out.println("Found " + searchResponse.getTotalSize() + " results");
References
  1. For code samples, conventions may allow for practices that favor simplicity and clarity.

System.out.println("\nResults of page number " + currentPage + ":");
System.out.println("Found " + page.getResponse().getResultsCount() + " results");
for (SearchResult searchResult : page.getResponse().getResultsList()) {
System.out.println("Search Result: \n" + searchResult);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Printing the entire searchResult protobuf object is redundant and creates very noisy output, especially since the product name is already printed below. Consider removing this line to keep the output clean.

References
  1. For code samples, conventions may allow for practices that favor simplicity and clarity.

Comment on lines +86 to +93
public static void deleteProduct(String productName) throws IOException {
DeleteProductRequest deleteProductRequest =
DeleteProductRequest.newBuilder().setName(productName).build();

try (ProductServiceClient serviceClient = ProductServiceClient.create()) {
serviceClient.deleteProduct(deleteProductRequest);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Creating a new ProductServiceClient for every product deletion is inefficient when cleaning up multiple products in a loop (as seen in SearchIT.tearDown). Consider refactoring this to reuse a single client instance for all deletions.

Comment on lines +95 to +113
public static int amountOfProductsReadyToTest(String projectId, String query) throws IOException {
try (SearchServiceClient searchServiceClient = SearchServiceClient.create()) {
ServingConfigName servingConfigName =
ServingConfigName.of(projectId, "global", "default_catalog", "default_search");
BranchName branchName =
BranchName.of(projectId, "global", "default_catalog", "default_branch");
SearchRequest searchRequest =
SearchRequest.newBuilder()
.setPlacement(servingConfigName.toString())
.setBranch(branchName.toString())
.setVisitorId("test_visitor")
.setQuery(query)
.setPageSize(2)
.build();
SearchPagedResponse response = searchServiceClient.search(searchRequest);

return response.getPage().getResponse().getResultsCount();
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Creating a new SearchServiceClient in every call to amountOfProductsReadyToTest is inefficient, especially since it's called within a loop in waitForProductsToBeIndexed. Consider passing an existing client to this method.

@alarconesparza
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Java code samples and integration tests for Vertex AI Search for commerce, covering basic search, pagination, and offset functionality. The feedback provided focuses on correcting an inaccurate description in the README, optimizing the POM file by leveraging the BOM for dependency management, and improving the efficiency and reliability of test utilities by reusing service clients and extending the indexing wait times to prevent flaky tests.

Comment thread retail/snippets/README.md

## Samples

- **[Search.java](src/main/java/com/example/search/Search.java)**: Basic search request showing both text search and browse search (using categories).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The description for Search.java is inaccurate. It states that the sample shows "both text search and browse search (using categories)", but the implementation in Search.java only demonstrates a basic text search using the query parameter. It does not include any category-based filtering or browsing logic.

Comment thread retail/snippets/pom.xml
Comment on lines +53 to +57
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-retail</artifactId>
<version>2.93.0</version>
</dependency>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since you are using the libraries-bom in the dependencyManagement section, you should omit the explicit version for google-cloud-retail. This allows the BOM to manage the version, ensuring compatibility across different Google Cloud libraries and avoiding potential dependency conflicts.

    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-retail</artifactId>
    </dependency>

DeleteProductRequest deleteProductRequest =
DeleteProductRequest.newBuilder().setName(productName).build();

try (ProductServiceClient serviceClient = ProductServiceClient.create()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Creating a ProductServiceClient for every single product deletion is inefficient. Since this method is called in a loop during test cleanup (e.g., in SearchIT.tearDown), consider refactoring the code to reuse a single client instance for all deletions. While Rule 5 allows for simpler patterns in samples, efficiency in test helpers is important to avoid resource exhaustion.

References
  1. For code samples, conventions may allow for practices that are not production-ready in favor of simplicity and clarity.


public static boolean areProductsReadyToTest(String projectId, String query, int amountOfProducts)
throws IOException {
try (SearchServiceClient searchServiceClient = SearchServiceClient.create()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Creating a SearchServiceClient is an expensive operation. Since areProductsReadyToTest is called repeatedly within a loop in waitForProductsToBeIndexed, creating a new client on every iteration is inefficient. You should create the client once and reuse it. While Rule 5 allows for simpler patterns in samples, efficiency in test helpers is important to avoid resource exhaustion.

References
  1. For code samples, conventions may allow for practices that are not production-ready in favor of simplicity and clarity.

public static void waitForProductsToBeIndexed(
String projectId, List<Product> products, String productTitle)
throws InterruptedException, IOException {
for (int i = 0; i < 3; i++) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current retry logic only attempts to check for indexed products 3 times with a 10-second delay (total 30 seconds). In Vertex AI Search for commerce, indexing new products can often take several minutes. This short timeout is likely to cause flaky tests in real-world scenarios. Consider increasing the number of retries or the wait interval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: retail Issues related to the Retail API API. samples Issues that are directly related to samples.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant