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