// index and query have already been defined ... try{Results<ScoredDocument>result=index.search(query);longtotalMatches=result.getNumberFound();intnumberOfDocsReturned=result.getNumberReturned();Collection<ScoredDocument>listOfDocs=result.getResults();}catch(SearchExceptione){// handle exception...}
// index and query have already been defined ...Results<ScoredDocument>result=index.search(query);// Grab the collection for later use:Collection<ScoredDocument>theDocs=result.getResults();// Alternatively, iterate over the results directly:for(ScoredDocumentdoc:result){// do work}
// index and queryString have already been definedtry{intnumberRetrieved=0;intoffset=0;do{// build options and queryQueryOptionsoptions=QueryOptions.newBuilder().setOffset(offset).build();Queryquery=Query.newBuilder().setOptions(options).build(queryString);// search at least onceResults<ScoredDocument>result=index.search(query);numberRetrieved=result.getNumberReturned();if(numberRetrieved > 0){offset+=numberRetrieved;// process the matched docs}}while(numberRetrieved > 0);}catch(SearchExceptione){// handle exception...}
// index and queryString have already been definedtry{// create the initial cursorCursorcursor=Cursor.newBuilder().build();do{// build options and queryQueryOptionsoptions=QueryOptions.newBuilder().setCursor(cursor).build();Queryquery=Query.newBuilder().setOptions(options).build(queryString);// search at least onceResults<ScoredDocument>result=index.search(query);intnumberRetrieved=result.getNumberReturned();cursor=result.getCursor();if(numberRetrieved > 0){// process the matched docs}}while(cursor!=null);// all done!}catch(SearchExceptione){// handle exception...}
// index and queryString have already been definedtry{// create an initial per-result cursorCursorcursor=Cursor.newBuilder().setPerResult(true).build();// build options and queryQueryOptionsoptions=QueryOptions.newBuilder().setCursor(cursor).build();Queryquery=Query.newBuilder().setOptions(options).build(queryString);Results<ScoredDocument>result=index.search(query);// process the matched docscursor=null;for(ScoredDocumentdoc:result){// discover some document of interest and grab its cursorif(...)cursor=doc.getCursor();}// Start the next search from the document of interestif(cursor!=null){options=QueryOptions.newBuilder().setCursor(cursor).build();query=Query.newBuilder().setOptions(options).build(queryString);result=index.search(query);}}catch(SearchExceptione){// handle exception}
保存和恢复游标
您可以将游标序列化为一个 Web 安全字符串,然后进行保护和恢复以供日后使用:
StringcursorString=cursor.toWebSafeString();// Save the string ... and restore:Cursorcursor=Cursor.newBuilder().build(cursorString));
[[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["很难理解","hardToUnderstand","thumb-down"],["信息或示例代码不正确","incorrectInformationOrSampleCode","thumb-down"],["没有我需要的信息/示例","missingTheInformationSamplesINeed","thumb-down"],["翻译问题","translationIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-08-21。"],[[["\u003cp\u003eA completed query returns a \u003ccode\u003eResults\u003c/code\u003e object, indicating the total matching documents and the number returned, including a collection of \u003ccode\u003eScoredDocuments\u003c/code\u003e.\u003c/p\u003e\n"],["\u003cp\u003eThe number of documents returned might be less than the total found, limited by the query's \u003ccode\u003elimit\u003c/code\u003e option, with a maximum of 10,000 matching documents per \u003ccode\u003esearch()\u003c/code\u003e call.\u003c/p\u003e\n"],["\u003cp\u003e\u003ccode\u003eScoredDocuments\u003c/code\u003e can be accessed through \u003ccode\u003egetResults()\u003c/code\u003e or by iterating directly over the search results, containing either all or a subset of the original document fields, based on \u003ccode\u003esetFieldsToReturn\u003c/code\u003e.\u003c/p\u003e\n"],["\u003cp\u003eTo retrieve all matching documents beyond the initial limit, repeat the search using either an offset, which is less efficient for large datasets, or a cursor, which is efficient for large result sets and paging.\u003c/p\u003e\n"],["\u003cp\u003eCursors can be per-query, holding the position of the last returned document and updating with each search, or per-result, associating a cursor with each document, enabling searches from a specific document's position.\u003c/p\u003e\n"]]],[],null,["# Handling Search Results\n\nWhen a query call completes normally, it returns the result as a [`Results`](/appengine/docs/legacy/standard/java/javadoc/com/google/appengine/api/search/Results) object. The Results object tells you how many matching documents were found in the index, and how many matched documents were returned. It also includes a collection of matching [`ScoredDocuments`](/appengine/docs/legacy/standard/java/javadoc/com/google/appengine/api/search/ScoredDocument). The collection usually contains a portion of all the matching documents found, since search returns a limited number of documents each time it's called. By using an offset or a cursor you can retrieve all the matching documents, a subset at a time.\n| This API is supported for first-generation runtimes and can be used when [upgrading to corresponding second-generation runtimes](/appengine/docs/standard/\n| java-gen2\n|\n| /services/access). If you are updating to the App Engine Java 11/17 runtime, refer to the [migration guide](/appengine/migration-center/standard/migrate-to-second-gen/java-differences) to learn about your migration options for legacy bundled services.\n\nResults\n-------\n\n // index and query have already been defined ... \n try {\n Results\u003cScoredDocument\u003e result = index.search(query);\n long totalMatches = result.getNumberFound();\n int numberOfDocsReturned = result.getNumberReturned();\n Collection\u003cScoredDocument\u003e listOfDocs = result.getResults();\n } catch (SearchException e) {\n // handle exception...\n }\n\nDepending on the value of the `limit` [query option](/appengine/docs/legacy/standard/java/search/options#queryoptions), the number of matching documents returned in the result may be less than the number found. Remember that the number found will be an estimate if the number found accuracy is less than the number found. No matter how you configure the search options, a `search()` call will find no more than 10,000 matching documents.\n\nIf more documents were found than returned, and you want to retrieve all of them, you need to repeat the search using either an offset or a cursor, as explained below.\n| **Note:** Your calling code should be prepared to handle exceptions which might be thrown if the query is invalid or there were problems processing it.\n\nScored documents\n----------------\n\nThe search results will include a Collection of [`ScoredDocuments`](/appengine/docs/legacy/standard/java/javadoc/com/google/appengine/api/search/ScoredDocument) that match the query. You can retrieve the collection using the method `getResults()` or iterate over its members directly from the search results itself. Both methods are shown here: \n\n // index and query have already been defined ...\n Results\u003cScoredDocument\u003e result = index.search(query);\n\n // Grab the collection for later use:\n Collection\u003cScoredDocument\u003e theDocs = result.getResults();\n\n // Alternatively, iterate over the results directly:\n for (ScoredDocument doc : result) {\n // do work\n }\n\nBy default, a scored document contains all the fields of the original document that was indexed. If your [query options](/appengine/docs/legacy/standard/java/search/options#queryoptions) specified `setFieldsToReturn`, only those fields will appear in the results when you call [`getFields()`](/appengine/docs/legacy/standard/java/javadoc/com/google/appengine/api/search/Document#getfields) on the document. If you used `addExpressionToReturn` or `setFieldsToSnippet` to create computed fields, retrieve them separately by calling [`getExpressions()`](/appengine/docs/legacy/standard/java/javadoc/com/google/appengine/api/search/ScoredDocument#getexpressions) on the document.\n\nUsing offsets\n-------------\n\nIf your search finds more documents than you can return at once, use an offset to index into the list of matching documents. For example, the default query limit is 20 documents. After you've executed a search the first time (with offset 0) and retrieved the first 20 documents, retrieve the next 20 documents by setting the offset to 20 and running the same search again. Keep repeating the search, incrementing the offset each time by the number of documents returned: \n\n // index and queryString have already been defined\n\n try {\n int numberRetrieved = 0;\n int offset = 0;\n do {\n // build options and query\n QueryOptions options = QueryOptions.newBuilder()\n .setOffset(offset)\n .build();\n Query query = Query.newBuilder().setOptions(options).build(queryString);\n \n // search at least once\n Results\u003cScoredDocument\u003e result = index.search(query);\n numberRetrieved = result.getNumberReturned();\n if (numberRetrieved \u003e 0) {\n offset += numberRetrieved;\n // process the matched docs\n }\n \n }\n while (numberRetrieved \u003e 0);\n } catch (SearchException e) {\n // handle exception...\n }\n\nOffsets can be inefficient when iterating over a very large result set.\n\nUsing cursors\n-------------\n\nYou can also use cursors to retrieve a subrange of results. Cursors are useful when you intend to present your search results in consecutive pages and you want to be sure you do not skip any documents in the case where an index could be modified between queries. Cursors are also more efficient when iterating across a very large result set.\n\nIn order to use cursors, you must create an initial cursor and include it in the query options. There are two kinds of cursors, *per-query* and *per-result*. A per-query cursor causes a separate cursor to be associated with the results object returned by the search call. A per-result cursor causes a cursor to be associated with every scored document in the results.\n\n### Using a per-query cursor\n\nBy default, a newly constructed cursor is a per-query cursor. This cursor holds the position of the last document returned in the search's results. It is updated with each search. To enumerate all matching documents in an index, execute the same search until the result returns a null cursor: \n\n // index and queryString have already been defined\n \n try {\n // create the initial cursor\n Cursor cursor = Cursor.newBuilder().build();\n\n do {\n // build options and query\n QueryOptions options = QueryOptions.newBuilder()\n .setCursor(cursor)\n .build();\n Query query = Query.newBuilder().setOptions(options).build(queryString);\n \n // search at least once\n Results\u003cScoredDocument\u003e result = index.search(query);\n int numberRetrieved = result.getNumberReturned();\n cursor = result.getCursor();\n\n if (numberRetrieved \u003e 0) {\n // process the matched docs\n }\n \n }\n while (cursor != null);\n // all done!\n } catch (SearchException e) {\n // handle exception...\n }\n\n### Using a per-result cursor\n\nTo create per-result cursors, you must set the cursor perResult property to true when you create the initial cursor. When the search returns, every document will have a cursor associated with it. You can use that cursor to specify a new search with results that begin with a specific document. Note that when you pass a per-result cursor to search, there will be no per-query cursor associated with the result itself; result.getCursor() will return null so you can't use this to test whether you've retrieved all the matches. \n\n // index and queryString have already been defined\n \n try {\n // create an initial per-result cursor\n Cursor cursor = Cursor.newBuilder().setPerResult(true).build();\n // build options and query\n QueryOptions options = QueryOptions.newBuilder()\n .setCursor(cursor)\n .build();\n Query query = Query.newBuilder().setOptions(options).build(queryString);\n Results\u003cScoredDocument\u003e result = index.search(query);\n\n // process the matched docs\n cursor = null;\n for (ScoredDocument doc : result) {\n // discover some document of interest and grab its cursor\n if (...)\n cursor = doc.getCursor();\n }\n \n // Start the next search from the document of interest\n if (cursor != null) {\n options = QueryOptions.newBuilder()\n .setCursor(cursor)\n .build();\n query = Query.newBuilder().setOptions(options).build(queryString);\n result = index.search(query);\n }\n } catch (SearchException e) {\n // handle exception\n }\n\n### Saving and restoring cursors\n\nA cursor can be serialized as a web-safe string, saved, and then restored for later use: \n\n String cursorString = cursor.toWebSafeString();\n // Save the string ... and restore:\n Cursor cursor = Cursor.newBuilder().build(cursorString));"]]