1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app.appsearch;
18 
19 import android.annotation.FlaggedApi;
20 import android.annotation.NonNull;
21 import android.annotation.SuppressLint;
22 
23 import com.android.appsearch.flags.Flags;
24 
25 import com.google.common.util.concurrent.ListenableFuture;
26 
27 import java.io.Closeable;
28 import java.util.List;
29 import java.util.Set;
30 
31 /**
32  * Provides a connection to a single AppSearch database.
33  *
34  * <p>An {@link AppSearchSessionShim} instance provides access to database operations such as
35  * setting a schema, adding documents, and searching.
36  *
37  * <p>Instances of this interface are usually obtained from a storage implementation, e.g. {@code
38  * AppSearchManager.createSearchSessionAsync()} or {@code
39  * PlatformStorage.createSearchSessionAsync()}.
40  *
41  * <p>All implementations of this interface must be thread safe.
42  *
43  * @see GlobalSearchSessionShim
44  */
45 public interface AppSearchSessionShim extends Closeable {
46 
47     /**
48      * Sets the schema that represents the organizational structure of data within the AppSearch
49      * database.
50      *
51      * <p>Upon creating an {@link AppSearchSessionShim}, {@link #setSchema} should be called. If the
52      * schema needs to be updated, or it has not been previously set, then the provided schema will
53      * be saved and persisted to disk. Otherwise, {@link #setSchema} is handled efficiently as a
54      * no-op call.
55      *
56      * @param request the schema to set or update the AppSearch database to.
57      * @return a {@link ListenableFuture} which resolves to a {@link SetSchemaResponse} object.
58      */
59     @NonNull
setSchemaAsync(@onNull SetSchemaRequest request)60     ListenableFuture<SetSchemaResponse> setSchemaAsync(@NonNull SetSchemaRequest request);
61 
62     /**
63      * Retrieves the schema most recently successfully provided to {@link #setSchema}.
64      *
65      * @return The pending {@link GetSchemaResponse} of performing this operation.
66      */
67     // This call hits disk; async API prevents us from treating these calls as properties.
68     @SuppressLint("KotlinPropertyAccess")
69     @NonNull
getSchemaAsync()70     ListenableFuture<GetSchemaResponse> getSchemaAsync();
71 
72     /**
73      * Retrieves the set of all namespaces in the current database with at least one document.
74      *
75      * @return The pending result of performing this operation.
76      */
77     @NonNull
getNamespacesAsync()78     ListenableFuture<Set<String>> getNamespacesAsync();
79 
80     /**
81      * Indexes documents into the {@link AppSearchSessionShim} database.
82      *
83      * <p>Each {@link GenericDocument} object must have a {@code schemaType} field set to an {@link
84      * AppSearchSchema} type that has been previously registered by calling the {@link #setSchema}
85      * method.
86      *
87      * @param request containing documents to be indexed.
88      * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The
89      *     keys of the returned {@link AppSearchBatchResult} are the IDs of the input documents. The
90      *     values are either {@code null} if the corresponding document was successfully indexed, or
91      *     a failed {@link AppSearchResult} otherwise.
92      */
93     @NonNull
putAsync( @onNull PutDocumentsRequest request)94     ListenableFuture<AppSearchBatchResult<String, Void>> putAsync(
95             @NonNull PutDocumentsRequest request);
96 
97     /**
98      * Gets {@link GenericDocument} objects by document IDs in a namespace from the {@link
99      * AppSearchSessionShim} database.
100      *
101      * @param request a request containing a namespace and IDs to get documents for.
102      * @return A {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The
103      *     keys of the {@link AppSearchBatchResult} represent the input document IDs from the {@link
104      *     GetByDocumentIdRequest} object. The values are either the corresponding {@link
105      *     GenericDocument} object for the ID on success, or an {@link AppSearchResult} object on
106      *     failure. For example, if an ID is not found, the value for that ID will be set to an
107      *     {@link AppSearchResult} object with result code: {@link
108      *     AppSearchResult#RESULT_NOT_FOUND}.
109      */
110     @NonNull
getByDocumentIdAsync( @onNull GetByDocumentIdRequest request)111     ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentIdAsync(
112             @NonNull GetByDocumentIdRequest request);
113 
114     /**
115      * Opens a batch of AppSearch Blobs for writing.
116      *
117      * <p>A "blob" is a large binary object. It is used to store a significant amount of data that
118      * is not searchable, such as images, videos, audio files, or other binary data. Unlike other
119      * fields in AppSearch, blobs are stored as blob files on disk rather than in memory, and use
120      * {@link android.os.ParcelFileDescriptor} to read and write. This allows for efficient handling
121      * of large, non-searchable content.
122      *
123      * <p>Once done writing, call {@link #commitBlob} to commit blob files.
124      *
125      * <p>This call will create a empty blob file for each given {@link AppSearchBlobHandle}, and a
126      * {@link android.os.ParcelFileDescriptor} of that blob file will be returned in the {@link
127      * OpenBlobForWriteResponse}.
128      *
129      * <p>If the blob file is already stored in AppSearch and committed. A failed {@link
130      * AppSearchResult} with error code {@link AppSearchResult#RESULT_ALREADY_EXISTS} will be
131      * associated with the {@link AppSearchBlobHandle}.
132      *
133      * <p>If the blob file is already stored in AppSearch but not committed. A {@link
134      * android.os.ParcelFileDescriptor} of that blob file will be returned for continue writing.
135      *
136      * <p>For given duplicate {@link AppSearchBlobHandle}, the same {@link
137      * android.os.ParcelFileDescriptor} pointing to the same blob file will be returned.
138      *
139      * <p>Pending blob files won't be lost or auto-commit if {@link AppSearchSessionShim} closed.
140      * Pending blob files will be stored in disk rather than memory. You can re-open {@link
141      * AppSearchSessionShim} and re-write the pending blob files.
142      *
143      * <p>A committed blob file will be considered as an orphan if no {@link GenericDocument}
144      * references it. Uncommitted pending blob files and orphan blobs files will be cleaned up if
145      * they has been created for an extended period (default is 1 week).
146      *
147      * <p>Both pending blob files and committed blob files can be manually removed via {@link
148      * #removeBlob}.
149      *
150      * <p class="caution">The returned {@link OpenBlobForWriteResponse} must be closed after use to
151      * avoid resource leaks. Failing to close it will result in system file descriptor exhaustion.
152      *
153      * @param handles The {@link AppSearchBlobHandle}s that identifies the blobs.
154      * @return a response containing the writeable file descriptors.
155      * @see GenericDocument.Builder#setPropertyBlobHandle
156      */
157     @FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE)
158     @NonNull
openBlobForWriteAsync( @onNull Set<AppSearchBlobHandle> handles)159     default ListenableFuture<OpenBlobForWriteResponse> openBlobForWriteAsync(
160             @NonNull Set<AppSearchBlobHandle> handles) {
161         throw new UnsupportedOperationException(
162                 Features.BLOB_STORAGE + " is not available on this AppSearch implementation.");
163     }
164 
165     /**
166      * Removes the blob data from AppSearch.
167      *
168      * <p>After this call, the blob data is removed immediately and cannot be recovered. It will not
169      * accessible via {@link #openBlobForRead}. {@link #openBlobForWrite} could reopen and rewrite
170      * it.
171      *
172      * <p>This API can be used to remove pending blob data and committed blob data.
173      *
174      * <p class="caution">Removing a committed blob data that is still referenced by documents will
175      * leave those documents with no readable blob content. It is highly recommended to let
176      * AppSearch control the blob data's life cycle. AppSearch automatically recycles orphaned and
177      * pending blob data. The default time to recycle pending and orphan blob file is 1 week. A blob
178      * file will be considered as an orphan if no {@link GenericDocument} references it. If you want
179      * to remove a committed blob data, you should remove the reference documents first.
180      *
181      * @param handles The {@link AppSearchBlobHandle}s that identifies the blob data.
182      * @return a response containing the remove results.
183      */
184     @FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE)
185     @NonNull
removeBlobAsync( @onNull Set<AppSearchBlobHandle> handles)186     default ListenableFuture<RemoveBlobResponse> removeBlobAsync(
187             @NonNull Set<AppSearchBlobHandle> handles) {
188         throw new UnsupportedOperationException(
189                 Features.BLOB_STORAGE + " is not available on this AppSearch implementation.");
190     }
191 
192     /**
193      * Commits the blobs to make it retrievable and immutable.
194      *
195      * <p>After this call, the blob is readable via {@link #openBlobForRead}. Any change to the
196      * content or rewrite via {@link #openBlobForWrite} of this blob won't be allowed.
197      *
198      * <p>If the blob is already stored in AppSearch and committed. A failed {@link AppSearchResult}
199      * with error code {@link AppSearchResult#RESULT_ALREADY_EXISTS} will be associated with the
200      * {@link AppSearchBlobHandle}.
201      *
202      * <p>If the blob content doesn't match the digest in {@link AppSearchBlobHandle}, a failed
203      * {@link AppSearchResult} with error code {@link AppSearchResult#RESULT_INVALID_ARGUMENT} will
204      * be associated with the {@link AppSearchBlobHandle}. The pending Blob file will be removed
205      * from AppSearch.
206      *
207      * <p>Pending blobs won't be lost or auto-commit if {@link AppSearchSessionShim} closed. Pending
208      * blobs will store in disk rather than memory. You can re-open {@link AppSearchSessionShim} and
209      * re-write the pending blobs.
210      *
211      * <p>The default time to recycle pending and orphan blobs is 1 week. A blob will be considered
212      * as an orphan if no {@link GenericDocument} references it.
213      *
214      * <p>Both pending blob files and committed blob files can be manually removed via {@link
215      * #removeBlob}.
216      *
217      * @param handles The {@link AppSearchBlobHandle}s that identifies the blobs.
218      * @return a response containing the commit results.
219      * @see GenericDocument.Builder#setPropertyBlobHandle
220      */
221     @FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE)
222     @NonNull
commitBlobAsync( @onNull Set<AppSearchBlobHandle> handles)223     default ListenableFuture<CommitBlobResponse> commitBlobAsync(
224             @NonNull Set<AppSearchBlobHandle> handles) {
225         throw new UnsupportedOperationException(
226                 Features.BLOB_STORAGE + " is not available on this AppSearch implementation.");
227     }
228 
229     /**
230      * Opens a batch of AppSearch Blobs for reading.
231      *
232      * <p>Only blobs committed via {@link #commitBlob} are available for reading.
233      *
234      * <p class="caution">The returned {@link OpenBlobForReadResponse} must be closed after use to
235      * avoid resource leaks. Failing to close it will result in system file descriptor exhaustion.
236      *
237      * @param handles The {@link AppSearchBlobHandle}s that identifies the blobs.
238      * @return a response containing the readable file descriptors.
239      * @see GenericDocument.Builder#setPropertyBlobHandle
240      */
241     @FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE)
242     @NonNull
openBlobForReadAsync( @onNull Set<AppSearchBlobHandle> handles)243     default ListenableFuture<OpenBlobForReadResponse> openBlobForReadAsync(
244             @NonNull Set<AppSearchBlobHandle> handles) {
245         throw new UnsupportedOperationException(
246                 Features.BLOB_STORAGE + " is not available on this AppSearch implementation.");
247     }
248 
249     /**
250      * Sets the visibility configuration for all blob namespaces within an appsearch database.
251      *
252      * <p>Blobs under the same namespace will share same visibility settings.
253      *
254      * <p>The default setting is blobs will be only visible to the owner package and System. To
255      * configure other kinds of sharing, set {@link SchemaVisibilityConfig} via {@link
256      * SetBlobVisibilityRequest}.
257      *
258      * @param request The request holds visibility settings for all blob namespaces
259      * @return The pending result of performing this operation which resolves to {@code null} on
260      *     success.
261      */
262     @FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE)
263     @NonNull
setBlobVisibilityAsync( @onNull SetBlobVisibilityRequest request)264     default ListenableFuture<Void> setBlobVisibilityAsync(
265             @NonNull SetBlobVisibilityRequest request) {
266         throw new UnsupportedOperationException(
267                 Features.BLOB_STORAGE + " is not available on this AppSearch implementation.");
268     }
269 
270     /**
271      * Retrieves documents from the open {@link AppSearchSessionShim} that match a given query
272      * string and type of search provided.
273      *
274      * <p>Query strings can be empty, contain one term with no operators, or contain multiple terms
275      * and operators.
276      *
277      * <p>For query strings that are empty, all documents that match the {@link SearchSpec} will be
278      * returned.
279      *
280      * <p>For query strings with a single term and no operators, documents that match the provided
281      * query string and {@link SearchSpec} will be returned.
282      *
283      * <p>The following operators are supported:
284      *
285      * <ul>
286      *   <li>AND (implicit)
287      *       <p>AND is an operator that matches documents that contain <i>all</i> provided terms.
288      *       <p><b>NOTE:</b> A space between terms is treated as an "AND" operator. Explicitly
289      *       including "AND" in a query string will treat "AND" as a term, returning documents that
290      *       also contain "AND".
291      *       <p>Example: "apple AND banana" matches documents that contain the terms "apple", "and",
292      *       "banana".
293      *       <p>Example: "apple banana" matches documents that contain both "apple" and "banana".
294      *       <p>Example: "apple banana cherry" matches documents that contain "apple", "banana", and
295      *       "cherry".
296      *   <li>OR
297      *       <p>OR is an operator that matches documents that contain <i>any</i> provided term.
298      *       <p>Example: "apple OR banana" matches documents that contain either "apple" or
299      *       "banana".
300      *       <p>Example: "apple OR banana OR cherry" matches documents that contain any of "apple",
301      *       "banana", or "cherry".
302      *   <li>Exclusion (-)
303      *       <p>Exclusion (-) is an operator that matches documents that <i>do not</i> contain the
304      *       provided term.
305      *       <p>Example: "-apple" matches documents that do not contain "apple".
306      *   <li>Grouped Terms
307      *       <p>For queries that require multiple operators and terms, terms can be grouped into
308      *       subqueries. Subqueries are contained within an open "(" and close ")" parenthesis.
309      *       <p>Example: "(donut OR bagel) (coffee OR tea)" matches documents that contain either
310      *       "donut" or "bagel" and either "coffee" or "tea".
311      *   <li>Property Restricts
312      *       <p>For queries that require a term to match a specific {@link AppSearchSchema} property
313      *       of a document, a ":" must be included between the property name and the term.
314      *       <p>Example: "subject:important" matches documents that contain the term "important" in
315      *       the "subject" property.
316      * </ul>
317      *
318      * <p>The above description covers the query operators that are supported on all versions of
319      * AppSearch. Additional operators and their required features are described below.
320      *
321      * <p>{@link Features#LIST_FILTER_QUERY_LANGUAGE}: This feature covers the expansion of the
322      * query language to conform to the definition of the list filters language (https://aip
323      * .dev/160). This includes:
324      *
325      * <ul>
326      *   <li>addition of explicit 'AND' and 'NOT' operators
327      *   <li>property restricts are allowed with groupings (ex. "prop:(a OR b)")
328      *   <li>addition of custom functions to control matching
329      * </ul>
330      *
331      * <p>The newly added custom functions covered by this feature are:
332      *
333      * <ul>
334      *   <li>createList(String...)
335      *   <li>search(String, {@code List<String>})
336      *   <li>propertyDefined(String)
337      * </ul>
338      *
339      * <p>createList takes a variable number of strings and returns a list of strings. It is for use
340      * with search.
341      *
342      * <p>search takes a query string that will be parsed according to the supported query language
343      * and an optional list of strings that specify the properties to be restricted to. This exists
344      * as a convenience for multiple property restricts. So, for example, the query `(subject:foo OR
345      * body:foo) (subject:bar OR body:bar)` could be rewritten as `search("foo bar",
346      * createList("subject", "body"))`.
347      *
348      * <p>propertyDefined takes a string specifying the property of interest and matches all
349      * documents of any type that defines the specified property (ex.
350      * `propertyDefined("sender.name")`). Note that propertyDefined will match so long as the
351      * document's type defines the specified property. Unlike the "hasProperty" function below, this
352      * function does NOT require that the document actually hold any values for this property.
353      *
354      * <p>{@link Features#NUMERIC_SEARCH}: This feature covers numeric search expressions. In the
355      * query language, the values of properties that have {@link
356      * AppSearchSchema.LongPropertyConfig#INDEXING_TYPE_RANGE} set can be matched with a numeric
357      * search expression (the property, a supported comparator and an integer value). Supported
358      * comparators are <, <=, ==, >= and >.
359      *
360      * <p>Ex. `price < 10` will match all documents that has a numeric value in its price property
361      * that is less than 10.
362      *
363      * <p>{@link Features#VERBATIM_SEARCH}: This feature covers the verbatim string operator
364      * (quotation marks).
365      *
366      * <p>Ex. `"foo/bar" OR baz` will ensure that 'foo/bar' is treated as a single 'verbatim' token.
367      *
368      * <p>{@link Features#LIST_FILTER_HAS_PROPERTY_FUNCTION}: This feature covers the "hasProperty"
369      * function in query expressions, which takes a string specifying the property of interest and
370      * matches all documents that hold values for this property. Not to be confused with the
371      * "propertyDefined" function, which checks whether a document's schema has defined the
372      * property, instead of whether a document itself has this property.
373      *
374      * <p>Ex. `foo hasProperty("sender.name")` will return all documents that have the term "foo"
375      * AND have values in the property "sender.name". Consider two documents, documentA and
376      * documentB, of the same schema with an optional property "sender.name". If documentA sets
377      * "foo" in this property but documentB does not, then `hasProperty("sender.name")` will only
378      * match documentA. However, `propertyDefined("sender.name")` will match both documentA and
379      * documentB, regardless of whether a value is actually set.
380      *
381      * <p>{@link Features#LIST_FILTER_MATCH_SCORE_EXPRESSION_FUNCTION}: This feature covers the
382      * "matchScoreExpression" function in query expressions.
383      *
384      * <p>Usage: matchScoreExpression({score_expression}, {low}, {high})
385      *
386      * <ul>
387      *   <li>matchScoreExpression matches all documents with scores falling within the specified
388      *       range. These scores are calculated using the provided score expression, which adheres
389      *       to the syntax defined in {@link SearchSpec.Builder#setRankingStrategy(String)}.
390      *   <li>"score_expression" is a string value that specifies the score expression.
391      *   <li>"low" and "high" are floating point numbers that specify the score range. The "high"
392      *       parameter is optional; if not provided, it defaults to positive infinity.
393      * </ul>
394      *
395      * <p>Ex. `matchScoreExpression("this.documentScore()", 3, 4)` will return all documents that
396      * have document scores from 3 to 4.
397      *
398      * <p>{@link Features#SCHEMA_EMBEDDING_PROPERTY_CONFIG}: This feature covers the
399      * "semanticSearch" and "getEmbeddingParameter" functions in query expressions, which are used
400      * for semantic search.
401      *
402      * <p>Usage: semanticSearch(getEmbeddingParameter({embedding_index}), {low}, {high}, {metric})
403      *
404      * <ul>
405      *   <li>semanticSearch matches all documents that have at least one embedding vector with a
406      *       matching model signature (see {@link EmbeddingVector#getModelSignature()}) and a
407      *       similarity score within the range specified based on the provided metric.
408      *   <li>getEmbeddingParameter({embedding_index}) retrieves the embedding search passed in
409      *       {@link SearchSpec.Builder#addEmbeddingParameters} based on the index specified, which
410      *       starts from 0.
411      *   <li>"low" and "high" are floating point numbers that specify the similarity score range. If
412      *       omitted, they default to negative and positive infinity, respectively.
413      *   <li>"metric" is a string value that specifies how embedding similarities should be
414      *       calculated. If omitted, it defaults to the metric specified in {@link
415      *       SearchSpec.Builder#setDefaultEmbeddingSearchMetricType(int)}. Possible values:
416      *       <ul>
417      *         <li>"COSINE"
418      *         <li>"DOT_PRODUCT"
419      *         <li>"EUCLIDEAN"
420      *       </ul>
421      * </ul>
422      *
423      * <p>Examples:
424      *
425      * <ul>
426      *   <li>Basic: semanticSearch(getEmbeddingParameter(0), 0.5, 1, "COSINE")
427      *   <li>With a property restriction: property1:semanticSearch(getEmbeddingParameter(0), 0.5, 1)
428      *   <li>Hybrid: foo OR semanticSearch(getEmbeddingParameter(0), 0.5, 1)
429      *   <li>Complex: (foo OR semanticSearch(getEmbeddingParameter(0), 0.5, 1)) AND bar
430      * </ul>
431      *
432      * <p>{@link Features#SEARCH_SPEC_SEARCH_STRING_PARAMETERS}: This feature covers the
433      * "getSearchStringParameter" function in query expressions, which substitutes the string
434      * provided at the same index in {@link SearchSpec.Builder#addSearchStringParameters} into the
435      * query as plain text. This string is then segmented, normalized and stripped of
436      * punctuation-only segments. The remaining tokens are then AND'd together. This function is
437      * useful for callers who wish to provide user input, but want to ensure that that user input
438      * does not invoke any query operators.
439      *
440      * <p>Usage: getSearchStringParameter({search_parameter_strings_index})
441      *
442      * <p>Ex. `foo OR getSearchStringParameter(0)` with {@link SearchSpec#getSearchStringParameters}
443      * returning {"bar OR baz."}. The string "bar OR baz." will be segmented into "bar", "OR",
444      * "baz", ".". Punctuation is removed and the segments are normalized to "bar", "or", "baz".
445      * This query will be equivalent to `foo OR (bar AND or AND baz)`.
446      *
447      * <p>The availability of each of these features can be checked by calling {@link
448      * Features#isFeatureSupported} with the desired feature.
449      *
450      * <p>Additional search specifications, such as filtering by {@link AppSearchSchema} type or
451      * adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter.
452      *
453      * <p>This method is lightweight. The heavy work will be done in {@link
454      * SearchResultsShim#getNextPage}.
455      *
456      * @param queryExpression query string to search.
457      * @param searchSpec spec for setting document filters, adding projection, setting term match
458      *     type, etc.
459      * @return a {@link SearchResultsShim} object for retrieved matched documents.
460      */
461     // TODO(b/326656531): Refine the javadoc to provide guidance on the best practice of
462     //  embedding searches and how to select an appropriate metric.
463     @NonNull
search(@onNull String queryExpression, @NonNull SearchSpec searchSpec)464     SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
465 
466     /**
467      * Retrieves suggested Strings that could be used as {@code queryExpression} in {@link
468      * #search(String, SearchSpec)} API.
469      *
470      * <p>The {@code suggestionQueryExpression} can contain one term with no operators, or contain
471      * multiple terms and operators. Operators will be considered as a normal term. Please see the
472      * operator examples below. The {@code suggestionQueryExpression} must end with a valid term,
473      * the suggestions are generated based on the last term. If the input {@code
474      * suggestionQueryExpression} doesn't have a valid token, AppSearch will return an empty result
475      * list. Please see the invalid examples below.
476      *
477      * <p>Example: if there are following documents with content stored in AppSearch.
478      *
479      * <ul>
480      *   <li>document1: "term1"
481      *   <li>document2: "term1 term2"
482      *   <li>document3: "term1 term2 term3"
483      *   <li>document4: "org"
484      * </ul>
485      *
486      * <p>Search suggestions with the single term {@code suggestionQueryExpression} "t", the
487      * suggested results are:
488      *
489      * <ul>
490      *   <li>"term1" - Use it to be queryExpression in {@link #search} could get 3 {@link
491      *       SearchResult}s, which contains document 1, 2 and 3.
492      *   <li>"term2" - Use it to be queryExpression in {@link #search} could get 2 {@link
493      *       SearchResult}s, which contains document 2 and 3.
494      *   <li>"term3" - Use it to be queryExpression in {@link #search} could get 1 {@link
495      *       SearchResult}, which contains document 3.
496      * </ul>
497      *
498      * <p>Search suggestions with the multiple term {@code suggestionQueryExpression} "org t", the
499      * suggested result will be "org term1" - The last token is completed by the suggested String.
500      *
501      * <p>Operators in {@link #search} are supported.
502      *
503      * <p><b>NOTE:</b> Exclusion and Grouped Terms in the last term is not supported.
504      *
505      * <p>example: "apple -f": This Api will throw an {@link
506      * android.app.appsearch.exceptions.AppSearchException} with {@link
507      * AppSearchResult#RESULT_INVALID_ARGUMENT}.
508      *
509      * <p>example: "apple (f)": This Api will return an empty results.
510      *
511      * <p>Invalid example: All these input {@code suggestionQueryExpression} don't have a valid last
512      * token, AppSearch will return an empty result list.
513      *
514      * <ul>
515      *   <li>"" - Empty {@code suggestionQueryExpression}.
516      *   <li>"(f)" - Ending in a closed brackets.
517      *   <li>"f:" - Ending in an operator.
518      *   <li>"f " - Ending in trailing space.
519      * </ul>
520      *
521      * @param suggestionQueryExpression the non empty query string to search suggestions
522      * @param searchSuggestionSpec spec for setting document filters
523      * @return The pending result of performing this operation which resolves to a List of {@link
524      *     SearchSuggestionResult} on success. The returned suggestion Strings are ordered by the
525      *     number of {@link SearchResult} you could get by using that suggestion in {@link #search}.
526      * @see #search(String, SearchSpec)
527      */
528     @NonNull
searchSuggestionAsync( @onNull String suggestionQueryExpression, @NonNull SearchSuggestionSpec searchSuggestionSpec)529     ListenableFuture<List<SearchSuggestionResult>> searchSuggestionAsync(
530             @NonNull String suggestionQueryExpression,
531             @NonNull SearchSuggestionSpec searchSuggestionSpec);
532 
533     /**
534      * Reports usage of a particular document by namespace and ID.
535      *
536      * <p>A usage report represents an event in which a user interacted with or viewed a document.
537      *
538      * <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency *
539      * metrics for that particular document. These metrics are used for ordering {@link #search}
540      * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and {@link
541      * SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
542      *
543      * <p>Reporting usage of a document is optional.
544      *
545      * @param request The usage reporting request.
546      * @return The pending result of performing this operation which resolves to {@code null} on
547      *     success.
548      */
549     @NonNull
reportUsageAsync(@onNull ReportUsageRequest request)550     ListenableFuture<Void> reportUsageAsync(@NonNull ReportUsageRequest request);
551 
552     /**
553      * Removes {@link GenericDocument} objects by document IDs in a namespace from the {@link
554      * AppSearchSessionShim} database.
555      *
556      * <p>Removed documents will no longer be surfaced by {@link #search} or {@link
557      * #getByDocumentId} calls.
558      *
559      * <p>Once the database crosses the document count or byte usage threshold, removed documents
560      * will be deleted from disk.
561      *
562      * @param request {@link RemoveByDocumentIdRequest} with IDs in a namespace to remove from the
563      *     index.
564      * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The
565      *     keys of the {@link AppSearchBatchResult} represent the input IDs from the {@link
566      *     RemoveByDocumentIdRequest} object. The values are either {@code null} on success, or a
567      *     failed {@link AppSearchResult} otherwise. IDs that are not found will return a failed
568      *     {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}.
569      */
570     @NonNull
removeAsync( @onNull RemoveByDocumentIdRequest request)571     ListenableFuture<AppSearchBatchResult<String, Void>> removeAsync(
572             @NonNull RemoveByDocumentIdRequest request);
573 
574     /**
575      * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
576      * match the {@code queryExpression} in given namespaces and schemaTypes which is set via {@link
577      * SearchSpec.Builder#addFilterNamespaces} and {@link SearchSpec.Builder#addFilterSchemas}.
578      *
579      * <p>An empty {@code queryExpression} matches all documents.
580      *
581      * <p>An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in the
582      * current database.
583      *
584      * @param queryExpression Query String to search.
585      * @param searchSpec Spec containing schemaTypes, namespaces and query expression indicates how
586      *     document will be removed. All specific about how to scoring, ordering, snippeting and
587      *     resulting will be ignored.
588      * @return The pending result of performing this operation.
589      * @throws IllegalArgumentException if the {@link SearchSpec} contains a {@link JoinSpec}.
590      *     {@link JoinSpec} lets you join docs that are not owned by the caller, so the semantics of
591      *     failures from this method would be complex.
592      */
593     @NonNull
removeAsync( @onNull String queryExpression, @NonNull SearchSpec searchSpec)594     ListenableFuture<Void> removeAsync(
595             @NonNull String queryExpression, @NonNull SearchSpec searchSpec);
596 
597     /**
598      * Gets the storage info for this {@link AppSearchSessionShim} database.
599      *
600      * <p>This may take time proportional to the number of documents and may be inefficient to call
601      * repeatedly.
602      *
603      * @return a {@link ListenableFuture} which resolves to a {@link StorageInfo} object.
604      */
605     @NonNull
getStorageInfoAsync()606     ListenableFuture<StorageInfo> getStorageInfoAsync();
607 
608     /**
609      * Flush all schema and document updates, additions, and deletes to disk if possible.
610      *
611      * <p>The request is not guaranteed to be handled and may be ignored by some implementations of
612      * AppSearchSessionShim.
613      *
614      * @return The pending result of performing this operation. {@link
615      *     android.app.appsearch.exceptions.AppSearchException} with {@link
616      *     AppSearchResult#RESULT_INTERNAL_ERROR} will be set to the future if we hit error when
617      *     save to disk.
618      */
619     @NonNull
requestFlushAsync()620     ListenableFuture<Void> requestFlushAsync();
621 
622     /**
623      * Returns the {@link Features} to check for the availability of certain features for this
624      * session.
625      */
626     @NonNull
getFeatures()627     Features getFeatures();
628 
629     /**
630      * Closes the {@link AppSearchSessionShim} to persist all schema and document updates,
631      * additions, and deletes to disk.
632      */
633     @Override
close()634     void close();
635 }
636