Skip to content

Commit 393fbd5

Browse files
authored
Add paged search example (#593)
* Add paged search example Change-Id: Iee5e08e5bd8a631f1266ffe63fed1b4eb887e071 * Address Josh comments Change-Id: I1f3bd57def4faa2360563d5d44277f64c7fb037d * Address Josh comments 2 Change-Id: I74cb3658e86c60e6e34c9116ecf2c810edfa95ea
1 parent 7dda56b commit 393fbd5

1 file changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.ads.googleads.examples.misc;
16+
17+
import com.beust.jcommander.Parameter;
18+
import com.google.ads.googleads.examples.utils.ArgumentNames;
19+
import com.google.ads.googleads.examples.utils.CodeSampleParams;
20+
import com.google.ads.googleads.lib.GoogleAdsClient;
21+
import com.google.ads.googleads.v10.errors.GoogleAdsError;
22+
import com.google.ads.googleads.v10.errors.GoogleAdsException;
23+
import com.google.ads.googleads.v10.services.GoogleAdsRow;
24+
import com.google.ads.googleads.v10.services.GoogleAdsServiceClient;
25+
import com.google.ads.googleads.v10.services.GoogleAdsServiceClient.SearchPage;
26+
import com.google.ads.googleads.v10.services.GoogleAdsServiceClient.SearchPagedResponse;
27+
import com.google.ads.googleads.v10.services.SearchGoogleAdsRequest;
28+
import java.io.FileNotFoundException;
29+
import java.io.IOException;
30+
import java.util.SortedMap;
31+
import java.util.TreeMap;
32+
33+
/**
34+
* GoogleAdsService.search results are paginated, but they can only be retrieved in sequence
35+
* starting by the first page. More details at
36+
* https://developers.google.com/google-ads/api/docs/reporting/paging.
37+
*
38+
* <p>This example searches campaigns illustrating how GoogleAdsService.search result page tokens
39+
* can be cached and reused to retrieve previous pages. This is useful when you need to request
40+
* pages that were already requested in the past without starting over from the first page. For
41+
* example, it can be used to implement an interactive application that displays a page of results
42+
* at a time without caching all the results first.
43+
*
44+
* <p>To add campaigns, run {@link com.google.ads.googleads.examples.basicoperations.AddCampaigns}.
45+
*/
46+
public class NavigateSearchResultPagesCachingTokens {
47+
48+
// The maximum number of results to retrieve.
49+
private static final int RESULTS_LIMIT = 10;
50+
// The size of the paginated search result pages.
51+
private static final int PAGE_SIZE = 3;
52+
53+
private static class NavigateSearchResultPagesCachingTokensParams extends CodeSampleParams {
54+
55+
@Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)
56+
private Long customerId;
57+
}
58+
59+
public static void main(String args[]) {
60+
NavigateSearchResultPagesCachingTokensParams params =
61+
new NavigateSearchResultPagesCachingTokensParams();
62+
if (!params.parseArguments(args)) {
63+
// Either pass the required parameters for this example on the command line, or insert them
64+
// into the code here. See the parameter class definition above for descriptions.
65+
params.customerId = Long.parseLong("ENTER_CUSTOMER_ID_HERE");
66+
}
67+
68+
GoogleAdsClient googleAdsClient = null;
69+
try {
70+
googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();
71+
} catch (FileNotFoundException fnfe) {
72+
System.err.printf(
73+
"Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe);
74+
System.exit(1);
75+
} catch (IOException ioe) {
76+
System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe);
77+
System.exit(1);
78+
}
79+
80+
try {
81+
new NavigateSearchResultPagesCachingTokens().runExample(googleAdsClient, params.customerId);
82+
} catch (GoogleAdsException gae) {
83+
// GoogleAdsException is the base class for most exceptions thrown by an API request.
84+
// Instances of this exception have a message and a GoogleAdsFailure that contains a
85+
// collection of GoogleAdsErrors that indicate the underlying causes of the
86+
// GoogleAdsException.
87+
System.err.printf(
88+
"Request ID %s failed due to GoogleAdsException. Underlying errors:%n",
89+
gae.getRequestId());
90+
int i = 0;
91+
for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {
92+
System.err.printf(" Error %d: %s%n", i++, googleAdsError);
93+
}
94+
System.exit(1);
95+
}
96+
}
97+
98+
/**
99+
* Runs this example.
100+
*
101+
* @param googleAdsClient the Google Ads API client.
102+
* @param customerId the client customer ID.
103+
* @throws GoogleAdsException if an API request failed with one or more service errors.
104+
*/
105+
private static void runExample(GoogleAdsClient googleAdsClient, long customerId) {
106+
// The cache of page tokens. It is stored in-memory and in ascendant order of page number.
107+
// The first page's token is always an empty string.
108+
SortedMap<Integer, String> pageTokens = new TreeMap<>();
109+
pageTokens.put(1, "");
110+
111+
// Creates a query that retrieves the campaigns.
112+
String query =
113+
String.format(
114+
"SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.name LIMIT %d",
115+
RESULTS_LIMIT);
116+
117+
// Creates a paginated search request.
118+
SearchGoogleAdsRequest request =
119+
SearchGoogleAdsRequest.newBuilder()
120+
.setCustomerId(Long.toString(customerId))
121+
.setPageSize(PAGE_SIZE)
122+
.setQuery(query)
123+
.setReturnTotalResultsCount(true)
124+
.build();
125+
126+
int totalNumberOfPages;
127+
try (GoogleAdsServiceClient googleAdsServiceClient =
128+
googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
129+
System.out.println("--- 0. Fetching page 1 to get metadata:");
130+
// Issues a paginated search request.
131+
SearchPagedResponse response = googleAdsServiceClient.search(request);
132+
cacheNextPageToken(pageTokens, response.getPage(), 2);
133+
134+
// Determines the total number of results and prints it.
135+
// The total results count does not take into consideration the LIMIT clause of the query,
136+
// so we need to find the minimal value between the limit and the total results count.
137+
long totalNumberOfResults =
138+
Math.min(RESULTS_LIMIT, response.getPage().getResponse().getTotalResultsCount());
139+
System.out.printf("Total number of campaigns found: %d.%n", totalNumberOfResults);
140+
141+
// Determines the total number of pages and prints it.
142+
totalNumberOfPages = (int) Math.ceil(totalNumberOfResults / (double) PAGE_SIZE);
143+
System.out.printf("Total number of pages: %d.%n", totalNumberOfPages);
144+
if (totalNumberOfPages == 0) {
145+
System.out.println("Could not find any campaigns.");
146+
return;
147+
}
148+
}
149+
150+
// Demonstrates how the logic works when iterating pages forward. We select a page that is
151+
// in the middle of the result set so that only a subset of the page tokens will be cached.
152+
int middlePageNumber = (int) Math.ceil(totalNumberOfPages / 2.0);
153+
System.out.printf("--- 1. Printing results of the middle page (page %d):%n", middlePageNumber);
154+
fetchAndPrintPageResults(googleAdsClient, customerId, query, middlePageNumber, pageTokens);
155+
156+
// Demonstrates how the logic works when iterating pages backward with some page tokens that
157+
// are not already cached.
158+
System.out.println("--- 2. Printing results of the last page to the first:");
159+
for (int pageNumber = totalNumberOfPages; pageNumber > 0; pageNumber--) {
160+
System.out.printf("-- Page %d results:%n", pageNumber);
161+
fetchAndPrintPageResults(googleAdsClient, customerId, query, pageNumber, pageTokens);
162+
}
163+
}
164+
165+
// [START navigate_search_result_pages_caching_tokens]
166+
/**
167+
* Fetches and prints the results of a page of a search using a cache of page tokens.
168+
*
169+
* @param googleAdsClient the Google Ads API client.
170+
* @param customerId the client customer ID.
171+
* @param query the search query.
172+
* @param pageNumber the number of the page to fetch and print results for.
173+
* @param pageTokens the cache of page tokens to use and update.
174+
*/
175+
private static void fetchAndPrintPageResults(
176+
GoogleAdsClient googleAdsClient,
177+
long customerId,
178+
String query,
179+
int pageNumber,
180+
SortedMap<Integer, String> pageTokens) {
181+
int currentPageNumber;
182+
// There is no need to fetch the pages we already know the page tokens for.
183+
if (pageTokens.containsKey(pageNumber)) {
184+
System.out.println(
185+
"The token of the requested page was cached, we will use it to get the results.");
186+
currentPageNumber = pageNumber;
187+
} else {
188+
System.out.printf(
189+
"The token of the requested page was never cached, we will use the closest page we know"
190+
+ " the token for (page %d) and sequentially get pages from there.%n",
191+
pageTokens.size());
192+
currentPageNumber = pageTokens.lastKey();
193+
}
194+
195+
// Fetches next pages in sequence and caches their tokens until the requested page results
196+
// are returned.
197+
while (currentPageNumber <= pageNumber) {
198+
// Fetches the next page.
199+
System.out.printf("Fetching page %d...%n", currentPageNumber);
200+
SearchGoogleAdsRequest request =
201+
SearchGoogleAdsRequest.newBuilder()
202+
.setCustomerId(Long.toString(customerId))
203+
.setPageSize(PAGE_SIZE)
204+
.setQuery(query)
205+
.setReturnTotalResultsCount(true)
206+
// Uses the page token cached for the current page number.
207+
.setPageToken(pageTokens.get(currentPageNumber))
208+
.build();
209+
try (GoogleAdsServiceClient googleAdsServiceClient =
210+
googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
211+
SearchPagedResponse response = googleAdsServiceClient.search(request);
212+
cacheNextPageToken(pageTokens, response.getPage(), currentPageNumber + 1);
213+
214+
// Prints only the results for the requested page.
215+
if (currentPageNumber == pageNumber) {
216+
// Prints the results of the requested page.
217+
System.out.printf("Printing results found for page %d:%n", pageNumber);
218+
for (GoogleAdsRow googleAdsRow : response.getPage().getResponse().getResultsList()) {
219+
System.out.printf(
220+
"- Campaign with ID %d and name '%s'.%n",
221+
googleAdsRow.getCampaign().getId(), googleAdsRow.getCampaign().getName());
222+
}
223+
}
224+
225+
currentPageNumber++;
226+
}
227+
}
228+
}
229+
// [END navigate_search_result_pages_caching_tokens]
230+
231+
/**
232+
* Updates the cache of page tokens based on a page that was retrieved.
233+
*
234+
* @param pageTokens the cache of page tokens to update.
235+
* @param page the page that was retrieved.
236+
* @param pageNumber the number of the page the cached token will retrieve.
237+
*/
238+
private static void cacheNextPageToken(
239+
SortedMap<Integer, String> pageTokens, SearchPage page, int pageNumber) {
240+
if (page.hasNextPage() && !pageTokens.containsKey(pageNumber)) {
241+
// Updates the cache with the next page token if it is not set yet.
242+
pageTokens.put(pageNumber, page.getNextPageToken());
243+
System.out.printf("Cached token for page %d.%n", pageNumber);
244+
}
245+
}
246+
}

0 commit comments

Comments
 (0)