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.IntDef; 21 import android.annotation.IntRange; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SuppressLint; 25 import android.app.appsearch.annotation.CanIgnoreReturnValue; 26 import android.app.appsearch.exceptions.AppSearchException; 27 import android.app.appsearch.safeparcel.AbstractSafeParcelable; 28 import android.app.appsearch.safeparcel.SafeParcelable; 29 import android.app.appsearch.util.BundleUtil; 30 import android.os.Bundle; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.util.ArrayMap; 34 import android.util.ArraySet; 35 36 import com.android.appsearch.flags.Flags; 37 import com.android.internal.util.Preconditions; 38 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.Collection; 44 import java.util.Collections; 45 import java.util.List; 46 import java.util.Map; 47 import java.util.Objects; 48 import java.util.Set; 49 50 /** 51 * This class represents the specification logic for AppSearch. It can be used to set the type of 52 * search, like prefix or exact only or apply filters to search for a specific schema type only etc. 53 */ 54 @SafeParcelable.Class(creator = "SearchSpecCreator") 55 // TODO(b/384721898): Switch to JSpecify annotations 56 @SuppressWarnings({"HiddenSuperclass", "JSpecifyNullness"}) 57 public final class SearchSpec extends AbstractSafeParcelable { 58 59 /** Creator class for {@link SearchSpec}. */ 60 @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2) 61 public static final @NonNull Parcelable.Creator<SearchSpec> CREATOR = new SearchSpecCreator(); 62 63 /** 64 * Schema type to be used in {@link SearchSpec.Builder#addProjection} to apply property paths to 65 * all results, excepting any types that have had their own, specific property paths set. 66 * 67 * @deprecated use {@link #SCHEMA_TYPE_WILDCARD} instead. 68 */ 69 @Deprecated public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*"; 70 71 /** 72 * Schema type to be used in {@link SearchSpec.Builder#addFilterProperties(String, Collection)} 73 * and {@link SearchSpec.Builder#addProjection} to apply property paths to all results, 74 * excepting any types that have had their own, specific property paths set. 75 */ 76 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES) 77 public static final String SCHEMA_TYPE_WILDCARD = "*"; 78 79 @Field(id = 1, getter = "getTermMatch") 80 private final int mTermMatchType; 81 82 @Field(id = 2, getter = "getFilterSchemas") 83 private final List<String> mSchemas; 84 85 @Field(id = 3, getter = "getFilterNamespaces") 86 private final List<String> mNamespaces; 87 88 @Field(id = 4) 89 final Bundle mTypePropertyFilters; 90 91 @Field(id = 5, getter = "getFilterPackageNames") 92 private final List<String> mPackageNames; 93 94 @Field(id = 6, getter = "getResultCountPerPage") 95 private final int mResultCountPerPage; 96 97 @Field(id = 7, getter = "getRankingStrategy") 98 @RankingStrategy 99 private final int mRankingStrategy; 100 101 @Field(id = 8, getter = "getOrder") 102 @Order 103 private final int mOrder; 104 105 @Field(id = 9, getter = "getSnippetCount") 106 private final int mSnippetCount; 107 108 @Field(id = 10, getter = "getSnippetCountPerProperty") 109 private final int mSnippetCountPerProperty; 110 111 @Field(id = 11, getter = "getMaxSnippetSize") 112 private final int mMaxSnippetSize; 113 114 @Field(id = 12) 115 final Bundle mProjectionTypePropertyMasks; 116 117 @Field(id = 13, getter = "getResultGroupingTypeFlags") 118 @GroupingType 119 private final int mResultGroupingTypeFlags; 120 121 @Field(id = 14, getter = "getResultGroupingLimit") 122 private final int mGroupingLimit; 123 124 @Field(id = 15) 125 final Bundle mTypePropertyWeightsField; 126 127 @Field(id = 16, getter = "getJoinSpec") 128 private final @Nullable JoinSpec mJoinSpec; 129 130 @Field(id = 17, getter = "getAdvancedRankingExpression") 131 private final String mAdvancedRankingExpression; 132 133 @Field(id = 18, getter = "getEnabledFeatures") 134 private final List<String> mEnabledFeatures; 135 136 @Field(id = 19, getter = "getSearchSourceLogTag") 137 private final @Nullable String mSearchSourceLogTag; 138 139 @Field(id = 20, getter = "getEmbeddingParameters") 140 private final @NonNull List<EmbeddingVector> mEmbeddingParameters; 141 142 @Field(id = 21, getter = "getDefaultEmbeddingSearchMetricType") 143 private final int mDefaultEmbeddingSearchMetricType; 144 145 @Field(id = 22, getter = "getInformationalRankingExpressions") 146 private final @NonNull List<String> mInformationalRankingExpressions; 147 148 @Field(id = 23, getter = "getSearchStringParameters") 149 private final @NonNull List<String> mSearchStringParameters; 150 151 /** 152 * Holds the list of document ids to search over. 153 * 154 * <p>If empty, the query will search over all documents. 155 */ 156 @Field(id = 24, getter = "getFilterDocumentIds") 157 private final @NonNull List<String> mFilterDocumentIds; 158 159 /** Whether to retrieve embedding match info for the query. */ 160 @Field(id = 25, getter = "shouldRetrieveEmbeddingMatchInfos") 161 private final boolean mRetrieveEmbeddingMatchInfos; 162 163 /** 164 * Default number of documents per page. 165 * 166 * @hide 167 */ 168 public static final int DEFAULT_NUM_PER_PAGE = 10; 169 170 // TODO(b/170371356): In framework, we may want these limits to be flag controlled. 171 // If that happens, the @IntRange() directives in this class may have to change. 172 private static final int MAX_NUM_PER_PAGE = 10_000; 173 private static final int MAX_SNIPPET_COUNT = 10_000; 174 private static final int MAX_SNIPPET_PER_PROPERTY_COUNT = 10_000; 175 private static final int MAX_SNIPPET_SIZE_LIMIT = 10_000; 176 177 /** 178 * Term Match Type for the query. 179 * 180 * @hide 181 */ 182 // NOTE: The integer values of these constants must match the proto enum constants in 183 // {@link com.google.android.icing.proto.SearchSpecProto.termMatchType} 184 185 @IntDef(value = {TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX}) 186 @Retention(RetentionPolicy.SOURCE) 187 public @interface TermMatch {} 188 189 /** 190 * Query terms will only match exact tokens in the index. 191 * 192 * <p>For example, a query term "foo" will only match indexed token "foo", and not "foot" or 193 * "football". 194 */ 195 public static final int TERM_MATCH_EXACT_ONLY = 1; 196 197 /** 198 * Query terms will match indexed tokens when the query term is a prefix of the token. 199 * 200 * <p>For example, a query term "foo" will match indexed tokens like "foo", "foot", and 201 * "football". 202 */ 203 public static final int TERM_MATCH_PREFIX = 2; 204 205 /** 206 * Ranking Strategy for query result. 207 * 208 * @hide 209 */ 210 // NOTE: The integer values of these constants must match the proto enum constants in 211 // {@link ScoringSpecProto.RankingStrategy.Code} 212 213 @IntDef( 214 value = { 215 RANKING_STRATEGY_NONE, 216 RANKING_STRATEGY_DOCUMENT_SCORE, 217 RANKING_STRATEGY_CREATION_TIMESTAMP, 218 RANKING_STRATEGY_RELEVANCE_SCORE, 219 RANKING_STRATEGY_USAGE_COUNT, 220 RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP, 221 RANKING_STRATEGY_SYSTEM_USAGE_COUNT, 222 RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP, 223 RANKING_STRATEGY_JOIN_AGGREGATE_SCORE, 224 RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION, 225 }) 226 @Retention(RetentionPolicy.SOURCE) 227 public @interface RankingStrategy {} 228 229 /** No Ranking, results are returned in arbitrary order. */ 230 public static final int RANKING_STRATEGY_NONE = 0; 231 232 /** Ranked by app-provided document scores. */ 233 public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; 234 235 /** Ranked by document creation timestamps. */ 236 public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; 237 238 /** Ranked by document relevance score. */ 239 public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3; 240 241 /** Ranked by number of usages, as reported by the app. */ 242 public static final int RANKING_STRATEGY_USAGE_COUNT = 4; 243 244 /** Ranked by timestamp of last usage, as reported by the app. */ 245 public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5; 246 247 /** Ranked by number of usages from a system UI surface. */ 248 public static final int RANKING_STRATEGY_SYSTEM_USAGE_COUNT = 6; 249 250 /** Ranked by timestamp of last usage from a system UI surface. */ 251 public static final int RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP = 7; 252 253 /** 254 * Ranked by the aggregated ranking signal of the joined documents. 255 * 256 * <p>Which aggregation strategy is used to determine a ranking signal is specified in the 257 * {@link JoinSpec} set by {@link Builder#setJoinSpec}. This ranking strategy may not be used if 258 * no {@link JoinSpec} is provided. 259 * 260 * @see Builder#build 261 */ 262 public static final int RANKING_STRATEGY_JOIN_AGGREGATE_SCORE = 8; 263 264 /** Ranked by the advanced ranking expression provided. */ 265 public static final int RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION = 9; 266 267 /** 268 * Order for query result. 269 * 270 * @hide 271 */ 272 // NOTE: The integer values of these constants must match the proto enum constants in 273 // {@link ScoringSpecProto.Order.Code} 274 275 @IntDef(value = {ORDER_DESCENDING, ORDER_ASCENDING}) 276 @Retention(RetentionPolicy.SOURCE) 277 public @interface Order {} 278 279 /** Search results will be returned in a descending order. */ 280 public static final int ORDER_DESCENDING = 0; 281 282 /** Search results will be returned in an ascending order. */ 283 public static final int ORDER_ASCENDING = 1; 284 285 /** 286 * Grouping type for result limits. 287 * 288 * @hide 289 */ 290 @IntDef( 291 flag = true, 292 value = { 293 GROUPING_TYPE_PER_PACKAGE, 294 GROUPING_TYPE_PER_NAMESPACE, 295 GROUPING_TYPE_PER_SCHEMA 296 }) 297 @Retention(RetentionPolicy.SOURCE) 298 public @interface GroupingType {} 299 300 /** 301 * Results should be grouped together by package for the purpose of enforcing a limit on the 302 * number of results returned per package. 303 */ 304 public static final int GROUPING_TYPE_PER_PACKAGE = 1 << 0; 305 306 /** 307 * Results should be grouped together by namespace for the purpose of enforcing a limit on the 308 * number of results returned per namespace. 309 */ 310 public static final int GROUPING_TYPE_PER_NAMESPACE = 1 << 1; 311 312 /** 313 * Results should be grouped together by schema type for the purpose of enforcing a limit on the 314 * number of results returned per schema type. 315 */ 316 @FlaggedApi(Flags.FLAG_ENABLE_GROUPING_TYPE_PER_SCHEMA) 317 public static final int GROUPING_TYPE_PER_SCHEMA = 1 << 2; 318 319 /** 320 * Type of scoring used to calculate similarity for embedding vectors. For details of each, see 321 * comments above each value. 322 * 323 * @hide 324 */ 325 // NOTE: The integer values of these constants must match the proto enum constants in 326 // {@link SearchSpecProto.EmbeddingQueryMetricType.Code} 327 328 @IntDef( 329 value = { 330 EMBEDDING_SEARCH_METRIC_TYPE_DEFAULT, 331 EMBEDDING_SEARCH_METRIC_TYPE_COSINE, 332 EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT, 333 EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN, 334 }) 335 @Retention(RetentionPolicy.SOURCE) 336 public @interface EmbeddingSearchMetricType {} 337 338 /** 339 * Use the default metric set in {@link SearchSpec#getDefaultEmbeddingSearchMetricType()} for 340 * embedding search and ranking. 341 */ 342 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) 343 public static final int EMBEDDING_SEARCH_METRIC_TYPE_DEFAULT = 0; 344 345 /** Cosine similarity as metric for embedding search and ranking. */ 346 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) 347 public static final int EMBEDDING_SEARCH_METRIC_TYPE_COSINE = 1; 348 349 /** Dot product similarity as metric for embedding search and ranking. */ 350 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) 351 public static final int EMBEDDING_SEARCH_METRIC_TYPE_DOT_PRODUCT = 2; 352 353 /** Euclidean distance as metric for embedding search and ranking. */ 354 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) 355 public static final int EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN = 3; 356 357 @Constructor SearchSpec( @aramid = 1) int termMatchType, @Param(id = 2) @NonNull List<String> schemas, @Param(id = 3) @NonNull List<String> namespaces, @Param(id = 4) @NonNull Bundle properties, @Param(id = 5) @NonNull List<String> packageNames, @Param(id = 6) int resultCountPerPage, @Param(id = 7) @RankingStrategy int rankingStrategy, @Param(id = 8) @Order int order, @Param(id = 9) int snippetCount, @Param(id = 10) int snippetCountPerProperty, @Param(id = 11) int maxSnippetSize, @Param(id = 12) @NonNull Bundle projectionTypePropertyMasks, @Param(id = 13) int resultGroupingTypeFlags, @Param(id = 14) int groupingLimit, @Param(id = 15) @NonNull Bundle typePropertyWeightsField, @Param(id = 16) @Nullable JoinSpec joinSpec, @Param(id = 17) @NonNull String advancedRankingExpression, @Param(id = 18) @NonNull List<String> enabledFeatures, @Param(id = 19) @Nullable String searchSourceLogTag, @Param(id = 20) @Nullable List<EmbeddingVector> embeddingParameters, @Param(id = 21) int defaultEmbeddingSearchMetricType, @Param(id = 22) @Nullable List<String> informationalRankingExpressions, @Param(id = 23) @Nullable List<String> searchStringParameters, @Param(id = 24) @Nullable List<String> filterDocumentIds, @Param(id = 25) boolean retrieveEmbeddingMatchInfos)358 SearchSpec( 359 @Param(id = 1) int termMatchType, 360 @Param(id = 2) @NonNull List<String> schemas, 361 @Param(id = 3) @NonNull List<String> namespaces, 362 @Param(id = 4) @NonNull Bundle properties, 363 @Param(id = 5) @NonNull List<String> packageNames, 364 @Param(id = 6) int resultCountPerPage, 365 @Param(id = 7) @RankingStrategy int rankingStrategy, 366 @Param(id = 8) @Order int order, 367 @Param(id = 9) int snippetCount, 368 @Param(id = 10) int snippetCountPerProperty, 369 @Param(id = 11) int maxSnippetSize, 370 @Param(id = 12) @NonNull Bundle projectionTypePropertyMasks, 371 @Param(id = 13) int resultGroupingTypeFlags, 372 @Param(id = 14) int groupingLimit, 373 @Param(id = 15) @NonNull Bundle typePropertyWeightsField, 374 @Param(id = 16) @Nullable JoinSpec joinSpec, 375 @Param(id = 17) @NonNull String advancedRankingExpression, 376 @Param(id = 18) @NonNull List<String> enabledFeatures, 377 @Param(id = 19) @Nullable String searchSourceLogTag, 378 @Param(id = 20) @Nullable List<EmbeddingVector> embeddingParameters, 379 @Param(id = 21) int defaultEmbeddingSearchMetricType, 380 @Param(id = 22) @Nullable List<String> informationalRankingExpressions, 381 @Param(id = 23) @Nullable List<String> searchStringParameters, 382 @Param(id = 24) @Nullable List<String> filterDocumentIds, 383 @Param(id = 25) boolean retrieveEmbeddingMatchInfos) { 384 mTermMatchType = termMatchType; 385 mSchemas = Collections.unmodifiableList(Objects.requireNonNull(schemas)); 386 mNamespaces = Collections.unmodifiableList(Objects.requireNonNull(namespaces)); 387 mTypePropertyFilters = Objects.requireNonNull(properties); 388 mPackageNames = Collections.unmodifiableList(Objects.requireNonNull(packageNames)); 389 mResultCountPerPage = resultCountPerPage; 390 mRankingStrategy = rankingStrategy; 391 mOrder = order; 392 mSnippetCount = snippetCount; 393 mSnippetCountPerProperty = snippetCountPerProperty; 394 mMaxSnippetSize = maxSnippetSize; 395 mProjectionTypePropertyMasks = Objects.requireNonNull(projectionTypePropertyMasks); 396 mResultGroupingTypeFlags = resultGroupingTypeFlags; 397 mGroupingLimit = groupingLimit; 398 mTypePropertyWeightsField = Objects.requireNonNull(typePropertyWeightsField); 399 mJoinSpec = joinSpec; 400 mAdvancedRankingExpression = Objects.requireNonNull(advancedRankingExpression); 401 mEnabledFeatures = Collections.unmodifiableList(Objects.requireNonNull(enabledFeatures)); 402 mSearchSourceLogTag = searchSourceLogTag; 403 if (embeddingParameters != null) { 404 mEmbeddingParameters = Collections.unmodifiableList(embeddingParameters); 405 } else { 406 mEmbeddingParameters = Collections.emptyList(); 407 } 408 mDefaultEmbeddingSearchMetricType = defaultEmbeddingSearchMetricType; 409 if (informationalRankingExpressions != null) { 410 mInformationalRankingExpressions = 411 Collections.unmodifiableList(informationalRankingExpressions); 412 } else { 413 mInformationalRankingExpressions = Collections.emptyList(); 414 } 415 mSearchStringParameters = 416 (searchStringParameters != null) 417 ? Collections.unmodifiableList(searchStringParameters) 418 : Collections.emptyList(); 419 mFilterDocumentIds = 420 (filterDocumentIds != null) 421 ? Collections.unmodifiableList(filterDocumentIds) 422 : Collections.emptyList(); 423 mRetrieveEmbeddingMatchInfos = retrieveEmbeddingMatchInfos; 424 } 425 426 /** Returns how the query terms should match terms in the index. */ 427 @TermMatch getTermMatch()428 public int getTermMatch() { 429 return mTermMatchType; 430 } 431 432 /** 433 * Returns the list of schema types to search for. 434 * 435 * <p>If empty, the query will search over all schema types. 436 */ getFilterSchemas()437 public @NonNull List<String> getFilterSchemas() { 438 if (mSchemas == null) { 439 return Collections.emptyList(); 440 } 441 return mSchemas; 442 } 443 444 /** 445 * Returns the map of schema and target properties to search over. 446 * 447 * <p>If empty, will search over all schema and properties. 448 * 449 * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this 450 * function, rather than calling it multiple times. 451 */ 452 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES) getFilterProperties()453 public @NonNull Map<String, List<String>> getFilterProperties() { 454 Set<String> schemas = mTypePropertyFilters.keySet(); 455 Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemas.size()); 456 for (String schema : schemas) { 457 typePropertyPathsMap.put( 458 schema, 459 Objects.requireNonNull(mTypePropertyFilters.getStringArrayList(schema))); 460 } 461 return typePropertyPathsMap; 462 } 463 464 /** 465 * Returns the list of namespaces to search over. 466 * 467 * <p>If empty, the query will search over all namespaces. 468 */ getFilterNamespaces()469 public @NonNull List<String> getFilterNamespaces() { 470 if (mNamespaces == null) { 471 return Collections.emptyList(); 472 } 473 return mNamespaces; 474 } 475 476 /** 477 * Returns the list of package name filters to search over. 478 * 479 * <p>If empty, the query will search over all packages that the caller has access to. If 480 * package names are specified which caller doesn't have access to, then those package names 481 * will be ignored. 482 */ getFilterPackageNames()483 public @NonNull List<String> getFilterPackageNames() { 484 if (mPackageNames == null) { 485 return Collections.emptyList(); 486 } 487 return mPackageNames; 488 } 489 490 /** 491 * Returns the list of document ids to search over. 492 * 493 * <p>If empty, the query will search over all documents. 494 */ 495 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_DOCUMENT_IDS) getFilterDocumentIds()496 public @NonNull List<String> getFilterDocumentIds() { 497 return mFilterDocumentIds; 498 } 499 500 /** Returns the number of results per page in the result set. */ getResultCountPerPage()501 public int getResultCountPerPage() { 502 return mResultCountPerPage; 503 } 504 505 /** Returns the ranking strategy. */ 506 @RankingStrategy getRankingStrategy()507 public int getRankingStrategy() { 508 return mRankingStrategy; 509 } 510 511 /** Returns the order of returned search results (descending or ascending). */ 512 @Order getOrder()513 public int getOrder() { 514 return mOrder; 515 } 516 517 /** Returns how many documents to generate snippets for. */ getSnippetCount()518 public int getSnippetCount() { 519 return mSnippetCount; 520 } 521 522 /** 523 * Returns how many matches for each property of a matching document to generate snippets for. 524 */ getSnippetCountPerProperty()525 public int getSnippetCountPerProperty() { 526 return mSnippetCountPerProperty; 527 } 528 529 /** Returns the maximum size of a snippet in characters. */ getMaxSnippetSize()530 public int getMaxSnippetSize() { 531 return mMaxSnippetSize; 532 } 533 534 /** 535 * Returns whether to retrieve embedding match infos as a part of {@link 536 * SearchResult#getMatchInfos()} 537 */ 538 @FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO) shouldRetrieveEmbeddingMatchInfos()539 public boolean shouldRetrieveEmbeddingMatchInfos() { 540 return mRetrieveEmbeddingMatchInfos; 541 } 542 543 /** 544 * Returns a map from schema type to property paths to be used for projection. 545 * 546 * <p>If the map is empty, then all properties will be retrieved for all results. 547 * 548 * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this 549 * function, rather than calling it multiple times. 550 * 551 * @return A mapping of schema types to lists of projection strings. 552 */ getProjections()553 public @NonNull Map<String, List<String>> getProjections() { 554 Set<String> schemas = mProjectionTypePropertyMasks.keySet(); 555 Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemas.size()); 556 for (String schema : schemas) { 557 typePropertyPathsMap.put( 558 schema, 559 Objects.requireNonNull( 560 mProjectionTypePropertyMasks.getStringArrayList(schema))); 561 } 562 return typePropertyPathsMap; 563 } 564 565 /** 566 * Returns a map from schema type to property paths to be used for projection. 567 * 568 * <p>If the map is empty, then all properties will be retrieved for all results. 569 * 570 * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this 571 * function, rather than calling it multiple times. 572 * 573 * @return A mapping of schema types to lists of projection {@link PropertyPath} objects. 574 */ getProjectionPaths()575 public @NonNull Map<String, List<PropertyPath>> getProjectionPaths() { 576 Set<String> schemas = mProjectionTypePropertyMasks.keySet(); 577 Map<String, List<PropertyPath>> typePropertyPathsMap = new ArrayMap<>(schemas.size()); 578 for (String schema : schemas) { 579 ArrayList<String> propertyPathList = 580 mProjectionTypePropertyMasks.getStringArrayList(schema); 581 if (propertyPathList != null) { 582 List<PropertyPath> copy = new ArrayList<>(propertyPathList.size()); 583 for (int i = 0; i < propertyPathList.size(); i++) { 584 String p = propertyPathList.get(i); 585 copy.add(new PropertyPath(p)); 586 } 587 typePropertyPathsMap.put(schema, copy); 588 } 589 } 590 return typePropertyPathsMap; 591 } 592 593 /** 594 * Returns properties weights to be used for scoring. 595 * 596 * <p>Calling this function repeatedly is inefficient. Prefer to retain the {@link Map} returned 597 * by this function, rather than calling it multiple times. 598 * 599 * @return a {@link Map} of schema type to an inner-map of property paths of the schema type to 600 * the weight to set for that property. 601 */ getPropertyWeights()602 public @NonNull Map<String, Map<String, Double>> getPropertyWeights() { 603 Set<String> schemaTypes = mTypePropertyWeightsField.keySet(); 604 Map<String, Map<String, Double>> typePropertyWeightsMap = 605 new ArrayMap<>(schemaTypes.size()); 606 for (String schemaType : schemaTypes) { 607 Bundle propertyPathBundle = mTypePropertyWeightsField.getBundle(schemaType); 608 if (propertyPathBundle != null) { 609 Set<String> propertyPaths = propertyPathBundle.keySet(); 610 Map<String, Double> propertyPathWeights = new ArrayMap<>(propertyPaths.size()); 611 for (String propertyPath : propertyPaths) { 612 propertyPathWeights.put( 613 propertyPath, propertyPathBundle.getDouble(propertyPath)); 614 } 615 typePropertyWeightsMap.put(schemaType, propertyPathWeights); 616 } 617 } 618 return typePropertyWeightsMap; 619 } 620 621 /** 622 * Returns properties weights to be used for scoring. 623 * 624 * <p>Calling this function repeatedly is inefficient. Prefer to retain the {@link Map} returned 625 * by this function, rather than calling it multiple times. 626 * 627 * @return a {@link Map} of schema type to an inner-map of property paths of the schema type to 628 * the weight to set for that property. 629 */ getPropertyWeightPaths()630 public @NonNull Map<String, Map<PropertyPath, Double>> getPropertyWeightPaths() { 631 Set<String> schemaTypes = mTypePropertyWeightsField.keySet(); 632 Map<String, Map<PropertyPath, Double>> typePropertyWeightsMap = 633 new ArrayMap<>(schemaTypes.size()); 634 for (String schemaType : schemaTypes) { 635 Bundle propertyPathBundle = mTypePropertyWeightsField.getBundle(schemaType); 636 if (propertyPathBundle != null) { 637 Set<String> propertyPaths = propertyPathBundle.keySet(); 638 Map<PropertyPath, Double> propertyPathWeights = 639 new ArrayMap<>(propertyPaths.size()); 640 for (String propertyPath : propertyPaths) { 641 propertyPathWeights.put( 642 new PropertyPath(propertyPath), 643 propertyPathBundle.getDouble(propertyPath)); 644 } 645 typePropertyWeightsMap.put(schemaType, propertyPathWeights); 646 } 647 } 648 return typePropertyWeightsMap; 649 } 650 651 /** 652 * Get the type of grouping limit to apply, or 0 if {@link Builder#setResultGrouping} was not 653 * called. 654 */ 655 @GroupingType getResultGroupingTypeFlags()656 public int getResultGroupingTypeFlags() { 657 return mResultGroupingTypeFlags; 658 } 659 660 /** 661 * Get the maximum number of results to return for each group. 662 * 663 * @return the maximum number of results to return for each group or 0 if {@link 664 * Builder#setResultGrouping(int, int)} was not called. 665 */ getResultGroupingLimit()666 public int getResultGroupingLimit() { 667 return mGroupingLimit; 668 } 669 670 /** Returns specification on which documents need to be joined. */ getJoinSpec()671 public @Nullable JoinSpec getJoinSpec() { 672 return mJoinSpec; 673 } 674 675 /** 676 * Get the advanced ranking expression, or "" if {@link Builder#setRankingStrategy(String)} was 677 * not called. 678 */ getAdvancedRankingExpression()679 public @NonNull String getAdvancedRankingExpression() { 680 return mAdvancedRankingExpression; 681 } 682 683 /** 684 * Gets a tag to indicate the source of this search, or {@code null} if {@link 685 * Builder#setSearchSourceLogTag(String)} was not called. 686 * 687 * <p>Some AppSearch implementations may log a hash of this tag using statsd. This tag may be 688 * used for tracing performance issues and crashes to a component of an app. 689 * 690 * <p>Call {@link Builder#setSearchSourceLogTag} and give a unique value if you want to 691 * distinguish this search scenario with other search scenarios during performance analysis. 692 * 693 * <p>Under no circumstances will AppSearch log the raw String value using statsd, but it will 694 * be provided as-is to custom {@code AppSearchLogger} implementations you have registered in 695 * your app. 696 */ 697 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG) getSearchSourceLogTag()698 public @Nullable String getSearchSourceLogTag() { 699 return mSearchSourceLogTag; 700 } 701 702 /** 703 * Returns the list of {@link EmbeddingVector} that can be referenced in the query through the 704 * "getEmbeddingParameter({index})" function. 705 * 706 * @see AppSearchSession#search 707 */ 708 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) getEmbeddingParameters()709 public @NonNull List<EmbeddingVector> getEmbeddingParameters() { 710 return mEmbeddingParameters; 711 } 712 713 /** 714 * Returns the default embedding metric type used for embedding search (see {@link 715 * AppSearchSession#search}) and ranking (see {@link 716 * SearchSpec.Builder#setRankingStrategy(String)}). 717 */ 718 @EmbeddingSearchMetricType 719 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) getDefaultEmbeddingSearchMetricType()720 public int getDefaultEmbeddingSearchMetricType() { 721 return mDefaultEmbeddingSearchMetricType; 722 } 723 724 /** 725 * Returns the informational ranking expressions. 726 * 727 * @see Builder#addInformationalRankingExpressions 728 */ 729 @FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS) getInformationalRankingExpressions()730 public @NonNull List<String> getInformationalRankingExpressions() { 731 return mInformationalRankingExpressions; 732 } 733 734 /** 735 * Returns the list of String parameters that can be referenced in the query through the 736 * "getSearchStringParameter({index})" function. 737 * 738 * @see AppSearchSession#search 739 */ 740 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_SEARCH_STRING_PARAMETERS) getSearchStringParameters()741 public @NonNull List<String> getSearchStringParameters() { 742 return mSearchStringParameters; 743 } 744 745 /** Returns whether the NUMERIC_SEARCH feature is enabled. */ isNumericSearchEnabled()746 public boolean isNumericSearchEnabled() { 747 return mEnabledFeatures.contains(FeatureConstants.NUMERIC_SEARCH); 748 } 749 750 /** Returns whether the VERBATIM_SEARCH feature is enabled. */ isVerbatimSearchEnabled()751 public boolean isVerbatimSearchEnabled() { 752 return mEnabledFeatures.contains(FeatureConstants.VERBATIM_SEARCH); 753 } 754 755 /** Returns whether the LIST_FILTER_QUERY_LANGUAGE feature is enabled. */ isListFilterQueryLanguageEnabled()756 public boolean isListFilterQueryLanguageEnabled() { 757 return mEnabledFeatures.contains(FeatureConstants.LIST_FILTER_QUERY_LANGUAGE); 758 } 759 760 /** Returns whether the ScorablePropertyRanking feature is enabled. */ 761 @FlaggedApi(Flags.FLAG_ENABLE_SCORABLE_PROPERTY) isScorablePropertyRankingEnabled()762 public boolean isScorablePropertyRankingEnabled() { 763 return mEnabledFeatures.contains(FeatureConstants.SCHEMA_SCORABLE_PROPERTY_CONFIG); 764 } 765 766 /** Returns whether the LIST_FILTER_HAS_PROPERTY_FUNCTION feature is enabled. */ 767 @FlaggedApi(Flags.FLAG_ENABLE_LIST_FILTER_HAS_PROPERTY_FUNCTION) isListFilterHasPropertyFunctionEnabled()768 public boolean isListFilterHasPropertyFunctionEnabled() { 769 return mEnabledFeatures.contains(FeatureConstants.LIST_FILTER_HAS_PROPERTY_FUNCTION); 770 } 771 772 /** Returns whether the LIST_FILTER_MATCH_SCORE_EXPRESSION_FUNCTION feature is enabled. */ 773 @FlaggedApi(Flags.FLAG_ENABLE_LIST_FILTER_MATCH_SCORE_EXPRESSION_FUNCTION) isListFilterMatchScoreExpressionFunctionEnabled()774 public boolean isListFilterMatchScoreExpressionFunctionEnabled() { 775 return mEnabledFeatures.contains( 776 FeatureConstants.LIST_FILTER_MATCH_SCORE_EXPRESSION_FUNCTION); 777 } 778 779 /** 780 * Get the list of enabled features that the caller is intending to use in this search call. 781 * 782 * @return the set of {@link Features} enabled in this {@link SearchSpec} Entry. 783 * @hide 784 */ getEnabledFeatures()785 public @NonNull List<String> getEnabledFeatures() { 786 return mEnabledFeatures; 787 } 788 789 @Override 790 @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2) writeToParcel(@onNull Parcel dest, int flags)791 public void writeToParcel(@NonNull Parcel dest, int flags) { 792 SearchSpecCreator.writeToParcel(this, dest, flags); 793 } 794 795 /** Builder for {@link SearchSpec objects}. */ 796 public static final class Builder { 797 private List<String> mSchemas = new ArrayList<>(); 798 private List<String> mNamespaces = new ArrayList<>(); 799 private Bundle mTypePropertyFilters = new Bundle(); 800 private List<String> mPackageNames = new ArrayList<>(); 801 private ArraySet<String> mEnabledFeatures = new ArraySet<>(); 802 private Bundle mProjectionTypePropertyMasks = new Bundle(); 803 private Bundle mTypePropertyWeights = new Bundle(); 804 private List<EmbeddingVector> mEmbeddingParameters = new ArrayList<>(); 805 private List<String> mSearchStringParameters = new ArrayList<>(); 806 private List<String> mFilterDocumentIds = new ArrayList<>(); 807 808 private int mResultCountPerPage = DEFAULT_NUM_PER_PAGE; 809 @TermMatch private int mTermMatchType = TERM_MATCH_PREFIX; 810 811 @EmbeddingSearchMetricType 812 private int mDefaultEmbeddingSearchMetricType = EMBEDDING_SEARCH_METRIC_TYPE_COSINE; 813 814 private int mSnippetCount = 0; 815 private int mSnippetCountPerProperty = MAX_SNIPPET_PER_PROPERTY_COUNT; 816 private int mMaxSnippetSize = 0; 817 @RankingStrategy private int mRankingStrategy = RANKING_STRATEGY_NONE; 818 @Order private int mOrder = ORDER_DESCENDING; 819 @GroupingType private int mGroupingTypeFlags = 0; 820 private int mGroupingLimit = 0; 821 private @Nullable JoinSpec mJoinSpec; 822 private String mAdvancedRankingExpression = ""; 823 private List<String> mInformationalRankingExpressions = new ArrayList<>(); 824 private @Nullable String mSearchSourceLogTag; 825 private boolean mRetrieveEmbeddingMatchInfos = false; 826 private boolean mBuilt = false; 827 828 /** Constructs a new {@link Builder} for {@link SearchSpec} objects. */ Builder()829 public Builder() {} 830 831 /** Constructs a new {@link Builder} from the given {@link SearchSpec}. */ 832 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) Builder(@onNull SearchSpec searchSpec)833 public Builder(@NonNull SearchSpec searchSpec) { 834 Objects.requireNonNull(searchSpec); 835 mSchemas = new ArrayList<>(searchSpec.getFilterSchemas()); 836 mNamespaces = new ArrayList<>(searchSpec.getFilterNamespaces()); 837 for (Map.Entry<String, List<String>> entry : 838 searchSpec.getFilterProperties().entrySet()) { 839 addFilterProperties(entry.getKey(), entry.getValue()); 840 } 841 mPackageNames = new ArrayList<>(searchSpec.getFilterPackageNames()); 842 mEnabledFeatures = new ArraySet<>(searchSpec.getEnabledFeatures()); 843 for (Map.Entry<String, List<String>> entry : searchSpec.getProjections().entrySet()) { 844 addProjection(entry.getKey(), entry.getValue()); 845 } 846 for (Map.Entry<String, Map<String, Double>> entry : 847 searchSpec.getPropertyWeights().entrySet()) { 848 setPropertyWeights(entry.getKey(), entry.getValue()); 849 } 850 mEmbeddingParameters = new ArrayList<>(searchSpec.getEmbeddingParameters()); 851 mSearchStringParameters = new ArrayList<>(searchSpec.getSearchStringParameters()); 852 mResultCountPerPage = searchSpec.getResultCountPerPage(); 853 mTermMatchType = searchSpec.getTermMatch(); 854 mDefaultEmbeddingSearchMetricType = searchSpec.getDefaultEmbeddingSearchMetricType(); 855 mSnippetCount = searchSpec.getSnippetCount(); 856 mSnippetCountPerProperty = searchSpec.getSnippetCountPerProperty(); 857 mMaxSnippetSize = searchSpec.getMaxSnippetSize(); 858 mRankingStrategy = searchSpec.getRankingStrategy(); 859 mOrder = searchSpec.getOrder(); 860 mGroupingTypeFlags = searchSpec.getResultGroupingTypeFlags(); 861 mGroupingLimit = searchSpec.getResultGroupingLimit(); 862 mJoinSpec = searchSpec.getJoinSpec(); 863 mAdvancedRankingExpression = searchSpec.getAdvancedRankingExpression(); 864 mInformationalRankingExpressions = 865 new ArrayList<>(searchSpec.getInformationalRankingExpressions()); 866 mSearchSourceLogTag = searchSpec.getSearchSourceLogTag(); 867 mFilterDocumentIds = new ArrayList<>(searchSpec.getFilterDocumentIds()); 868 mRetrieveEmbeddingMatchInfos = searchSpec.shouldRetrieveEmbeddingMatchInfos(); 869 } 870 871 /** 872 * Sets how the query terms should match {@code TermMatchCode} in the index. 873 * 874 * <p>If this method is not called, the default term match type is {@link 875 * SearchSpec#TERM_MATCH_PREFIX}. 876 */ 877 @CanIgnoreReturnValue setTermMatch(@ermMatch int termMatchType)878 public @NonNull Builder setTermMatch(@TermMatch int termMatchType) { 879 Preconditions.checkArgumentInRange( 880 termMatchType, TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX, "Term match type"); 881 resetIfBuilt(); 882 mTermMatchType = termMatchType; 883 return this; 884 } 885 886 /** 887 * Adds a Schema type filter to {@link SearchSpec} Entry. Only search for documents that 888 * have the specified schema types. 889 * 890 * <p>If unset, the query will search over all schema types. 891 */ 892 @CanIgnoreReturnValue addFilterSchemas(@onNull String... schemas)893 public @NonNull Builder addFilterSchemas(@NonNull String... schemas) { 894 Objects.requireNonNull(schemas); 895 resetIfBuilt(); 896 return addFilterSchemas(Arrays.asList(schemas)); 897 } 898 899 /** 900 * Adds a Schema type filter to {@link SearchSpec} Entry. Only search for documents that 901 * have the specified schema types. 902 * 903 * <p>If unset, the query will search over all schema types. 904 */ 905 @CanIgnoreReturnValue addFilterSchemas(@onNull Collection<String> schemas)906 public @NonNull Builder addFilterSchemas(@NonNull Collection<String> schemas) { 907 Objects.requireNonNull(schemas); 908 resetIfBuilt(); 909 mSchemas.addAll(schemas); 910 return this; 911 } 912 913 /** Clears all schema type filters. */ 914 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 915 @CanIgnoreReturnValue clearFilterSchemas()916 public @NonNull Builder clearFilterSchemas() { 917 resetIfBuilt(); 918 mSchemas.clear(); 919 return this; 920 } 921 922 /** 923 * Adds property paths for the specified type to the property filter of {@link SearchSpec} 924 * Entry. Only returns documents that have matches under the specified properties. If 925 * property paths are added for a type, then only the properties referred to will be 926 * searched for results of that type. 927 * 928 * <p>If a property path that is specified isn't present in a result, it will be ignored for 929 * that result. Property paths cannot be null. 930 * 931 * <p>If no property paths are added for a particular type, then all properties of results 932 * of that type will be searched. 933 * 934 * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. 935 * 936 * <p>If property paths are added for the {@link SearchSpec#SCHEMA_TYPE_WILDCARD}, then 937 * those property paths will apply to all results, excepting any types that have their own, 938 * specific property paths set. 939 * 940 * @param schema the {@link AppSearchSchema} that contains the target properties 941 * @param propertyPaths The String version of {@link PropertyPath}. A dot-delimited sequence 942 * of property names. 943 */ 944 @CanIgnoreReturnValue 945 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES) addFilterProperties( @onNull String schema, @NonNull Collection<String> propertyPaths)946 public @NonNull Builder addFilterProperties( 947 @NonNull String schema, @NonNull Collection<String> propertyPaths) { 948 Objects.requireNonNull(schema); 949 Objects.requireNonNull(propertyPaths); 950 resetIfBuilt(); 951 ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size()); 952 for (String propertyPath : propertyPaths) { 953 Objects.requireNonNull(propertyPath); 954 propertyPathsArrayList.add(propertyPath); 955 } 956 mTypePropertyFilters.putStringArrayList(schema, propertyPathsArrayList); 957 return this; 958 } 959 960 /** 961 * Adds property paths for the specified type to the property filter of {@link SearchSpec} 962 * Entry. Only returns documents that have matches under the specified properties. If 963 * property paths are added for a type, then only the properties referred to will be 964 * searched for results of that type. 965 * 966 * @see #addFilterProperties(String, Collection) 967 * @param schema the {@link AppSearchSchema} that contains the target properties 968 * @param propertyPaths The {@link PropertyPath} to search search over 969 */ 970 // Getter method is getFilterProperties 971 @SuppressLint("MissingGetterMatchingBuilder") 972 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_PROPERTIES) addFilterPropertyPaths( @onNull String schema, @NonNull Collection<PropertyPath> propertyPaths)973 public @NonNull Builder addFilterPropertyPaths( 974 @NonNull String schema, @NonNull Collection<PropertyPath> propertyPaths) { 975 Objects.requireNonNull(schema); 976 Objects.requireNonNull(propertyPaths); 977 ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size()); 978 for (PropertyPath propertyPath : propertyPaths) { 979 propertyPathsArrayList.add(propertyPath.toString()); 980 } 981 return addFilterProperties(schema, propertyPathsArrayList); 982 } 983 984 /** Clears the property filters for all schema types. */ 985 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 986 @CanIgnoreReturnValue clearFilterProperties()987 public @NonNull Builder clearFilterProperties() { 988 resetIfBuilt(); 989 mTypePropertyFilters.clear(); 990 return this; 991 } 992 993 /** 994 * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have 995 * the specified namespaces. 996 * 997 * <p>If unset, the query will search over all namespaces. 998 */ 999 @CanIgnoreReturnValue addFilterNamespaces(@onNull String... namespaces)1000 public @NonNull Builder addFilterNamespaces(@NonNull String... namespaces) { 1001 Objects.requireNonNull(namespaces); 1002 resetIfBuilt(); 1003 return addFilterNamespaces(Arrays.asList(namespaces)); 1004 } 1005 1006 /** 1007 * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have 1008 * the specified namespaces. 1009 * 1010 * <p>If unset, the query will search over all namespaces. 1011 */ 1012 @CanIgnoreReturnValue addFilterNamespaces(@onNull Collection<String> namespaces)1013 public @NonNull Builder addFilterNamespaces(@NonNull Collection<String> namespaces) { 1014 Objects.requireNonNull(namespaces); 1015 resetIfBuilt(); 1016 mNamespaces.addAll(namespaces); 1017 return this; 1018 } 1019 1020 /** Clears all namespace filters. */ 1021 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1022 @CanIgnoreReturnValue clearFilterNamespaces()1023 public @NonNull Builder clearFilterNamespaces() { 1024 resetIfBuilt(); 1025 mNamespaces.clear(); 1026 return this; 1027 } 1028 1029 /** 1030 * Adds a package name filter to {@link SearchSpec} Entry. Only search for documents that 1031 * were indexed from the specified packages. 1032 * 1033 * <p>If unset, the query will search over all packages that the caller has access to. If 1034 * package names are specified which caller doesn't have access to, then those package names 1035 * will be ignored. 1036 */ 1037 @CanIgnoreReturnValue addFilterPackageNames(@onNull String... packageNames)1038 public @NonNull Builder addFilterPackageNames(@NonNull String... packageNames) { 1039 Objects.requireNonNull(packageNames); 1040 resetIfBuilt(); 1041 return addFilterPackageNames(Arrays.asList(packageNames)); 1042 } 1043 1044 /** 1045 * Adds a package name filter to {@link SearchSpec} Entry. Only search for documents that 1046 * were indexed from the specified packages. 1047 * 1048 * <p>If unset, the query will search over all packages that the caller has access to. If 1049 * package names are specified which caller doesn't have access to, then those package names 1050 * will be ignored. 1051 */ 1052 @CanIgnoreReturnValue addFilterPackageNames(@onNull Collection<String> packageNames)1053 public @NonNull Builder addFilterPackageNames(@NonNull Collection<String> packageNames) { 1054 Objects.requireNonNull(packageNames); 1055 resetIfBuilt(); 1056 mPackageNames.addAll(packageNames); 1057 return this; 1058 } 1059 1060 /** Clears all package name filters. */ 1061 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1062 @CanIgnoreReturnValue clearFilterPackageNames()1063 public @NonNull Builder clearFilterPackageNames() { 1064 resetIfBuilt(); 1065 mPackageNames.clear(); 1066 return this; 1067 } 1068 1069 /** 1070 * Adds a document id filter to {@link SearchSpec} Entry. Only search for documents that 1071 * have the specified document ids. 1072 * 1073 * <p>If unset, the query will search over all documents. 1074 */ 1075 @CanIgnoreReturnValue 1076 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_DOCUMENT_IDS) addFilterDocumentIds(@onNull String... documentIds)1077 public @NonNull Builder addFilterDocumentIds(@NonNull String... documentIds) { 1078 Objects.requireNonNull(documentIds); 1079 resetIfBuilt(); 1080 return addFilterDocumentIds(Arrays.asList(documentIds)); 1081 } 1082 1083 /** 1084 * Adds a document id filter to {@link SearchSpec} Entry. Only search for documents that 1085 * have the specified document ids. 1086 * 1087 * <p>If unset, the query will search over all documents. 1088 */ 1089 @CanIgnoreReturnValue 1090 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_FILTER_DOCUMENT_IDS) addFilterDocumentIds(@onNull Collection<String> documentIds)1091 public @NonNull Builder addFilterDocumentIds(@NonNull Collection<String> documentIds) { 1092 Objects.requireNonNull(documentIds); 1093 resetIfBuilt(); 1094 mFilterDocumentIds.addAll(documentIds); 1095 return this; 1096 } 1097 1098 /** Clears the document id filters. */ 1099 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1100 @CanIgnoreReturnValue clearFilterDocumentIds()1101 public @NonNull Builder clearFilterDocumentIds() { 1102 resetIfBuilt(); 1103 mFilterDocumentIds.clear(); 1104 return this; 1105 } 1106 1107 /** 1108 * Sets the number of results per page in the returned object. 1109 * 1110 * <p>The default number of results per page is 10. 1111 */ 1112 @CanIgnoreReturnValue setResultCountPerPage( @ntRangefrom = 0, to = MAX_NUM_PER_PAGE) int resultCountPerPage)1113 public @NonNull SearchSpec.Builder setResultCountPerPage( 1114 @IntRange(from = 0, to = MAX_NUM_PER_PAGE) int resultCountPerPage) { 1115 Preconditions.checkArgumentInRange( 1116 resultCountPerPage, 0, MAX_NUM_PER_PAGE, "resultCountPerPage"); 1117 resetIfBuilt(); 1118 mResultCountPerPage = resultCountPerPage; 1119 return this; 1120 } 1121 1122 /** Sets ranking strategy for AppSearch results. */ 1123 @CanIgnoreReturnValue setRankingStrategy(@ankingStrategy int rankingStrategy)1124 public @NonNull Builder setRankingStrategy(@RankingStrategy int rankingStrategy) { 1125 Preconditions.checkArgumentInRange( 1126 rankingStrategy, 1127 RANKING_STRATEGY_NONE, 1128 RANKING_STRATEGY_JOIN_AGGREGATE_SCORE, 1129 "Result ranking strategy"); 1130 resetIfBuilt(); 1131 mRankingStrategy = rankingStrategy; 1132 mAdvancedRankingExpression = ""; 1133 return this; 1134 } 1135 1136 /** 1137 * Enables advanced ranking to score based on {@code advancedRankingExpression}. 1138 * 1139 * <p>This method will set RankingStrategy to {@link 1140 * #RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION}. 1141 * 1142 * <p>The ranking expression is a mathematical expression that will be evaluated to a 1143 * floating-point number of double type representing the score of each document. 1144 * 1145 * <p>Numeric literals, arithmetic operators, mathematical functions, document-based, and 1146 * property-value-based functions are supported to build expressions. 1147 * 1148 * <p>The following are supported arithmetic operators: 1149 * 1150 * <ul> 1151 * <li>Addition(+) 1152 * <li>Subtraction(-) 1153 * <li>Multiplication(*) 1154 * <li>Floating Point Division(/) 1155 * </ul> 1156 * 1157 * <p>Operator precedences are compliant with the Java Language, and parentheses are 1158 * supported. For example, "2.2 + (3 - 4) / 2" evaluates to 1.7. 1159 * 1160 * <p>The following are supported basic mathematical functions: 1161 * 1162 * <ul> 1163 * <li>log(x) - the natural log of x 1164 * <li>log(x, y) - the log of y with base x 1165 * <li>pow(x, y) - x to the power of y 1166 * <li>sqrt(x) 1167 * <li>abs(x) 1168 * <li>sin(x), cos(x), tan(x) 1169 * <li>Example: "max(abs(-100), 10) + pow(2, 10)" will be evaluated to 1124 1170 * </ul> 1171 * 1172 * <p>The following variadic mathematical functions are supported, with n > 0. They also 1173 * accept list value parameters. For example, if V is a value of list type, we can call 1174 * sum(V) to get the sum of all the values in V. List literals are not supported, so a value 1175 * of list type can only be constructed as a return value of some particular document-based 1176 * functions. 1177 * 1178 * <ul> 1179 * <li>max(v1, v2, ..., vn) or max(V) 1180 * <li>min(v1, v2, ..., vn) or min(V) 1181 * <li>len(v1, v2, ..., vn) or len(V) 1182 * <li>sum(v1, v2, ..., vn) or sum(V) 1183 * <li>avg(v1, v2, ..., vn) or avg(V) 1184 * </ul> 1185 * 1186 * <p>Document-based functions must be called via "this", which represents the current 1187 * document being scored. The following are supported document-based functions: 1188 * 1189 * <ul> 1190 * <li>this.documentScore() 1191 * <p>Get the app-provided document score of the current document. This is the same 1192 * score that is returned for {@link #RANKING_STRATEGY_DOCUMENT_SCORE}. 1193 * <li>this.creationTimestamp() 1194 * <p>Get the creation timestamp of the current document. This is the same score that 1195 * is returned for {@link #RANKING_STRATEGY_CREATION_TIMESTAMP}. 1196 * <li>this.relevanceScore() 1197 * <p>Get the BM25F relevance score of the current document in relation to the query 1198 * string. This is the same score that is returned for {@link 1199 * #RANKING_STRATEGY_RELEVANCE_SCORE}. 1200 * <li>this.usageCount(type) and this.usageLastUsedTimestamp(type) 1201 * <p>Get the number of usages or the timestamp of last usage by type for the current 1202 * document, where type must be evaluated to an integer from 1 to 2. Type 1 refers to 1203 * usages reported by {@link AppSearchSession#reportUsage}, and type 2 refers to 1204 * usages reported by {@link GlobalSearchSession#reportSystemUsage}. 1205 * <li>this.childrenRankingSignals() 1206 * <p>Returns a list of children ranking signals calculated by scoring the joined 1207 * documents using the ranking strategy specified in the nested {@link SearchSpec}. 1208 * Currently, a document can only be a child of another document in the context of 1209 * joins. If this function is called without the Join API enabled, a type error will 1210 * be raised. 1211 * <li>this.propertyWeights() 1212 * <p>Returns a list of the normalized weights of the matched properties for the 1213 * current document being scored. Property weights come from what's specified in 1214 * {@link SearchSpec}. After normalizing, each provided weight will be divided by the 1215 * maximum weight, so that each of them will be <= 1. 1216 * <li>this.matchedSemanticScores(getEmbeddingParameter({embedding_index}), {metric}) 1217 * <p>Returns a list of the matched similarity scores from "semanticSearch" in the 1218 * query expression (see also {@link AppSearchSession#search}) based on 1219 * embedding_index and metric. If metric is omitted, it defaults to the metric 1220 * specified in {@link SearchSpec.Builder#setDefaultEmbeddingSearchMetricType(int)}. 1221 * If no "semanticSearch" is called for embedding_index and metric in the query, this 1222 * function will return an empty list. If multiple "semanticSearch"s are called for 1223 * the same embedding_index and metric, this function will return a list of their 1224 * merged scores. 1225 * <p>Example: `this.matchedSemanticScores(getEmbeddingParameter(0), "COSINE")` will 1226 * return a list of matched scores within the range of [0.5, 1], if 1227 * `semanticSearch(getEmbeddingParameter(0), 0.5, 1, "COSINE")` is called in the query 1228 * expression. 1229 * </ul> 1230 * 1231 * <p>Property-value-based functions can be called via the function of 1232 * getScorableProperty(schemaType, propertyPath) 1233 * 1234 * <ul> 1235 * <li>In order to use this function, ScorablePropertyRanking feature must be enabled via 1236 * {@link SearchSpec.Builder#setScorablePropertyRankingEnabled(boolean)}. 1237 * <li>Param 'schemaType' must be a valid AppSearch SchemaType otherwise an error is 1238 * returned. 1239 * <li>Param 'propertyPath' must be valid and scorable otherwise an error is returned. It 1240 * is considered scorable when: 1241 * <ul> 1242 * <li>It is to a property that is set to be enabled for scoring, or that 1243 * <li>It points to a scorable property of nested schema types. 1244 * </ul> 1245 * <li>This function returns a list double values for the matched documents. 1246 * <ul> 1247 * <li>If the matched document's schema is different from 'schemaType', or the 1248 * property under the 'propertyPath' holds no element, an empty list is 1249 * returned. 1250 * </ul> 1251 * <li>Some examples below: 1252 * <p>Suppose that there are two schemas: 'Gmail' and 'Person'. 'Gmail' schema has a 1253 * property 'recipient' with schema type 'Person'. In the advanced ranking expression, 1254 * you can have: 1255 * <ul> 1256 * <li>"sum(getScorableProperty('Gmail', 'viewTimes'))" 1257 * <li>"maxOrDefault(getScorableProperty('Person', 'income'), 0)" 1258 * <li>"sum(getScorableProperty('Gmail', 'recipient.income'))" 1259 * <li>"this.documentScore() + sum(getScorableProperty('Gmail', 'viewTimes'))" 1260 * </ul> 1261 * </ul> 1262 * 1263 * <p>The following functions are provided for enhanced list manipulation. 1264 * 1265 * <ul> 1266 * <li>minOrDefault(V, default_score) 1267 * <p>Returns the minimum value in the input list V or the default_score if the list 1268 * is empty. 1269 * <p>Example: "minOrDefault(this.matchedSemanticScores(getEmbeddingParameter(0)), 1270 * 10)" will return the minimum matched semantic scores or 10 if there is no matched 1271 * score for the current document. 1272 * <p>This function requires the feature {@link 1273 * Features#SEARCH_SPEC_RANKING_FUNCTION_MAX_MIN_OR_DEFAULT}. 1274 * <li>maxOrDefault(V, default_score) 1275 * <p>Returns the maximum value in the input list V or the default_score if the list 1276 * is empty. 1277 * <p>Example: "maxOrDefault(this.matchedSemanticScores(getEmbeddingParameter(0)), 1278 * -10)" will return the maximum matched semantic scores or -10 if there is no matched 1279 * score for the current document. 1280 * <p>This function requires the feature {@link 1281 * Features#SEARCH_SPEC_RANKING_FUNCTION_MAX_MIN_OR_DEFAULT}. 1282 * <li>filterByRange(V, low, high) 1283 * <p>Returns a sublist of V that only contains the elements that fall within the 1284 * specified range [low, high]. 1285 * <p>Example: "filterByRange(this.matchedSemanticScores(getEmbeddingParameter(0)), 0, 1286 * 1)" will return a list of matched semantic scores that are between 0 and 1, 1287 * inclusive. 1288 * <p>This function requires the feature {@link 1289 * Features#SEARCH_SPEC_RANKING_FUNCTION_FILTER_BY_RANGE}. 1290 * </ul> 1291 * 1292 * <p>Some errors may occur when using advanced ranking. 1293 * 1294 * <p>Syntax Error: the expression violates the syntax of the advanced ranking language. 1295 * Below are some examples. 1296 * 1297 * <ul> 1298 * <li>"1 + " - missing operand 1299 * <li>"2 * (1 + 2))" - unbalanced parenthesis 1300 * <li>"2 ^ 3" - unknown operator 1301 * </ul> 1302 * 1303 * <p>Type Error: the expression fails a static type check. Below are some examples. 1304 * 1305 * <ul> 1306 * <li>"sin(2, 3)" - wrong number of arguments for the sin function 1307 * <li>"this.childrenRankingSignals() + 1" - cannot add a list with a number 1308 * <li>"this.propertyWeights()" - the final type of the overall expression cannot be a 1309 * list, which can be fixed by "max(this.propertyWeights())" 1310 * <li>"abs(this.propertyWeights())" - the abs function does not support list type 1311 * arguments 1312 * <li>"print(2)" - unknown function 1313 * </ul> 1314 * 1315 * <p>Evaluation Error: an error occurred while evaluating the value of the expression. 1316 * Below are some examples. 1317 * 1318 * <ul> 1319 * <li>"1 / 0", "log(0)", "1 + sqrt(-1)" - getting a non-finite value in the middle of 1320 * evaluation 1321 * <li>"this.usageCount(1 + 0.5)" - expect the argument to be an integer. Note that this 1322 * is not a type error and "this.usageCount(1.5 + 1/2)" can succeed without any issues 1323 * <li>"this.documentScore()" - in case of an IO error, this will be an evaluation error 1324 * </ul> 1325 * 1326 * <p>Syntax errors and type errors will fail the entire search and will cause {@link 1327 * SearchResults#getNextPage} to throw an {@link AppSearchException} with the result code of 1328 * {@link AppSearchResult#RESULT_INVALID_ARGUMENT}. 1329 * 1330 * <p>Evaluation errors will result in the offending documents receiving the default score. 1331 * For {@link #ORDER_DESCENDING}, the default score will be 0, for {@link #ORDER_ASCENDING} 1332 * the default score will be infinity. 1333 * 1334 * @param advancedRankingExpression a non-empty string representing the ranking expression. 1335 */ 1336 @CanIgnoreReturnValue setRankingStrategy(@onNull String advancedRankingExpression)1337 public @NonNull Builder setRankingStrategy(@NonNull String advancedRankingExpression) { 1338 Preconditions.checkStringNotEmpty(advancedRankingExpression); 1339 resetIfBuilt(); 1340 mRankingStrategy = RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION; 1341 mAdvancedRankingExpression = advancedRankingExpression; 1342 return this; 1343 } 1344 1345 /** 1346 * Adds informational ranking expressions to be evaluated for each document in the search 1347 * result. The values of these expressions will be returned to the caller via {@link 1348 * SearchResult#getInformationalRankingSignals()}. These expressions are purely for the 1349 * caller to retrieve additional information about the result and have no effect on ranking. 1350 * 1351 * <p>The syntax is exactly the same as specified in {@link 1352 * SearchSpec.Builder#setRankingStrategy(String)}. 1353 */ 1354 @CanIgnoreReturnValue 1355 @FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS) addInformationalRankingExpressions( @onNull String... informationalRankingExpressions)1356 public @NonNull Builder addInformationalRankingExpressions( 1357 @NonNull String... informationalRankingExpressions) { 1358 Objects.requireNonNull(informationalRankingExpressions); 1359 resetIfBuilt(); 1360 return addInformationalRankingExpressions( 1361 Arrays.asList(informationalRankingExpressions)); 1362 } 1363 1364 /** 1365 * Adds informational ranking expressions to be evaluated for each document in the search 1366 * result. The values of these expressions will be returned to the caller via {@link 1367 * SearchResult#getInformationalRankingSignals()}. These expressions are purely for the 1368 * caller to retrieve additional information about the result and have no effect on ranking. 1369 * 1370 * <p>The syntax is exactly the same as specified in {@link 1371 * SearchSpec.Builder#setRankingStrategy(String)}. 1372 */ 1373 @CanIgnoreReturnValue 1374 @FlaggedApi(Flags.FLAG_ENABLE_INFORMATIONAL_RANKING_EXPRESSIONS) addInformationalRankingExpressions( @onNull Collection<String> informationalRankingExpressions)1375 public @NonNull Builder addInformationalRankingExpressions( 1376 @NonNull Collection<String> informationalRankingExpressions) { 1377 Objects.requireNonNull(informationalRankingExpressions); 1378 resetIfBuilt(); 1379 mInformationalRankingExpressions.addAll(informationalRankingExpressions); 1380 return this; 1381 } 1382 1383 /** Clears all informational ranking expressions. */ 1384 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1385 @CanIgnoreReturnValue clearInformationalRankingExpressions()1386 public @NonNull Builder clearInformationalRankingExpressions() { 1387 resetIfBuilt(); 1388 mInformationalRankingExpressions.clear(); 1389 return this; 1390 } 1391 1392 /** 1393 * Sets an optional log tag to indicate the source of this search. 1394 * 1395 * <p>Some AppSearch implementations may log a hash of this tag using statsd. This tag may 1396 * be used for tracing performance issues and crashes to a component of an app. 1397 * 1398 * <p>Call this method and give a unique value if you want to distinguish this search 1399 * scenario with other search scenarios during performance analysis. 1400 * 1401 * <p>Under no circumstances will AppSearch log the raw String value using statsd, but it 1402 * will be provided as-is to custom {@code AppSearchLogger} implementations you have 1403 * registered in your app. 1404 * 1405 * @param searchSourceLogTag A String to indicate the source caller of this search. It is 1406 * used to label the search statsd for performance analysis. It is not the tag we are 1407 * using in {@link android.util.Log}. The length of the teg should between 1 and 100. 1408 */ 1409 @CanIgnoreReturnValue 1410 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_SET_SEARCH_SOURCE_LOG_TAG) setSearchSourceLogTag(@onNull String searchSourceLogTag)1411 public @NonNull Builder setSearchSourceLogTag(@NonNull String searchSourceLogTag) { 1412 Preconditions.checkStringNotEmpty(searchSourceLogTag); 1413 Preconditions.checkArgument( 1414 searchSourceLogTag.length() <= 100, 1415 "The maximum supported tag length is 100. This tag is too long: " 1416 + searchSourceLogTag.length()); 1417 resetIfBuilt(); 1418 mSearchSourceLogTag = searchSourceLogTag; 1419 return this; 1420 } 1421 1422 /** Clears the log tag that indicates the source of this search. */ 1423 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1424 @CanIgnoreReturnValue clearSearchSourceLogTag()1425 public @NonNull Builder clearSearchSourceLogTag() { 1426 resetIfBuilt(); 1427 mSearchSourceLogTag = null; 1428 return this; 1429 } 1430 1431 /** 1432 * Sets the order of returned search results, the default is {@link #ORDER_DESCENDING}, 1433 * meaning that results with higher scores come first. 1434 * 1435 * <p>This order field will be ignored if RankingStrategy = {@code RANKING_STRATEGY_NONE}. 1436 */ 1437 @CanIgnoreReturnValue setOrder(@rder int order)1438 public @NonNull Builder setOrder(@Order int order) { 1439 Preconditions.checkArgumentInRange( 1440 order, ORDER_DESCENDING, ORDER_ASCENDING, "Result ranking order"); 1441 resetIfBuilt(); 1442 mOrder = order; 1443 return this; 1444 } 1445 1446 /** 1447 * Sets the {@code snippetCount} such that the first {@code snippetCount} documents based on 1448 * the ranking strategy will have snippet information provided. 1449 * 1450 * <p>The list returned from {@link SearchResult#getMatchInfos} will contain at most this 1451 * many entries. 1452 * 1453 * <p>If set to 0 (default), snippeting is disabled and the list returned from {@link 1454 * SearchResult#getMatchInfos} will be empty. 1455 */ 1456 @CanIgnoreReturnValue setSnippetCount( @ntRangefrom = 0, to = MAX_SNIPPET_COUNT) int snippetCount)1457 public @NonNull SearchSpec.Builder setSnippetCount( 1458 @IntRange(from = 0, to = MAX_SNIPPET_COUNT) int snippetCount) { 1459 Preconditions.checkArgumentInRange(snippetCount, 0, MAX_SNIPPET_COUNT, "snippetCount"); 1460 resetIfBuilt(); 1461 mSnippetCount = snippetCount; 1462 return this; 1463 } 1464 1465 /** 1466 * Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty} 1467 * snippets for each property of each {@link GenericDocument} will contain snippet 1468 * information. 1469 * 1470 * <p>If set to 0, snippeting is disabled and the list returned from {@link 1471 * SearchResult#getMatchInfos} will be empty. 1472 * 1473 * <p>The default behavior is to snippet all matches a property contains, up to the maximum 1474 * value of 10,000. 1475 */ 1476 @CanIgnoreReturnValue setSnippetCountPerProperty( @ntRangefrom = 0, to = MAX_SNIPPET_PER_PROPERTY_COUNT) int snippetCountPerProperty)1477 public @NonNull SearchSpec.Builder setSnippetCountPerProperty( 1478 @IntRange(from = 0, to = MAX_SNIPPET_PER_PROPERTY_COUNT) 1479 int snippetCountPerProperty) { 1480 Preconditions.checkArgumentInRange( 1481 snippetCountPerProperty, 1482 0, 1483 MAX_SNIPPET_PER_PROPERTY_COUNT, 1484 "snippetCountPerProperty"); 1485 resetIfBuilt(); 1486 mSnippetCountPerProperty = snippetCountPerProperty; 1487 return this; 1488 } 1489 1490 /** 1491 * Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at {@code 1492 * maxSnippetSize/2} bytes before the middle of the matching token and end at {@code 1493 * maxSnippetSize/2} bytes after the middle of the matching token. It respects token 1494 * boundaries, therefore the returned window may be smaller than requested. 1495 * 1496 * <p>Setting {@code maxSnippetSize} to 0 will disable windowing and an empty String will be 1497 * returned. If matches enabled is also set to false, then snippeting is disabled. 1498 * 1499 * <p>For example, {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" 1500 * will return a window of "bar baz bat" which is only 11 bytes long. 1501 */ 1502 @CanIgnoreReturnValue setMaxSnippetSize( @ntRangefrom = 0, to = MAX_SNIPPET_SIZE_LIMIT) int maxSnippetSize)1503 public @NonNull SearchSpec.Builder setMaxSnippetSize( 1504 @IntRange(from = 0, to = MAX_SNIPPET_SIZE_LIMIT) int maxSnippetSize) { 1505 Preconditions.checkArgumentInRange( 1506 maxSnippetSize, 0, MAX_SNIPPET_SIZE_LIMIT, "maxSnippetSize"); 1507 resetIfBuilt(); 1508 mMaxSnippetSize = maxSnippetSize; 1509 return this; 1510 } 1511 1512 /** 1513 * Sets whether to retrieve embedding match infos as a part of {@link 1514 * SearchResult#getMatchInfos()}. 1515 * 1516 * <p>Note that this does not modify the snippet count fields, and any retrieved embedding 1517 * match infos also count toward the limit set in {@link SearchSpec#getSnippetCount()} and 1518 * {@link SearchSpec#getSnippetCountPerProperty()}. 1519 */ 1520 @CanIgnoreReturnValue 1521 @FlaggedApi(Flags.FLAG_ENABLE_EMBEDDING_MATCH_INFO) 1522 @SuppressLint("MissingGetterMatchingBuilder") setRetrieveEmbeddingMatchInfos( boolean retrieveEmbeddingMatchInfos)1523 public @NonNull Builder setRetrieveEmbeddingMatchInfos( 1524 boolean retrieveEmbeddingMatchInfos) { 1525 resetIfBuilt(); 1526 mRetrieveEmbeddingMatchInfos = retrieveEmbeddingMatchInfos; 1527 return this; 1528 } 1529 1530 /** 1531 * Adds property paths for the specified type to be used for projection. If property paths 1532 * are added for a type, then only the properties referred to will be retrieved for results 1533 * of that type. If a property path that is specified isn't present in a result, it will be 1534 * ignored for that result. Property paths cannot be null. 1535 * 1536 * @see #addProjectionPaths 1537 * @param schema a string corresponding to the schema to add projections to. 1538 * @param propertyPaths the projections to add. 1539 */ 1540 @CanIgnoreReturnValue addProjection( @onNull String schema, @NonNull Collection<String> propertyPaths)1541 public @NonNull SearchSpec.Builder addProjection( 1542 @NonNull String schema, @NonNull Collection<String> propertyPaths) { 1543 Objects.requireNonNull(schema); 1544 Objects.requireNonNull(propertyPaths); 1545 resetIfBuilt(); 1546 ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size()); 1547 for (String propertyPath : propertyPaths) { 1548 Objects.requireNonNull(propertyPath); 1549 propertyPathsArrayList.add(propertyPath); 1550 } 1551 mProjectionTypePropertyMasks.putStringArrayList(schema, propertyPathsArrayList); 1552 return this; 1553 } 1554 1555 /** 1556 * Adds property paths for the specified type to be used for projection. If property paths 1557 * are added for a type, then only the properties referred to will be retrieved for results 1558 * of that type. If a property path that is specified isn't present in a result, it will be 1559 * ignored for that result. Property paths cannot be null. 1560 * 1561 * <p>If no property paths are added for a particular type, then all properties of results 1562 * of that type will be retrieved. 1563 * 1564 * <p>If property path is added for the {@link SearchSpec#SCHEMA_TYPE_WILDCARD}, then those 1565 * property paths will apply to all results, excepting any types that have their own, 1566 * specific property paths set. 1567 * 1568 * <p>Suppose the following document is in the index. 1569 * 1570 * <pre>{@code 1571 * Email: Document { 1572 * sender: Document { 1573 * name: "Mr. Person" 1574 * email: "mrperson123@google.com" 1575 * } 1576 * recipients: [ 1577 * Document { 1578 * name: "John Doe" 1579 * email: "johndoe123@google.com" 1580 * } 1581 * Document { 1582 * name: "Jane Doe" 1583 * email: "janedoe123@google.com" 1584 * } 1585 * ] 1586 * subject: "IMPORTANT" 1587 * body: "Limited time offer!" 1588 * } 1589 * }</pre> 1590 * 1591 * <p>Then, suppose that a query for "important" is issued with the following projection 1592 * type property paths: 1593 * 1594 * <pre>{@code 1595 * {schema: "Email", ["subject", "sender.name", "recipients.name"]} 1596 * }</pre> 1597 * 1598 * <p>The above document will be returned as: 1599 * 1600 * <pre>{@code 1601 * Email: Document { 1602 * sender: Document { 1603 * name: "Mr. Body" 1604 * } 1605 * recipients: [ 1606 * Document { 1607 * name: "John Doe" 1608 * } 1609 * Document { 1610 * name: "Jane Doe" 1611 * } 1612 * ] 1613 * subject: "IMPORTANT" 1614 * } 1615 * }</pre> 1616 * 1617 * @param schema a string corresponding to the schema to add projections to. 1618 * @param propertyPaths the projections to add. 1619 */ 1620 @CanIgnoreReturnValue addProjectionPaths( @onNull String schema, @NonNull Collection<PropertyPath> propertyPaths)1621 public @NonNull SearchSpec.Builder addProjectionPaths( 1622 @NonNull String schema, @NonNull Collection<PropertyPath> propertyPaths) { 1623 Objects.requireNonNull(schema); 1624 Objects.requireNonNull(propertyPaths); 1625 ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size()); 1626 for (PropertyPath propertyPath : propertyPaths) { 1627 propertyPathsArrayList.add(propertyPath.toString()); 1628 } 1629 return addProjection(schema, propertyPathsArrayList); 1630 } 1631 1632 /** Clears the projections for all schema types. */ 1633 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1634 @CanIgnoreReturnValue clearProjections()1635 public @NonNull Builder clearProjections() { 1636 resetIfBuilt(); 1637 mProjectionTypePropertyMasks.clear(); 1638 return this; 1639 } 1640 1641 /** 1642 * Sets the maximum number of results to return for each group, where groups are defined by 1643 * grouping type. 1644 * 1645 * <p>Calling this method will override any previous calls. So calling {@code 1646 * setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 7)} and then calling {@code 1647 * setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 2)} will result in only the latter, a limit 1648 * of two results per package, being applied. Or calling {@code setResultGrouping 1649 * (GROUPING_TYPE_PER_PACKAGE, 1)} and then calling {@code setResultGrouping 1650 * (GROUPING_TYPE_PER_PACKAGE | GROUPING_PER_NAMESPACE, 5)} will result in five results per 1651 * package per namespace. 1652 * 1653 * @param groupingTypeFlags One or more combination of grouping types. 1654 * @param limit Number of results to return per {@code groupingTypeFlags}. 1655 * @throws IllegalArgumentException if groupingTypeFlags is zero. 1656 */ 1657 // Individual parameters available from getResultGroupingTypeFlags and 1658 // getResultGroupingLimit 1659 @CanIgnoreReturnValue 1660 @SuppressLint("MissingGetterMatchingBuilder") setResultGrouping(@roupingType int groupingTypeFlags, int limit)1661 public @NonNull Builder setResultGrouping(@GroupingType int groupingTypeFlags, int limit) { 1662 Preconditions.checkState( 1663 groupingTypeFlags != 0, "Result grouping type cannot be zero."); 1664 resetIfBuilt(); 1665 mGroupingTypeFlags = groupingTypeFlags; 1666 mGroupingLimit = limit; 1667 return this; 1668 } 1669 1670 /** Clears the result grouping and limit. */ 1671 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1672 @CanIgnoreReturnValue clearResultGrouping()1673 public @NonNull Builder clearResultGrouping() { 1674 resetIfBuilt(); 1675 mGroupingTypeFlags = 0; 1676 mGroupingLimit = 0; 1677 return this; 1678 } 1679 1680 /** 1681 * Sets property weights by schema type and property path. 1682 * 1683 * <p>Property weights are used to promote and demote query term matches within a {@link 1684 * GenericDocument} property when applying scoring. 1685 * 1686 * <p>Property weights must be positive values (greater than 0). A property's weight is 1687 * multiplied with that property's scoring contribution. This means weights set between 0.0 1688 * and 1.0 demote scoring contributions by a term match within the property. Weights set 1689 * above 1.0 promote scoring contributions by a term match within the property. 1690 * 1691 * <p>Properties that exist in the {@link AppSearchSchema}, but do not have a weight 1692 * explicitly set will be given a default weight of 1.0. 1693 * 1694 * <p>Weights set for property paths that do not exist in the {@link AppSearchSchema} will 1695 * be discarded and not affect scoring. 1696 * 1697 * <p><b>NOTE:</b> Property weights only affect scoring for query-dependent scoring 1698 * strategies, such as {@link #RANKING_STRATEGY_RELEVANCE_SCORE}. 1699 * 1700 * @param schemaType the schema type to set property weights for. 1701 * @param propertyPathWeights a {@link Map} of property paths of the schema type to the 1702 * weight to set for that property. 1703 * @throws IllegalArgumentException if a weight is equal to or less than 0.0. 1704 */ 1705 @CanIgnoreReturnValue setPropertyWeights( @onNull String schemaType, @NonNull Map<String, Double> propertyPathWeights)1706 public @NonNull SearchSpec.Builder setPropertyWeights( 1707 @NonNull String schemaType, @NonNull Map<String, Double> propertyPathWeights) { 1708 Objects.requireNonNull(schemaType); 1709 Objects.requireNonNull(propertyPathWeights); 1710 1711 Bundle propertyPathBundle = new Bundle(); 1712 for (Map.Entry<String, Double> propertyPathWeightEntry : 1713 propertyPathWeights.entrySet()) { 1714 String propertyPath = Objects.requireNonNull(propertyPathWeightEntry.getKey()); 1715 Double weight = Objects.requireNonNull(propertyPathWeightEntry.getValue()); 1716 if (weight <= 0.0) { 1717 throw new IllegalArgumentException( 1718 "Cannot set non-positive property weight " 1719 + "value " 1720 + weight 1721 + " for property path: " 1722 + propertyPath); 1723 } 1724 propertyPathBundle.putDouble(propertyPath, weight); 1725 } 1726 mTypePropertyWeights.putBundle(schemaType, propertyPathBundle); 1727 return this; 1728 } 1729 1730 /** Clears the property weights for all schema types. */ 1731 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1732 @CanIgnoreReturnValue clearPropertyWeights()1733 public @NonNull Builder clearPropertyWeights() { 1734 resetIfBuilt(); 1735 mTypePropertyWeights.clear(); 1736 return this; 1737 } 1738 1739 /** 1740 * Specifies which documents to join with, and how to join. 1741 * 1742 * <p>If the ranking strategy is {@link #RANKING_STRATEGY_JOIN_AGGREGATE_SCORE}, and the 1743 * JoinSpec is null, {@link #build} will throw an {@link AppSearchException}. 1744 * 1745 * @param joinSpec a specification on how to perform the Join operation. 1746 */ 1747 @CanIgnoreReturnValue setJoinSpec(@onNull JoinSpec joinSpec)1748 public @NonNull Builder setJoinSpec(@NonNull JoinSpec joinSpec) { 1749 resetIfBuilt(); 1750 mJoinSpec = Objects.requireNonNull(joinSpec); 1751 return this; 1752 } 1753 1754 /** Clears the {@link JoinSpec}. */ 1755 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1756 @CanIgnoreReturnValue clearJoinSpec()1757 public @NonNull Builder clearJoinSpec() { 1758 resetIfBuilt(); 1759 mJoinSpec = null; 1760 return this; 1761 } 1762 1763 /** 1764 * Sets property weights by schema type and property path. 1765 * 1766 * <p>Property weights are used to promote and demote query term matches within a {@link 1767 * GenericDocument} property when applying scoring. 1768 * 1769 * <p>Property weights must be positive values (greater than 0). A property's weight is 1770 * multiplied with that property's scoring contribution. This means weights set between 0.0 1771 * and 1.0 demote scoring contributions by a term match within the property. Weights set 1772 * above 1.0 promote scoring contributions by a term match within the property. 1773 * 1774 * <p>Properties that exist in the {@link AppSearchSchema}, but do not have a weight 1775 * explicitly set will be given a default weight of 1.0. 1776 * 1777 * <p>Weights set for property paths that do not exist in the {@link AppSearchSchema} will 1778 * be discarded and not affect scoring. 1779 * 1780 * <p><b>NOTE:</b> Property weights only affect scoring for query-dependent scoring 1781 * strategies, such as {@link #RANKING_STRATEGY_RELEVANCE_SCORE}. 1782 * 1783 * @param schemaType the schema type to set property weights for. 1784 * @param propertyPathWeights a {@link Map} of property paths of the schema type to the 1785 * weight to set for that property. 1786 * @throws IllegalArgumentException if a weight is equal to or less than 0.0. 1787 */ 1788 @CanIgnoreReturnValue setPropertyWeightPaths( @onNull String schemaType, @NonNull Map<PropertyPath, Double> propertyPathWeights)1789 public @NonNull SearchSpec.Builder setPropertyWeightPaths( 1790 @NonNull String schemaType, 1791 @NonNull Map<PropertyPath, Double> propertyPathWeights) { 1792 Objects.requireNonNull(propertyPathWeights); 1793 1794 Map<String, Double> propertyWeights = new ArrayMap<>(propertyPathWeights.size()); 1795 for (Map.Entry<PropertyPath, Double> propertyPathWeightEntry : 1796 propertyPathWeights.entrySet()) { 1797 PropertyPath propertyPath = 1798 Objects.requireNonNull(propertyPathWeightEntry.getKey()); 1799 propertyWeights.put(propertyPath.toString(), propertyPathWeightEntry.getValue()); 1800 } 1801 return setPropertyWeights(schemaType, propertyWeights); 1802 } 1803 1804 /** 1805 * Adds an embedding search to {@link SearchSpec} Entry, which will be referred in the query 1806 * expression and the ranking expression for embedding search. 1807 * 1808 * @see AppSearchSession#search 1809 * @see SearchSpec.Builder#setRankingStrategy(String) 1810 */ 1811 @CanIgnoreReturnValue 1812 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) addEmbeddingParameters( @onNull EmbeddingVector... searchEmbeddings)1813 public @NonNull Builder addEmbeddingParameters( 1814 @NonNull EmbeddingVector... searchEmbeddings) { 1815 Objects.requireNonNull(searchEmbeddings); 1816 resetIfBuilt(); 1817 return addEmbeddingParameters(Arrays.asList(searchEmbeddings)); 1818 } 1819 1820 /** 1821 * Adds an embedding search to {@link SearchSpec} Entry, which will be referred in the query 1822 * expression and the ranking expression for embedding search. 1823 * 1824 * @see AppSearchSession#search 1825 * @see SearchSpec.Builder#setRankingStrategy(String) 1826 */ 1827 @CanIgnoreReturnValue 1828 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) addEmbeddingParameters( @onNull Collection<EmbeddingVector> searchEmbeddings)1829 public @NonNull Builder addEmbeddingParameters( 1830 @NonNull Collection<EmbeddingVector> searchEmbeddings) { 1831 Objects.requireNonNull(searchEmbeddings); 1832 resetIfBuilt(); 1833 mEmbeddingParameters.addAll(searchEmbeddings); 1834 return this; 1835 } 1836 1837 /** Clears the embedding parameters. */ 1838 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1839 @CanIgnoreReturnValue clearEmbeddingParameters()1840 public @NonNull Builder clearEmbeddingParameters() { 1841 resetIfBuilt(); 1842 mEmbeddingParameters.clear(); 1843 return this; 1844 } 1845 1846 /** 1847 * Sets the default embedding metric type used for embedding search (see {@link 1848 * AppSearchSession#search}) and ranking (see {@link 1849 * SearchSpec.Builder#setRankingStrategy(String)}). 1850 * 1851 * <p>If this method is not called, the default embedding search metric type is {@link 1852 * SearchSpec#EMBEDDING_SEARCH_METRIC_TYPE_COSINE}. Metrics specified within 1853 * "semanticSearch" or "matchedSemanticScores" functions in search/ranking expressions will 1854 * override this default. 1855 */ 1856 @CanIgnoreReturnValue 1857 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) setDefaultEmbeddingSearchMetricType( @mbeddingSearchMetricType int defaultEmbeddingSearchMetricType)1858 public @NonNull Builder setDefaultEmbeddingSearchMetricType( 1859 @EmbeddingSearchMetricType int defaultEmbeddingSearchMetricType) { 1860 Preconditions.checkArgumentInRange( 1861 defaultEmbeddingSearchMetricType, 1862 EMBEDDING_SEARCH_METRIC_TYPE_COSINE, 1863 EMBEDDING_SEARCH_METRIC_TYPE_EUCLIDEAN, 1864 "Embedding search metric type"); 1865 resetIfBuilt(); 1866 mDefaultEmbeddingSearchMetricType = defaultEmbeddingSearchMetricType; 1867 return this; 1868 } 1869 1870 /** 1871 * Adds Strings to the list of String parameters that can be referenced in the query through 1872 * the "getSearchStringParameter({index})" function. 1873 * 1874 * @see AppSearchSession#search 1875 */ 1876 @CanIgnoreReturnValue 1877 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_SEARCH_STRING_PARAMETERS) addSearchStringParameters( @onNull String... searchStringParameters)1878 public @NonNull Builder addSearchStringParameters( 1879 @NonNull String... searchStringParameters) { 1880 Objects.requireNonNull(searchStringParameters); 1881 resetIfBuilt(); 1882 return addSearchStringParameters(Arrays.asList(searchStringParameters)); 1883 } 1884 1885 /** 1886 * Adds Strings to the list of String parameters that can be referenced in the query through 1887 * the "getSearchStringParameter({index})" function. 1888 * 1889 * @see AppSearchSession#search 1890 */ 1891 @CanIgnoreReturnValue 1892 @FlaggedApi(Flags.FLAG_ENABLE_SEARCH_SPEC_SEARCH_STRING_PARAMETERS) addSearchStringParameters( @onNull List<String> searchStringParameters)1893 public @NonNull Builder addSearchStringParameters( 1894 @NonNull List<String> searchStringParameters) { 1895 Objects.requireNonNull(searchStringParameters); 1896 resetIfBuilt(); 1897 mSearchStringParameters.addAll(searchStringParameters); 1898 return this; 1899 } 1900 1901 /** 1902 * Clears the list of String parameters that can be referenced in the query through the 1903 * "getSearchStringParameter({index})" function. 1904 */ 1905 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 1906 @CanIgnoreReturnValue clearSearchStringParameters()1907 public @NonNull Builder clearSearchStringParameters() { 1908 resetIfBuilt(); 1909 mSearchStringParameters.clear(); 1910 return this; 1911 } 1912 1913 /** 1914 * Sets the NUMERIC_SEARCH feature as enabled/disabled according to the enabled parameter. 1915 * 1916 * @param enabled Enables the feature if true, otherwise disables it. 1917 * <p>If disabled, disallows use of {@link 1918 * AppSearchSchema.LongPropertyConfig#INDEXING_TYPE_RANGE} and all other numeric 1919 * querying features. 1920 */ 1921 @CanIgnoreReturnValue setNumericSearchEnabled(boolean enabled)1922 public @NonNull Builder setNumericSearchEnabled(boolean enabled) { 1923 modifyEnabledFeature(FeatureConstants.NUMERIC_SEARCH, enabled); 1924 return this; 1925 } 1926 1927 /** 1928 * Sets the VERBATIM_SEARCH feature as enabled/disabled according to the enabled parameter. 1929 * 1930 * @param enabled Enables the feature if true, otherwise disables it 1931 * <p>If disabled, disallows use of {@link 1932 * AppSearchSchema.StringPropertyConfig#TOKENIZER_TYPE_VERBATIM} and all other verbatim 1933 * search features within the query language that allows clients to search using the 1934 * verbatim string operator. 1935 * <p>For example, The verbatim string operator '"foo/bar" OR baz' will ensure that 1936 * 'foo/bar' is treated as a single 'verbatim' token. 1937 */ 1938 @CanIgnoreReturnValue setVerbatimSearchEnabled(boolean enabled)1939 public @NonNull Builder setVerbatimSearchEnabled(boolean enabled) { 1940 modifyEnabledFeature(FeatureConstants.VERBATIM_SEARCH, enabled); 1941 return this; 1942 } 1943 1944 /** 1945 * Sets the LIST_FILTER_QUERY_LANGUAGE feature as enabled/disabled according to the enabled 1946 * parameter. 1947 * 1948 * @param enabled Enables the feature if true, otherwise disables it. 1949 * <p>This feature covers the expansion of the query language to conform to the 1950 * definition of the list filters language (https://aip.dev/160). This includes: 1951 * <ul> 1952 * <li>addition of explicit 'AND' and 'NOT' operators 1953 * <li>property restricts are allowed with grouping (ex. "prop:(a OR b)") 1954 * <li>addition of custom functions to control matching 1955 * </ul> 1956 * <p>The newly added custom functions covered by this feature are: 1957 * <ul> 1958 * <li>createList(String...) 1959 * <li>termSearch(String, {@code List<String>}) 1960 * </ul> 1961 * <p>createList takes a variable number of strings and returns a list of strings. It is 1962 * for use with termSearch. 1963 * <p>termSearch takes a query string that will be parsed according to the supported 1964 * query language and an optional list of strings that specify the properties to be 1965 * restricted to. This exists as a convenience for multiple property restricts. So, for 1966 * example, the query "(subject:foo OR body:foo) (subject:bar OR body:bar)" could be 1967 * rewritten as "termSearch(\"foo bar\", createList(\"subject\", \"bar\"))" 1968 */ 1969 @CanIgnoreReturnValue setListFilterQueryLanguageEnabled(boolean enabled)1970 public @NonNull Builder setListFilterQueryLanguageEnabled(boolean enabled) { 1971 modifyEnabledFeature(FeatureConstants.LIST_FILTER_QUERY_LANGUAGE, enabled); 1972 return this; 1973 } 1974 1975 /** 1976 * Sets the LIST_FILTER_HAS_PROPERTY_FUNCTION feature as enabled/disabled according to the 1977 * enabled parameter. 1978 * 1979 * @param enabled Enables the feature if true, otherwise disables it 1980 * <p>If disabled, disallows the use of the "hasProperty" function. See {@link 1981 * AppSearchSession#search} for more details about the function. 1982 */ 1983 @CanIgnoreReturnValue 1984 @FlaggedApi(Flags.FLAG_ENABLE_LIST_FILTER_HAS_PROPERTY_FUNCTION) setListFilterHasPropertyFunctionEnabled(boolean enabled)1985 public @NonNull Builder setListFilterHasPropertyFunctionEnabled(boolean enabled) { 1986 modifyEnabledFeature(FeatureConstants.LIST_FILTER_HAS_PROPERTY_FUNCTION, enabled); 1987 return this; 1988 } 1989 1990 /** 1991 * Sets the LIST_FILTER_MATCH_SCORE_EXPRESSION_FUNCTION feature as enabled/disabled 1992 * according to the enabled parameter. 1993 * 1994 * <p>If not enabled, the use of the "matchScoreExpression" function is disallowed. See 1995 * {@link AppSearchSession#search} for more details about the function. 1996 * 1997 * @param enabled Enables the feature if true, otherwise disables it 1998 */ 1999 @CanIgnoreReturnValue 2000 @FlaggedApi(Flags.FLAG_ENABLE_LIST_FILTER_MATCH_SCORE_EXPRESSION_FUNCTION) setListFilterMatchScoreExpressionFunctionEnabled(boolean enabled)2001 public @NonNull Builder setListFilterMatchScoreExpressionFunctionEnabled(boolean enabled) { 2002 modifyEnabledFeature( 2003 FeatureConstants.LIST_FILTER_MATCH_SCORE_EXPRESSION_FUNCTION, enabled); 2004 return this; 2005 } 2006 2007 /** 2008 * Sets the ScorablePropertyRanking feature as enabled or disabled. 2009 * 2010 * <p>If enabled, 'getScorableProperty' function can be used in the advanced ranking 2011 * expression. For details, see {@link SearchSpec.Builder#setRankingStrategy(String)}. 2012 * 2013 * @param enabled Enables the feature if true, otherwise disables it. 2014 */ 2015 @CanIgnoreReturnValue 2016 @FlaggedApi(Flags.FLAG_ENABLE_SCORABLE_PROPERTY) setScorablePropertyRankingEnabled(boolean enabled)2017 public @NonNull Builder setScorablePropertyRankingEnabled(boolean enabled) { 2018 modifyEnabledFeature(FeatureConstants.SCHEMA_SCORABLE_PROPERTY_CONFIG, enabled); 2019 return this; 2020 } 2021 2022 /** 2023 * Constructs a new {@link SearchSpec} from the contents of this builder. 2024 * 2025 * @throws IllegalArgumentException if property weights are provided with a ranking strategy 2026 * that isn't RANKING_STRATEGY_RELEVANCE_SCORE. 2027 * @throws IllegalStateException if the ranking strategy is {@link 2028 * #RANKING_STRATEGY_JOIN_AGGREGATE_SCORE} and {@link #setJoinSpec} has never been 2029 * called. 2030 * @throws IllegalStateException if the aggregation scoring strategy has been set in {@link 2031 * JoinSpec#getAggregationScoringStrategy()} but the ranking strategy is not {@link 2032 * #RANKING_STRATEGY_JOIN_AGGREGATE_SCORE}. 2033 */ build()2034 public @NonNull SearchSpec build() { 2035 if (mJoinSpec != null) { 2036 if (mRankingStrategy != RANKING_STRATEGY_JOIN_AGGREGATE_SCORE 2037 && mJoinSpec.getAggregationScoringStrategy() 2038 != JoinSpec.AGGREGATION_SCORING_OUTER_RESULT_RANKING_SIGNAL) { 2039 throw new IllegalStateException( 2040 "Aggregate scoring strategy has been set in " 2041 + "the nested JoinSpec, but ranking strategy is not " 2042 + "RANKING_STRATEGY_JOIN_AGGREGATE_SCORE"); 2043 } 2044 } else if (mRankingStrategy == RANKING_STRATEGY_JOIN_AGGREGATE_SCORE) { 2045 throw new IllegalStateException( 2046 "Attempting to rank based on joined documents, but " 2047 + "no JoinSpec provided"); 2048 } 2049 if (!mTypePropertyWeights.isEmpty() 2050 && mRankingStrategy != RANKING_STRATEGY_RELEVANCE_SCORE 2051 && mRankingStrategy != RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION) { 2052 throw new IllegalArgumentException( 2053 "Property weights are only compatible with the" 2054 + " RANKING_STRATEGY_RELEVANCE_SCORE and" 2055 + " RANKING_STRATEGY_ADVANCED_RANKING_EXPRESSION ranking strategies."); 2056 } 2057 2058 mBuilt = true; 2059 return new SearchSpec( 2060 mTermMatchType, 2061 mSchemas, 2062 mNamespaces, 2063 mTypePropertyFilters, 2064 mPackageNames, 2065 mResultCountPerPage, 2066 mRankingStrategy, 2067 mOrder, 2068 mSnippetCount, 2069 mSnippetCountPerProperty, 2070 mMaxSnippetSize, 2071 mProjectionTypePropertyMasks, 2072 mGroupingTypeFlags, 2073 mGroupingLimit, 2074 mTypePropertyWeights, 2075 mJoinSpec, 2076 mAdvancedRankingExpression, 2077 new ArrayList<>(mEnabledFeatures), 2078 mSearchSourceLogTag, 2079 mEmbeddingParameters, 2080 mDefaultEmbeddingSearchMetricType, 2081 mInformationalRankingExpressions, 2082 mSearchStringParameters, 2083 mFilterDocumentIds, 2084 mRetrieveEmbeddingMatchInfos); 2085 } 2086 resetIfBuilt()2087 private void resetIfBuilt() { 2088 if (mBuilt) { 2089 mSchemas = new ArrayList<>(mSchemas); 2090 mTypePropertyFilters = BundleUtil.deepCopy(mTypePropertyFilters); 2091 mNamespaces = new ArrayList<>(mNamespaces); 2092 mPackageNames = new ArrayList<>(mPackageNames); 2093 mProjectionTypePropertyMasks = BundleUtil.deepCopy(mProjectionTypePropertyMasks); 2094 mTypePropertyWeights = BundleUtil.deepCopy(mTypePropertyWeights); 2095 mEmbeddingParameters = new ArrayList<>(mEmbeddingParameters); 2096 mInformationalRankingExpressions = 2097 new ArrayList<>(mInformationalRankingExpressions); 2098 mSearchStringParameters = new ArrayList<>(mSearchStringParameters); 2099 mFilterDocumentIds = new ArrayList<>(mFilterDocumentIds); 2100 mBuilt = false; 2101 } 2102 } 2103 modifyEnabledFeature(@onNull String feature, boolean enabled)2104 private void modifyEnabledFeature(@NonNull String feature, boolean enabled) { 2105 resetIfBuilt(); 2106 if (enabled) { 2107 mEnabledFeatures.add(feature); 2108 } else { 2109 mEnabledFeatures.remove(feature); 2110 } 2111 } 2112 } 2113 } 2114