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