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 androidx.appsearch.app; 18 19 import android.annotation.SuppressLint; 20 import android.os.Bundle; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 24 import androidx.annotation.IntDef; 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 import androidx.annotation.OptIn; 28 import androidx.annotation.RequiresFeature; 29 import androidx.annotation.RestrictTo; 30 import androidx.appsearch.annotation.CanIgnoreReturnValue; 31 import androidx.appsearch.exceptions.AppSearchException; 32 import androidx.appsearch.exceptions.IllegalSchemaException; 33 import androidx.appsearch.flags.FlaggedApi; 34 import androidx.appsearch.flags.Flags; 35 import androidx.appsearch.safeparcel.AbstractSafeParcelable; 36 import androidx.appsearch.safeparcel.PropertyConfigParcel; 37 import androidx.appsearch.safeparcel.PropertyConfigParcel.DocumentIndexingConfigParcel; 38 import androidx.appsearch.safeparcel.PropertyConfigParcel.JoinableConfigParcel; 39 import androidx.appsearch.safeparcel.PropertyConfigParcel.StringIndexingConfigParcel; 40 import androidx.appsearch.safeparcel.SafeParcelable; 41 import androidx.appsearch.safeparcel.stub.StubCreators.AppSearchSchemaCreator; 42 import androidx.appsearch.util.IndentingStringBuilder; 43 import androidx.collection.ArraySet; 44 import androidx.core.util.ObjectsCompat; 45 import androidx.core.util.Preconditions; 46 47 import java.lang.annotation.Retention; 48 import java.lang.annotation.RetentionPolicy; 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Collection; 52 import java.util.Collections; 53 import java.util.LinkedHashSet; 54 import java.util.List; 55 import java.util.Set; 56 57 /** 58 * The AppSearch Schema for a particular type of document. 59 * 60 * <p>For example, an e-mail message or a music recording could be a schema type. 61 * 62 * <p>The schema consists of type information, properties, and config (like tokenization type). 63 * 64 * @see AppSearchSession#setSchemaAsync 65 */ 66 @SafeParcelable.Class(creator = "AppSearchSchemaCreator") 67 // TODO(b/384721898): Switch to JSpecify annotations 68 @SuppressWarnings({"HiddenSuperclass", "JSpecifyNullness"}) 69 public final class AppSearchSchema extends AbstractSafeParcelable { 70 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 71 @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2) 72 public static final @NonNull Parcelable.Creator<AppSearchSchema> CREATOR = 73 new AppSearchSchemaCreator(); 74 75 @Field(id = 1, getter = "getSchemaType") 76 private final String mSchemaType; 77 78 @Field(id = 2) 79 final List<PropertyConfigParcel> mPropertyConfigParcels; 80 81 @Field(id = 3, getter = "getParentTypes") 82 private final List<String> mParentTypes; 83 84 @Field(id = 4, getter = "getDescription") 85 private final String mDescription; 86 87 @Constructor AppSearchSchema( @aramid = 1) @onNull String schemaType, @Param(id = 2) @NonNull List<PropertyConfigParcel> propertyConfigParcels, @Param(id = 3) @NonNull List<String> parentTypes, @Param(id = 4) @NonNull String description)88 AppSearchSchema( 89 @Param(id = 1) @NonNull String schemaType, 90 @Param(id = 2) @NonNull List<PropertyConfigParcel> propertyConfigParcels, 91 @Param(id = 3) @NonNull List<String> parentTypes, 92 @Param(id = 4) @NonNull String description) { 93 mSchemaType = Preconditions.checkNotNull(schemaType); 94 mPropertyConfigParcels = Preconditions.checkNotNull(propertyConfigParcels); 95 mParentTypes = Preconditions.checkNotNull(parentTypes); 96 mDescription = Preconditions.checkNotNull(description); 97 } 98 99 @Override toString()100 public @NonNull String toString() { 101 IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); 102 appendAppSearchSchemaString(stringBuilder); 103 return stringBuilder.toString(); 104 } 105 106 /** 107 * Appends a debugging string for the {@link AppSearchSchema} instance to the given string 108 * builder. 109 * 110 * @param builder the builder to append to. 111 */ 112 @OptIn(markerClass = ExperimentalAppSearchApi.class) appendAppSearchSchemaString(@onNull IndentingStringBuilder builder)113 private void appendAppSearchSchemaString(@NonNull IndentingStringBuilder builder) { 114 Preconditions.checkNotNull(builder); 115 116 builder.append("{\n"); 117 builder.increaseIndentLevel(); 118 builder.append("schemaType: \"").append(getSchemaType()).append("\",\n"); 119 builder.append("description: \"").append(getDescription()).append("\",\n"); 120 builder.append("properties: [\n"); 121 122 AppSearchSchema.PropertyConfig[] sortedProperties = getProperties() 123 .toArray(new AppSearchSchema.PropertyConfig[0]); 124 Arrays.sort(sortedProperties, (o1, o2) -> o1.getName().compareTo(o2.getName())); 125 126 for (int i = 0; i < sortedProperties.length; i++) { 127 AppSearchSchema.PropertyConfig propertyConfig = sortedProperties[i]; 128 builder.increaseIndentLevel(); 129 propertyConfig.appendPropertyConfigString(builder); 130 if (i != sortedProperties.length - 1) { 131 builder.append(",\n"); 132 } 133 builder.decreaseIndentLevel(); 134 } 135 136 builder.append("\n"); 137 builder.append("]\n"); 138 builder.decreaseIndentLevel(); 139 builder.append("}"); 140 } 141 142 /** Returns the name of this schema type, such as Email. */ getSchemaType()143 public @NonNull String getSchemaType() { 144 return mSchemaType; 145 } 146 147 /** 148 * Returns a natural language description of this schema type. 149 * 150 * <p>Ex. The description for an Email type could be "A type of electronic message". 151 * 152 * <p>This information is purely to help apps consuming this type to understand its semantic 153 * meaning. This field has no effect in AppSearch - it is just stored with the AppSearchSchema. 154 * If {@link Builder#setDescription} is uncalled, then this method will return an empty string. 155 */ 156 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 157 @ExperimentalAppSearchApi getDescription()158 public @NonNull String getDescription() { 159 return mDescription; 160 } 161 162 /** 163 * Returns the list of {@link PropertyConfig}s that are part of this schema. 164 * 165 * <p>This method creates a new list when called. 166 */ 167 @SuppressWarnings({"MixedMutabilityReturnType"}) getProperties()168 public @NonNull List<PropertyConfig> getProperties() { 169 if (mPropertyConfigParcels.isEmpty()) { 170 return Collections.emptyList(); 171 } 172 List<PropertyConfig> ret = new ArrayList<>(mPropertyConfigParcels.size()); 173 for (int i = 0; i < mPropertyConfigParcels.size(); i++) { 174 ret.add(PropertyConfig.fromParcel(mPropertyConfigParcels.get(i))); 175 } 176 return ret; 177 } 178 179 /** 180 * Returns the list of parent types of this schema for polymorphism. 181 */ 182 @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES) getParentTypes()183 public @NonNull List<String> getParentTypes() { 184 return Collections.unmodifiableList(mParentTypes); 185 } 186 187 @Override 188 @OptIn(markerClass = ExperimentalAppSearchApi.class) equals(@ullable Object other)189 public boolean equals(@Nullable Object other) { 190 if (this == other) { 191 return true; 192 } 193 if (!(other instanceof AppSearchSchema)) { 194 return false; 195 } 196 AppSearchSchema otherSchema = (AppSearchSchema) other; 197 if (!getSchemaType().equals(otherSchema.getSchemaType())) { 198 return false; 199 } 200 if (!getDescription().equals(otherSchema.getDescription())) { 201 return false; 202 } 203 if (!getParentTypes().equals(otherSchema.getParentTypes())) { 204 return false; 205 } 206 return getProperties().equals(otherSchema.getProperties()); 207 } 208 209 @Override 210 @OptIn(markerClass = ExperimentalAppSearchApi.class) hashCode()211 public int hashCode() { 212 return ObjectsCompat.hash( 213 getSchemaType(), 214 getProperties(), 215 getParentTypes(), 216 getDescription()); 217 } 218 219 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 220 @FlaggedApi(Flags.FLAG_ENABLE_SAFE_PARCELABLE_2) 221 @Override writeToParcel(@onNull Parcel dest, int flags)222 public void writeToParcel(@NonNull Parcel dest, int flags) { 223 AppSearchSchemaCreator.writeToParcel(this, dest, flags); 224 } 225 226 /** Builder for {@link AppSearchSchema objects}. */ 227 public static final class Builder { 228 private String mSchemaType; 229 private String mDescription = ""; 230 private ArrayList<PropertyConfigParcel> mPropertyConfigParcels = new ArrayList<>(); 231 private LinkedHashSet<String> mParentTypes = new LinkedHashSet<>(); 232 private final Set<String> mPropertyNames = new ArraySet<>(); 233 private boolean mBuilt = false; 234 235 /** Creates a new {@link AppSearchSchema.Builder}. */ Builder(@onNull String schemaType)236 public Builder(@NonNull String schemaType) { 237 mSchemaType = Preconditions.checkNotNull(schemaType); 238 } 239 240 /** 241 * Creates a new {@link AppSearchSchema.Builder} from the given {@link AppSearchSchema}. 242 */ 243 @ExperimentalAppSearchApi 244 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 245 @OptIn(markerClass = ExperimentalAppSearchApi.class) Builder(@onNull AppSearchSchema schema)246 public Builder(@NonNull AppSearchSchema schema) { 247 mSchemaType = schema.getSchemaType(); 248 mDescription = schema.getDescription(); 249 mPropertyConfigParcels.addAll(schema.mPropertyConfigParcels); 250 mParentTypes.addAll(schema.mParentTypes); 251 for (int i = 0; i < mPropertyConfigParcels.size(); i++) { 252 mPropertyNames.add(mPropertyConfigParcels.get(i).getName()); 253 } 254 } 255 256 /** Sets the schema type name. */ 257 @ExperimentalAppSearchApi 258 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 259 @CanIgnoreReturnValue setSchemaType(@onNull String schemaType)260 public @NonNull AppSearchSchema.Builder setSchemaType(@NonNull String schemaType) { 261 Preconditions.checkNotNull(schemaType); 262 resetIfBuilt(); 263 mSchemaType = schemaType; 264 return this; 265 } 266 267 /** 268 * Sets a natural language description of this schema type. 269 * 270 * <p> For more details about the description field, see {@link 271 * AppSearchSchema#getDescription}. 272 */ 273 @RequiresFeature( 274 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 275 name = Features.SCHEMA_SET_DESCRIPTION) 276 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 277 @ExperimentalAppSearchApi 278 @CanIgnoreReturnValue setDescription(@onNull String description)279 public @NonNull AppSearchSchema.Builder setDescription(@NonNull String description) { 280 Preconditions.checkNotNull(description); 281 resetIfBuilt(); 282 mDescription = description; 283 return this; 284 } 285 286 /** Adds a property to the schema type. */ 287 @CanIgnoreReturnValue addProperty( @onNull PropertyConfig propertyConfig)288 public @NonNull AppSearchSchema.Builder addProperty( 289 @NonNull PropertyConfig propertyConfig) { 290 Preconditions.checkNotNull(propertyConfig); 291 resetIfBuilt(); 292 String name = propertyConfig.getName(); 293 if (!mPropertyNames.add(name)) { 294 throw new IllegalSchemaException("Property defined more than once: " + name); 295 } 296 mPropertyConfigParcels.add(propertyConfig.mPropertyConfigParcel); 297 return this; 298 } 299 300 /** 301 * Clears all properties added through {@link #addProperty(PropertyConfig)} from the schema 302 * type. 303 */ 304 @ExperimentalAppSearchApi 305 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 306 @CanIgnoreReturnValue clearProperties()307 public @NonNull AppSearchSchema.Builder clearProperties() { 308 resetIfBuilt(); 309 mPropertyConfigParcels.clear(); 310 mPropertyNames.clear(); 311 return this; 312 } 313 314 /** 315 * Adds a parent type to the schema type for polymorphism, so that the schema type will be 316 * considered as a subtype of {@code parentSchemaType}. 317 * 318 * <p>Subtype relations are automatically considered transitive, so callers are only 319 * required to provide direct parents. Specifically, if T1 <: T2 and T2 <: T3 are 320 * known, then T1 <: T3 will be inferred automatically, where <: is the subtype 321 * symbol. 322 * 323 * <p>Polymorphism is currently supported in the following ways: 324 * <ul> 325 * <li>Search filters on a parent type will automatically be extended to the child 326 * types as well. For example, if Artist <: Person, then a search with a filter on 327 * type Person (by calling {@link SearchSpec.Builder#addFilterSchemas}) will also 328 * include documents of type Artist in the search result. 329 * <li>In the projection API, the property paths to project specified for a 330 * parent type will automatically be extended to the child types as well. If both a 331 * parent type and one of its child type are specified in the projection API, the 332 * parent type's paths will be merged into the child's. For more details on 333 * projection, see {@link SearchSpec.Builder#addProjection}. 334 * <li>A document property defined as type U is allowed to be set with a document of 335 * type T, as long as T <: U, but note that index will only be based on the defined 336 * type, which is U. For example, consider a document of type "Company" with a 337 * repeated "employees" field of type "Person". We can add employees of either 338 * type "Person" or type "Artist" or both to this property, as long as "Artist" is a 339 * subtype of "Person". However, the index of the "employees" property will be based 340 * on what's defined in "Person", even for an added document of type "Artist". 341 * </ul> 342 * 343 * <p>Subtypes must meet the following requirements. A violation of the requirements will 344 * cause {@link AppSearchSession#setSchemaAsync} to throw an {@link AppSearchException} 345 * with the result code of {@link AppSearchResult#RESULT_INVALID_ARGUMENT}. Consider a 346 * type Artist and a type Person, and Artist claims to be a subtype of Person, then: 347 * <ul> 348 * <li>Every property in Person must have a corresponding property in Artist with the 349 * same name. 350 * <li>Every non-document property in Person must have the same type as the type of 351 * the corresponding property in Artist. For example, if "age" is an integer property 352 * in Person, then "age" must also be an integer property in Artist, instead of a 353 * string. 354 * <li>The schema type of every document property in Artist must be a subtype of the 355 * schema type of the corresponding document property in Person, if such a property 356 * exists in Person. For example, if "awards" is a document property of type Award in 357 * Person, then the type of the "awards" property in Artist must be a subtype of 358 * Award, say ArtAward. Note that every type is a subtype of itself. 359 * <li>Every property in Artist must have a cardinality stricter than or equal to the 360 * cardinality of the corresponding property in Person, if such a property exists in 361 * Person. For example, if "awards" is a property in Person of cardinality OPTIONAL, 362 * then the cardinality of the "awards" property in Artist can only be REQUIRED or 363 * OPTIONAL. Rule: REQUIRED < OPTIONAL < REPEATED. 364 * <li>There are no other enforcements on the corresponding properties in Artist, 365 * such as index type, tokenizer type, etc. These settings can be safely overridden. 366 * </ul> 367 * 368 * <p>A type can be defined to have multiple parents, but it must be compatible with each 369 * of its parents based on the above rules. For example, if LocalBusiness is defined as a 370 * subtype of both Place and Organization, then the compatibility of LocalBusiness with 371 * Place and the compatibility of LocalBusiness with Organization will both be checked. 372 */ 373 @CanIgnoreReturnValue 374 @RequiresFeature( 375 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 376 name = Features.SCHEMA_ADD_PARENT_TYPE) addParentType(@onNull String parentSchemaType)377 public @NonNull AppSearchSchema.Builder addParentType(@NonNull String parentSchemaType) { 378 Preconditions.checkNotNull(parentSchemaType); 379 resetIfBuilt(); 380 mParentTypes.add(parentSchemaType); 381 return this; 382 } 383 384 /** 385 * Clears all parent types added through {@link #addParentType(String)} from the schema 386 * type. 387 */ 388 @ExperimentalAppSearchApi 389 @FlaggedApi(Flags.FLAG_ENABLE_ADDITIONAL_BUILDER_COPY_CONSTRUCTORS) 390 @CanIgnoreReturnValue clearParentTypes()391 public @NonNull AppSearchSchema.Builder clearParentTypes() { 392 resetIfBuilt(); 393 mParentTypes.clear(); 394 return this; 395 } 396 397 /** Constructs a new {@link AppSearchSchema} from the contents of this builder. */ build()398 public @NonNull AppSearchSchema build() { 399 mBuilt = true; 400 return new AppSearchSchema(mSchemaType, 401 mPropertyConfigParcels, 402 new ArrayList<>(mParentTypes), 403 mDescription); 404 } 405 resetIfBuilt()406 private void resetIfBuilt() { 407 if (mBuilt) { 408 mPropertyConfigParcels = new ArrayList<>(mPropertyConfigParcels); 409 mParentTypes = new LinkedHashSet<>(mParentTypes); 410 mBuilt = false; 411 } 412 } 413 } 414 415 /** 416 * Common configuration for a single property (field) in a Document. 417 * 418 * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be 419 * a property. 420 */ 421 public abstract static class PropertyConfig { 422 /** 423 * Physical data-types of the contents of the property. 424 * 425 * <p>NOTE: The integer values of these constants must match the proto enum constants in 426 * com.google.android.icing.proto.PropertyConfigProto.DataType.Code. 427 * 428 * @exportToFramework:hide 429 */ 430 @RestrictTo(RestrictTo.Scope.LIBRARY) 431 @IntDef(value = { 432 DATA_TYPE_STRING, 433 DATA_TYPE_LONG, 434 DATA_TYPE_DOUBLE, 435 DATA_TYPE_BOOLEAN, 436 DATA_TYPE_BYTES, 437 DATA_TYPE_DOCUMENT, 438 DATA_TYPE_EMBEDDING, 439 DATA_TYPE_BLOB_HANDLE, 440 }) 441 @Retention(RetentionPolicy.SOURCE) 442 public @interface DataType { 443 } 444 445 /** 446 * Constant value for String data type. 447 * 448 * @exportToFramework:hide 449 */ 450 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 451 public static final int DATA_TYPE_STRING = 1; 452 453 /** 454 * Constant value for Long data type. 455 * 456 * @exportToFramework:hide 457 */ 458 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 459 public static final int DATA_TYPE_LONG = 2; 460 461 /** 462 * Constant value for Double data type. 463 * 464 * @exportToFramework:hide 465 */ 466 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 467 public static final int DATA_TYPE_DOUBLE = 3; 468 469 /** 470 * Constant value for Boolean data type. 471 * 472 * @exportToFramework:hide 473 */ 474 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 475 public static final int DATA_TYPE_BOOLEAN = 4; 476 477 /** 478 * Unstructured BLOB. 479 * 480 * @exportToFramework:hide 481 */ 482 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 483 public static final int DATA_TYPE_BYTES = 5; 484 485 /** 486 * Indicates that the property is itself a {@link GenericDocument}, making it part of a 487 * hierarchical schema. Any property using this DataType MUST have a valid 488 * {@link PropertyConfig#getSchemaType}. 489 * 490 * @exportToFramework:hide 491 */ 492 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 493 public static final int DATA_TYPE_DOCUMENT = 6; 494 495 /** 496 * Indicates that the property is an {@link EmbeddingVector}. 497 * 498 * @exportToFramework:hide 499 */ 500 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 501 public static final int DATA_TYPE_EMBEDDING = 7; 502 503 /** 504 * Indicates that the property is an {@link AppSearchBlobHandle}. 505 * 506 * @exportToFramework:hide 507 */ 508 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 509 public static final int DATA_TYPE_BLOB_HANDLE = 8; 510 511 /** 512 * The cardinality of the property (whether it is required, optional or repeated). 513 * 514 * <p>NOTE: The integer values of these constants must match the proto enum constants in 515 * com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code. 516 * 517 * @exportToFramework:hide 518 */ 519 @RestrictTo(RestrictTo.Scope.LIBRARY) 520 @IntDef(value = { 521 CARDINALITY_REPEATED, 522 CARDINALITY_OPTIONAL, 523 CARDINALITY_REQUIRED, 524 }) 525 @Retention(RetentionPolicy.SOURCE) 526 public @interface Cardinality { 527 } 528 529 /** Any number of items (including zero) [0...*]. */ 530 public static final int CARDINALITY_REPEATED = 1; 531 532 /** Zero or one value [0,1]. */ 533 public static final int CARDINALITY_OPTIONAL = 2; 534 535 /** Exactly one value [1]. */ 536 public static final int CARDINALITY_REQUIRED = 3; 537 538 final PropertyConfigParcel mPropertyConfigParcel; 539 PropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)540 PropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 541 mPropertyConfigParcel = Preconditions.checkNotNull(propertyConfigParcel); 542 } 543 544 @Override toString()545 public @NonNull String toString() { 546 IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); 547 appendPropertyConfigString(stringBuilder); 548 return stringBuilder.toString(); 549 } 550 551 /** 552 * Appends a debug string for the {@link AppSearchSchema.PropertyConfig} instance to the 553 * given string builder. 554 * 555 * @param builder the builder to append to. 556 */ 557 @OptIn(markerClass = ExperimentalAppSearchApi.class) appendPropertyConfigString(@onNull IndentingStringBuilder builder)558 void appendPropertyConfigString(@NonNull IndentingStringBuilder builder) { 559 Preconditions.checkNotNull(builder); 560 561 builder.append("{\n"); 562 builder.increaseIndentLevel(); 563 builder.append("name: \"").append(getName()).append("\",\n"); 564 builder.append("description: \"").append(getDescription()).append("\",\n"); 565 566 if (this instanceof AppSearchSchema.StringPropertyConfig) { 567 ((StringPropertyConfig) this) 568 .appendStringPropertyConfigFields(builder); 569 } else if (this instanceof AppSearchSchema.DocumentPropertyConfig) { 570 ((DocumentPropertyConfig) this) 571 .appendDocumentPropertyConfigFields(builder); 572 } else if (this instanceof AppSearchSchema.LongPropertyConfig) { 573 ((LongPropertyConfig) this) 574 .appendLongPropertyConfigFields(builder); 575 } 576 577 switch (getCardinality()) { 578 case AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED: 579 builder.append("cardinality: CARDINALITY_REPEATED,\n"); 580 break; 581 case AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL: 582 builder.append("cardinality: CARDINALITY_OPTIONAL,\n"); 583 break; 584 case AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED: 585 builder.append("cardinality: CARDINALITY_REQUIRED,\n"); 586 break; 587 default: 588 builder.append("cardinality: CARDINALITY_UNKNOWN,\n"); 589 } 590 591 switch (getDataType()) { 592 case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING: 593 builder.append("dataType: DATA_TYPE_STRING,\n"); 594 break; 595 case AppSearchSchema.PropertyConfig.DATA_TYPE_LONG: 596 builder.append("dataType: DATA_TYPE_LONG,\n"); 597 break; 598 case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE: 599 builder.append("dataType: DATA_TYPE_DOUBLE,\n"); 600 break; 601 case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN: 602 builder.append("dataType: DATA_TYPE_BOOLEAN,\n"); 603 break; 604 case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES: 605 builder.append("dataType: DATA_TYPE_BYTES,\n"); 606 break; 607 case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT: 608 builder.append("dataType: DATA_TYPE_DOCUMENT,\n"); 609 break; 610 case PropertyConfig.DATA_TYPE_EMBEDDING: 611 builder.append("dataType: DATA_TYPE_EMBEDDING,\n"); 612 break; 613 case PropertyConfig.DATA_TYPE_BLOB_HANDLE: 614 builder.append("dataType: DATA_TYPE_BLOB_HANDLE,\n"); 615 break; 616 default: 617 builder.append("dataType: DATA_TYPE_UNKNOWN,\n"); 618 } 619 builder.decreaseIndentLevel(); 620 builder.append("}"); 621 } 622 623 /** Returns the name of this property. */ getName()624 public @NonNull String getName() { 625 return mPropertyConfigParcel.getName(); 626 } 627 628 /** 629 * Returns a natural language description of this property. 630 * 631 * <p>Ex. The description for the "homeAddress" property of a "Person" type could be "the 632 * address at which this person lives". 633 * 634 * <p>This information is purely to help apps consuming this type the semantic meaning of 635 * its properties. This field has no effect in AppSearch - it is just stored with the 636 * AppSearchSchema. If the description is not set, then this method will return an empty 637 * string. 638 */ 639 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 640 @ExperimentalAppSearchApi getDescription()641 public @NonNull String getDescription() { 642 return mPropertyConfigParcel.getDescription(); 643 } 644 645 /** 646 * Returns the type of data the property contains (such as string, int, bytes, etc). 647 * 648 * @exportToFramework:hide 649 */ 650 @RestrictTo(RestrictTo.Scope.LIBRARY) 651 @DataType getDataType()652 public int getDataType() { 653 return mPropertyConfigParcel.getDataType(); 654 } 655 656 /** 657 * Returns the cardinality of the property (whether it is optional, required or repeated). 658 */ 659 @Cardinality getCardinality()660 public int getCardinality() { 661 return mPropertyConfigParcel.getCardinality(); 662 } 663 664 @Override equals(@ullable Object other)665 public boolean equals(@Nullable Object other) { 666 if (this == other) { 667 return true; 668 } 669 if (!(other instanceof PropertyConfig)) { 670 return false; 671 } 672 PropertyConfig otherProperty = (PropertyConfig) other; 673 return ObjectsCompat.equals(mPropertyConfigParcel, otherProperty.mPropertyConfigParcel); 674 } 675 676 @Override hashCode()677 public int hashCode() { 678 return mPropertyConfigParcel.hashCode(); 679 } 680 681 /** 682 * Converts a {@link Bundle} into a {@link PropertyConfig} depending on its internal data 683 * type. 684 * 685 * <p>The bundle is not cloned. 686 * 687 * @throws IllegalArgumentException if the bundle does no contain a recognized 688 * value in its {@code DATA_TYPE_FIELD}. 689 * @exportToFramework:hide 690 */ 691 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 692 @OptIn(markerClass = ExperimentalAppSearchApi.class) fromParcel( @onNull PropertyConfigParcel propertyConfigParcel)693 public static @NonNull PropertyConfig fromParcel( 694 @NonNull PropertyConfigParcel propertyConfigParcel) { 695 Preconditions.checkNotNull(propertyConfigParcel); 696 switch (propertyConfigParcel.getDataType()) { 697 case PropertyConfig.DATA_TYPE_STRING: 698 return new StringPropertyConfig(propertyConfigParcel); 699 case PropertyConfig.DATA_TYPE_LONG: 700 return new LongPropertyConfig(propertyConfigParcel); 701 case PropertyConfig.DATA_TYPE_DOUBLE: 702 return new DoublePropertyConfig(propertyConfigParcel); 703 case PropertyConfig.DATA_TYPE_BOOLEAN: 704 return new BooleanPropertyConfig(propertyConfigParcel); 705 case PropertyConfig.DATA_TYPE_BYTES: 706 return new BytesPropertyConfig(propertyConfigParcel); 707 case PropertyConfig.DATA_TYPE_DOCUMENT: 708 return new DocumentPropertyConfig(propertyConfigParcel); 709 case PropertyConfig.DATA_TYPE_EMBEDDING: 710 return new EmbeddingPropertyConfig(propertyConfigParcel); 711 case PropertyConfig.DATA_TYPE_BLOB_HANDLE: 712 return new BlobHandlePropertyConfig(propertyConfigParcel); 713 default: 714 throw new IllegalArgumentException( 715 "Unsupported property bundle of type " 716 + propertyConfigParcel.getDataType() 717 + "; contents: " + propertyConfigParcel); 718 } 719 } 720 } 721 722 /** Configuration for a property of type String in a Document. */ 723 public static final class StringPropertyConfig extends PropertyConfig { 724 /** 725 * Encapsulates the configurations on how AppSearch should query/index these terms. 726 * @exportToFramework:hide 727 */ 728 @RestrictTo(RestrictTo.Scope.LIBRARY) 729 @IntDef(value = { 730 INDEXING_TYPE_NONE, 731 INDEXING_TYPE_EXACT_TERMS, 732 INDEXING_TYPE_PREFIXES, 733 }) 734 @Retention(RetentionPolicy.SOURCE) 735 public @interface IndexingType { 736 } 737 738 /** Content in this property will not be tokenized or indexed. */ 739 public static final int INDEXING_TYPE_NONE = 0; 740 741 /** 742 * Content in this property should only be returned for queries matching the exact tokens 743 * appearing in this property. 744 * 745 * <p>For example, a property with "fool" should NOT match a query for "foo". 746 */ 747 public static final int INDEXING_TYPE_EXACT_TERMS = 1; 748 749 /** 750 * Content in this property should be returned for queries that are either exact matches or 751 * query matches of the tokens appearing in this property. 752 * 753 * <p>For example, a property with "fool" <b>should</b> match a query for "foo". 754 */ 755 public static final int INDEXING_TYPE_PREFIXES = 2; 756 757 /** 758 * Configures how tokens should be extracted from this property. 759 * 760 * <p>NOTE: The integer values of these constants must match the proto enum constants in 761 * com.google.android.icing.proto.IndexingConfig.TokenizerType.Code. 762 * 763 * @exportToFramework:hide 764 */ 765 @RestrictTo(RestrictTo.Scope.LIBRARY) 766 @IntDef(value = { 767 TOKENIZER_TYPE_NONE, 768 TOKENIZER_TYPE_PLAIN, 769 TOKENIZER_TYPE_VERBATIM, 770 TOKENIZER_TYPE_RFC822 771 }) 772 @Retention(RetentionPolicy.SOURCE) 773 public @interface TokenizerType { 774 } 775 776 /** 777 * This value indicates that no tokens should be extracted from this property. 778 * 779 * <p>It is only valid for tokenizer_type to be 'NONE' if {@link #getIndexingType} is 780 * {@link #INDEXING_TYPE_NONE}. 781 */ 782 public static final int TOKENIZER_TYPE_NONE = 0; 783 784 /** 785 * Tokenization for plain text. This value indicates that tokens should be extracted from 786 * this property based on word breaks. Segments of whitespace and punctuation are not 787 * considered tokens. 788 * 789 * <p>For example, a property with "foo bar. baz." will produce tokens for "foo", "bar" and 790 * "baz". The segments " " and "." will not be considered tokens. 791 * 792 * <p>It is only valid for tokenizer_type to be 'PLAIN' if {@link #getIndexingType} is 793 * {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}. 794 */ 795 public static final int TOKENIZER_TYPE_PLAIN = 1; 796 797 /** 798 * This value indicates that no normalization or segmentation should be applied to string 799 * values that are tokenized using this type. Therefore, the output token is equivalent 800 * to the raw string value. 801 * 802 * <p>For example, a property with "Hello, world!" will produce the token "Hello, world!", 803 * preserving punctuation and capitalization, and not creating separate tokens between the 804 * space. 805 * 806 * <p>It is only valid for tokenizer_type to be 'VERBATIM' if {@link #getIndexingType} is 807 * {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}. 808 */ 809 @RequiresFeature( 810 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 811 name = Features.VERBATIM_SEARCH) 812 public static final int TOKENIZER_TYPE_VERBATIM = 2; 813 814 /** 815 * Tokenization for emails. This value indicates that tokens should be extracted from 816 * this property based on email structure. 817 * 818 * <p>For example, a property with "alex.sav@google.com" will produce tokens for "alex", 819 * "sav", "alex.sav", "google", "com", and "alexsav@google.com" 820 * 821 * <p>It is only valid for tokenizer_type to be 'RFC822' if {@link #getIndexingType} is 822 * {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}. 823 */ 824 @RequiresFeature( 825 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 826 name = Features.TOKENIZER_TYPE_RFC822) 827 public static final int TOKENIZER_TYPE_RFC822 = 3; 828 829 /** 830 * The joinable value type of the property. By setting the appropriate joinable value type 831 * for a property, the client can use the property for joining documents from other schema 832 * types using Search API (see {@link JoinSpec}). 833 * @exportToFramework:hide 834 */ 835 // NOTE: The integer values of these constants must match the proto enum constants in 836 // com.google.android.icing.proto.JoinableConfig.ValueType.Code. 837 @IntDef(value = { 838 JOINABLE_VALUE_TYPE_NONE, 839 JOINABLE_VALUE_TYPE_QUALIFIED_ID, 840 }) 841 @RestrictTo(RestrictTo.Scope.LIBRARY) 842 @Retention(RetentionPolicy.SOURCE) 843 public @interface JoinableValueType { 844 } 845 846 /** Content in this property is not joinable. */ 847 public static final int JOINABLE_VALUE_TYPE_NONE = 0; 848 849 /** 850 * Content in this string property will be used as a qualified id to join documents. 851 * <ul> 852 * <li>Qualified id: a unique identifier for a document, and this joinable value type is 853 * similar to primary and foreign key in relational database. See 854 * {@link androidx.appsearch.util.DocumentIdUtil} for more details. 855 * <li>Currently we only support single string joining, so it should only be used with 856 * {@link PropertyConfig#CARDINALITY_OPTIONAL} and 857 * {@link PropertyConfig#CARDINALITY_REQUIRED}. 858 * </ul> 859 */ 860 @RequiresFeature( 861 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 862 name = Features.JOIN_SPEC_AND_QUALIFIED_ID) 863 public static final int JOINABLE_VALUE_TYPE_QUALIFIED_ID = 1; 864 865 /** 866 * The delete propagation type of the property. By setting the delete propagation type for a 867 * property, the client can propagate deletion between the document and the referenced 868 * document. The propagation direction is determined by the delete propagation type. 869 * 870 * @exportToFramework:hide 871 */ 872 // NOTE: The integer values of these constants must match the proto enum constants in 873 // com.google.android.icing.proto.JoinableConfig.DeletePropagationType.Code. 874 @IntDef(value = { 875 DELETE_PROPAGATION_TYPE_NONE, 876 DELETE_PROPAGATION_TYPE_PROPAGATE_FROM, 877 }) 878 @RestrictTo(RestrictTo.Scope.LIBRARY) 879 @Retention(RetentionPolicy.SOURCE) 880 @ExperimentalAppSearchApi 881 public @interface DeletePropagationType { 882 } 883 884 /** Does not propagate deletion. */ 885 // TODO(b/384947619) unhide the API once it is ready. 886 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 887 @ExperimentalAppSearchApi 888 @FlaggedApi(Flags.FLAG_ENABLE_DELETE_PROPAGATION_TYPE) 889 public static final int DELETE_PROPAGATION_TYPE_NONE = 0; 890 891 /** 892 * Content in this string property will be used as a qualified id referring to another 893 * (parent) document, and the deletion of the referenced document will propagate to this 894 * (child) document. 895 * 896 * <p>Please note that this propagates further. If the child document has any children that 897 * also set delete propagation type PROPAGATE_FROM for their joinable properties, then those 898 * (grandchild) documents will be deleted. 899 * 900 * <p>Since delete propagation works between the document and the referenced document, if 901 * setting this type for delete propagation, the string property should also be qualified id 902 * joinable (i.e. having {@link StringPropertyConfig#JOINABLE_VALUE_TYPE_QUALIFIED_ID} for 903 * the joinable value type). Otherwise, throw {@link IllegalStateException} when building 904 * (see {@link StringPropertyConfig.Builder#build}). 905 */ 906 // TODO(b/384947619) unhide the API once it is ready. 907 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 908 @RequiresFeature( 909 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 910 name = Features 911 .SCHEMA_STRING_PROPERTY_CONFIG_DELETE_PROPAGATION_TYPE_PROPAGATE_FROM) 912 @ExperimentalAppSearchApi 913 @FlaggedApi(Flags.FLAG_ENABLE_DELETE_PROPAGATION_TYPE) 914 public static final int DELETE_PROPAGATION_TYPE_PROPAGATE_FROM = 1; 915 StringPropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)916 StringPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 917 super(propertyConfigParcel); 918 } 919 920 /** Returns how the property is indexed. */ 921 @StringPropertyConfig.IndexingType getIndexingType()922 public int getIndexingType() { 923 StringIndexingConfigParcel indexingConfigParcel = 924 mPropertyConfigParcel.getStringIndexingConfigParcel(); 925 if (indexingConfigParcel == null) { 926 return INDEXING_TYPE_NONE; 927 } 928 929 return indexingConfigParcel.getIndexingType(); 930 } 931 932 /** Returns how this property is tokenized (split into words). */ 933 @TokenizerType getTokenizerType()934 public int getTokenizerType() { 935 StringIndexingConfigParcel indexingConfigParcel = 936 mPropertyConfigParcel.getStringIndexingConfigParcel(); 937 if (indexingConfigParcel == null) { 938 return TOKENIZER_TYPE_NONE; 939 } 940 941 return indexingConfigParcel.getTokenizerType(); 942 } 943 944 /** 945 * Returns how this property is going to be used to join documents from other schema types. 946 */ 947 @JoinableValueType getJoinableValueType()948 public int getJoinableValueType() { 949 JoinableConfigParcel joinableConfigParcel = mPropertyConfigParcel 950 .getJoinableConfigParcel(); 951 if (joinableConfigParcel == null) { 952 return JOINABLE_VALUE_TYPE_NONE; 953 } 954 955 return joinableConfigParcel.getJoinableValueType(); 956 } 957 958 /** 959 * Returns how the deletion will be propagated between this document and the referenced 960 * document whose qualified id is held by this property. 961 */ 962 // TODO(b/384947619) unhide the API once it is ready. 963 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 964 @FlaggedApi(Flags.FLAG_ENABLE_DELETE_PROPAGATION_TYPE) 965 @ExperimentalAppSearchApi 966 @DeletePropagationType getDeletePropagationType()967 public int getDeletePropagationType() { 968 JoinableConfigParcel joinableConfigParcel = mPropertyConfigParcel 969 .getJoinableConfigParcel(); 970 if (joinableConfigParcel == null) { 971 return DELETE_PROPAGATION_TYPE_NONE; 972 } 973 974 return joinableConfigParcel.getDeletePropagationType(); 975 } 976 977 /** Builder for {@link StringPropertyConfig}. */ 978 @OptIn(markerClass = ExperimentalAppSearchApi.class) 979 public static final class Builder { 980 private final String mPropertyName; 981 private String mDescription = ""; 982 @Cardinality 983 private int mCardinality = CARDINALITY_OPTIONAL; 984 @StringPropertyConfig.IndexingType 985 private int mIndexingType = INDEXING_TYPE_NONE; 986 @TokenizerType 987 private int mTokenizerType = TOKENIZER_TYPE_NONE; 988 @JoinableValueType 989 private int mJoinableValueType = JOINABLE_VALUE_TYPE_NONE; 990 @DeletePropagationType 991 private int mDeletePropagationType = DELETE_PROPAGATION_TYPE_NONE; 992 993 /** Creates a new {@link StringPropertyConfig.Builder}. */ Builder(@onNull String propertyName)994 public Builder(@NonNull String propertyName) { 995 mPropertyName = Preconditions.checkNotNull(propertyName); 996 } 997 998 /** 999 * Sets a natural language description of this property. 1000 * 1001 * <p> For more details about the description field, see {@link 1002 * AppSearchSchema.PropertyConfig#getDescription}. 1003 */ 1004 @CanIgnoreReturnValue 1005 @RequiresFeature( 1006 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1007 name = Features.SCHEMA_SET_DESCRIPTION) 1008 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 1009 @ExperimentalAppSearchApi 1010 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setDescription( @onNull String description)1011 public @NonNull StringPropertyConfig.Builder setDescription( 1012 @NonNull String description) { 1013 mDescription = Preconditions.checkNotNull(description); 1014 return this; 1015 } 1016 1017 /** 1018 * Sets the cardinality of the property (whether it is optional, required or repeated). 1019 * 1020 * <p>If this method is not called, the default cardinality is 1021 * {@link PropertyConfig#CARDINALITY_OPTIONAL}. 1022 */ 1023 @CanIgnoreReturnValue 1024 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setCardinality( @ardinality int cardinality)1025 public @NonNull StringPropertyConfig.Builder setCardinality( 1026 @Cardinality int cardinality) { 1027 Preconditions.checkArgumentInRange( 1028 cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); 1029 mCardinality = cardinality; 1030 return this; 1031 } 1032 1033 /** 1034 * Configures how a property should be indexed so that it can be retrieved by queries. 1035 * 1036 * <p>If this method is not called, the default indexing type is 1037 * {@link StringPropertyConfig#INDEXING_TYPE_NONE}, so that it cannot be matched by 1038 * queries. 1039 */ 1040 @CanIgnoreReturnValue setIndexingType( @tringPropertyConfig.IndexingType int indexingType)1041 public @NonNull StringPropertyConfig.Builder setIndexingType( 1042 @StringPropertyConfig.IndexingType int indexingType) { 1043 Preconditions.checkArgumentInRange( 1044 indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType"); 1045 mIndexingType = indexingType; 1046 return this; 1047 } 1048 1049 /** 1050 * Configures how this property should be tokenized (split into words). 1051 * 1052 * <p>If this method is not called, the default indexing type is 1053 * {@link StringPropertyConfig#TOKENIZER_TYPE_NONE}, so that it is not tokenized. 1054 * 1055 * <p>This method must be called with a value other than 1056 * {@link StringPropertyConfig#TOKENIZER_TYPE_NONE} if the property is indexed (that is, 1057 * if {@link #setIndexingType} has been called with a value other than 1058 * {@link StringPropertyConfig#INDEXING_TYPE_NONE}). 1059 */ 1060 @CanIgnoreReturnValue setTokenizerType( @okenizerType int tokenizerType)1061 public @NonNull StringPropertyConfig.Builder setTokenizerType( 1062 @TokenizerType int tokenizerType) { 1063 Preconditions.checkArgumentInRange( 1064 tokenizerType, TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_RFC822, "tokenizerType"); 1065 mTokenizerType = tokenizerType; 1066 return this; 1067 } 1068 1069 /** 1070 * Configures how this property should be used as a joining matcher. 1071 * 1072 * <p>If this method is not called, the default joinable value type is 1073 * {@link StringPropertyConfig#JOINABLE_VALUE_TYPE_NONE}, so that it is not joinable. 1074 * 1075 * <p>At most, 64 properties can be set as joinable per schema. 1076 */ 1077 @CanIgnoreReturnValue setJoinableValueType( @oinableValueType int joinableValueType)1078 public @NonNull StringPropertyConfig.Builder setJoinableValueType( 1079 @JoinableValueType int joinableValueType) { 1080 Preconditions.checkArgumentInRange( 1081 joinableValueType, 1082 JOINABLE_VALUE_TYPE_NONE, 1083 JOINABLE_VALUE_TYPE_QUALIFIED_ID, 1084 "joinableValueType"); 1085 mJoinableValueType = joinableValueType; 1086 return this; 1087 } 1088 1089 /** 1090 * Configures how the deletion will be propagated between this document and the 1091 * referenced document whose qualified id is held by this property. 1092 * 1093 * <p>If this method is not called, the default delete propagation type is 1094 * {@link StringPropertyConfig#DELETE_PROPAGATION_TYPE_NONE}, indicating that deletion 1095 * will not propagate between this document and the referenced document. 1096 * 1097 * <p>If the delete propagation type is not 1098 * {@link StringPropertyConfig#DELETE_PROPAGATION_TYPE_NONE}, then 1099 * {@link StringPropertyConfig#JOINABLE_VALUE_TYPE_QUALIFIED_ID} must also be set since 1100 * the delete propagation has to use the qualified id. Otherwise, throw 1101 * {@link IllegalStateException} when building. 1102 */ 1103 // TODO(b/384947619) unhide the API once it is ready. 1104 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 1105 @CanIgnoreReturnValue 1106 @RequiresFeature( 1107 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1108 name = Features 1109 .SCHEMA_STRING_PROPERTY_CONFIG_DELETE_PROPAGATION_TYPE_PROPAGATE_FROM) 1110 @FlaggedApi(Flags.FLAG_ENABLE_DELETE_PROPAGATION_TYPE) 1111 @ExperimentalAppSearchApi setDeletePropagationType( @eletePropagationType int deletePropagationType)1112 public @NonNull StringPropertyConfig.Builder setDeletePropagationType( 1113 @DeletePropagationType int deletePropagationType) { 1114 Preconditions.checkArgumentInRange( 1115 deletePropagationType, 1116 DELETE_PROPAGATION_TYPE_NONE, 1117 DELETE_PROPAGATION_TYPE_PROPAGATE_FROM, 1118 "deletePropagationType"); 1119 mDeletePropagationType = deletePropagationType; 1120 return this; 1121 } 1122 1123 /** 1124 * Constructs a new {@link StringPropertyConfig} from the contents of this builder. 1125 * 1126 * @throws IllegalStateException if any following condition: 1127 * <ul> 1128 * <li>Tokenizer type is not {@link StringPropertyConfig#TOKENIZER_TYPE_NONE} with 1129 * indexing type {@link StringPropertyConfig#INDEXING_TYPE_NONE}. 1130 * <li>Indexing type is not {@link StringPropertyConfig#INDEXING_TYPE_NONE} with 1131 * tokenizer type {@link StringPropertyConfig#TOKENIZER_TYPE_NONE}. 1132 * <li>{@link StringPropertyConfig#JOINABLE_VALUE_TYPE_QUALIFIED_ID} is set to a 1133 * {@link PropertyConfig#CARDINALITY_REPEATED} property. 1134 * <li>Deletion type other than 1135 * {@link StringPropertyConfig#DELETE_PROPAGATION_TYPE_NONE} is used without setting 1136 * {@link StringPropertyConfig#JOINABLE_VALUE_TYPE_QUALIFIED_ID}. 1137 * </ul> 1138 */ build()1139 public @NonNull StringPropertyConfig build() { 1140 if (mTokenizerType == TOKENIZER_TYPE_NONE) { 1141 Preconditions.checkState(mIndexingType == INDEXING_TYPE_NONE, "Cannot set " 1142 + "TOKENIZER_TYPE_NONE with an indexing type other than " 1143 + "INDEXING_TYPE_NONE."); 1144 } else { 1145 Preconditions.checkState(mIndexingType != INDEXING_TYPE_NONE, "Cannot set " 1146 + "TOKENIZER_TYPE_PLAIN with INDEXING_TYPE_NONE."); 1147 } 1148 if (mJoinableValueType == JOINABLE_VALUE_TYPE_QUALIFIED_ID) { 1149 Preconditions.checkState(mCardinality != CARDINALITY_REPEATED, "Cannot set " 1150 + "JOINABLE_VALUE_TYPE_QUALIFIED_ID with CARDINALITY_REPEATED."); 1151 } 1152 if (mDeletePropagationType != DELETE_PROPAGATION_TYPE_NONE) { 1153 Preconditions.checkState(mJoinableValueType == JOINABLE_VALUE_TYPE_QUALIFIED_ID, 1154 "Cannot set delete propagation without setting " 1155 + "JOINABLE_VALUE_TYPE_QUALIFIED_ID."); 1156 } 1157 PropertyConfigParcel.StringIndexingConfigParcel stringConfigParcel = 1158 new StringIndexingConfigParcel(mIndexingType, mTokenizerType); 1159 JoinableConfigParcel joinableConfigParcel = 1160 new JoinableConfigParcel(mJoinableValueType, mDeletePropagationType); 1161 return new StringPropertyConfig( 1162 PropertyConfigParcel.createForString( 1163 mPropertyName, 1164 mDescription, 1165 mCardinality, 1166 stringConfigParcel, 1167 joinableConfigParcel)); 1168 } 1169 } 1170 1171 /** 1172 * Appends a debug string for the {@link StringPropertyConfig} instance to the given 1173 * string builder. 1174 * 1175 * <p>This appends fields specific to a {@link StringPropertyConfig} instance. 1176 * 1177 * @param builder the builder to append to. 1178 */ 1179 @OptIn(markerClass = ExperimentalAppSearchApi.class) appendStringPropertyConfigFields(@onNull IndentingStringBuilder builder)1180 void appendStringPropertyConfigFields(@NonNull IndentingStringBuilder builder) { 1181 switch (getIndexingType()) { 1182 case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE: 1183 builder.append("indexingType: INDEXING_TYPE_NONE,\n"); 1184 break; 1185 case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS: 1186 builder.append("indexingType: INDEXING_TYPE_EXACT_TERMS,\n"); 1187 break; 1188 case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES: 1189 builder.append("indexingType: INDEXING_TYPE_PREFIXES,\n"); 1190 break; 1191 default: 1192 builder.append("indexingType: INDEXING_TYPE_UNKNOWN,\n"); 1193 } 1194 1195 switch (getTokenizerType()) { 1196 case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE: 1197 builder.append("tokenizerType: TOKENIZER_TYPE_NONE,\n"); 1198 break; 1199 case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN: 1200 builder.append("tokenizerType: TOKENIZER_TYPE_PLAIN,\n"); 1201 break; 1202 case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_VERBATIM: 1203 builder.append("tokenizerType: TOKENIZER_TYPE_VERBATIM,\n"); 1204 break; 1205 case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_RFC822: 1206 builder.append("tokenizerType: TOKENIZER_TYPE_RFC822,\n"); 1207 break; 1208 default: 1209 builder.append("tokenizerType: TOKENIZER_TYPE_UNKNOWN,\n"); 1210 } 1211 1212 switch (getJoinableValueType()) { 1213 case AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE: 1214 builder.append("joinableValueType: JOINABLE_VALUE_TYPE_NONE,\n"); 1215 break; 1216 case AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID: 1217 builder.append("joinableValueType: JOINABLE_VALUE_TYPE_QUALIFIED_ID,\n"); 1218 break; 1219 default: 1220 builder.append("joinableValueType: JOINABLE_VALUE_TYPE_UNKNOWN,\n"); 1221 } 1222 1223 switch (getDeletePropagationType()) { 1224 case StringPropertyConfig.DELETE_PROPAGATION_TYPE_NONE: 1225 builder.append("deletePropagationType: DELETE_PROPAGATION_TYPE_NONE,\n"); 1226 break; 1227 case StringPropertyConfig.DELETE_PROPAGATION_TYPE_PROPAGATE_FROM: 1228 builder.append( 1229 "deletePropagationType: DELETE_PROPAGATION_TYPE_PROPAGATE_FROM,\n"); 1230 break; 1231 default: 1232 builder.append("deletePropagationType: DELETE_PROPAGATION_TYPE_UNKNOWN,\n"); 1233 } 1234 } 1235 } 1236 1237 /** Configuration for a property containing a 64-bit integer. */ 1238 public static final class LongPropertyConfig extends PropertyConfig { 1239 /** 1240 * Encapsulates the configurations on how AppSearch should query/index these 64-bit 1241 * integers. 1242 * 1243 * @exportToFramework:hide 1244 */ 1245 @IntDef(value = { 1246 INDEXING_TYPE_NONE, 1247 INDEXING_TYPE_RANGE 1248 }) 1249 @RestrictTo(RestrictTo.Scope.LIBRARY) 1250 @Retention(RetentionPolicy.SOURCE) 1251 public @interface IndexingType { 1252 } 1253 1254 /** Content in this property will not be indexed. */ 1255 public static final int INDEXING_TYPE_NONE = 0; 1256 1257 /** 1258 * Content in this property will be indexed and can be fetched via numeric search range 1259 * query. 1260 * 1261 * <p>For example, a property with 1024 should match numeric search range query [0, 2000]. 1262 */ 1263 @RequiresFeature( 1264 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1265 name = Features.NUMERIC_SEARCH) 1266 public static final int INDEXING_TYPE_RANGE = 1; 1267 LongPropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)1268 LongPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 1269 super(propertyConfigParcel); 1270 } 1271 1272 /** Returns how the property is indexed. */ 1273 @LongPropertyConfig.IndexingType getIndexingType()1274 public int getIndexingType() { 1275 PropertyConfigParcel.IntegerIndexingConfigParcel indexingConfigParcel = 1276 mPropertyConfigParcel.getIntegerIndexingConfigParcel(); 1277 if (indexingConfigParcel == null) { 1278 return INDEXING_TYPE_NONE; 1279 } 1280 return indexingConfigParcel.getIndexingType(); 1281 } 1282 1283 /** Returns if the property is enabled for scoring. */ 1284 @ExperimentalAppSearchApi 1285 @FlaggedApi(Flags.FLAG_ENABLE_SCORABLE_PROPERTY) isScoringEnabled()1286 public boolean isScoringEnabled() { 1287 return mPropertyConfigParcel.isScoringEnabled(); 1288 } 1289 1290 /** Builder for {@link LongPropertyConfig}. */ 1291 public static final class Builder { 1292 private final String mPropertyName; 1293 private String mDescription = ""; 1294 @Cardinality 1295 private int mCardinality = CARDINALITY_OPTIONAL; 1296 @LongPropertyConfig.IndexingType 1297 private int mIndexingType = INDEXING_TYPE_NONE; 1298 private boolean mScoringEnabled = false; 1299 1300 /** Creates a new {@link LongPropertyConfig.Builder}. */ Builder(@onNull String propertyName)1301 public Builder(@NonNull String propertyName) { 1302 mPropertyName = Preconditions.checkNotNull(propertyName); 1303 } 1304 1305 /** 1306 * Sets a natural language description of this property. 1307 * 1308 * <p> For more details about the description field, see {@link 1309 * AppSearchSchema.PropertyConfig#getDescription}. 1310 */ 1311 @CanIgnoreReturnValue 1312 @RequiresFeature( 1313 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1314 name = Features.SCHEMA_SET_DESCRIPTION) 1315 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 1316 @ExperimentalAppSearchApi 1317 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setDescription(@onNull String description)1318 public @NonNull LongPropertyConfig.Builder setDescription(@NonNull String description) { 1319 mDescription = Preconditions.checkNotNull(description); 1320 return this; 1321 } 1322 1323 /** 1324 * Sets the cardinality of the property (whether it is optional, required or repeated). 1325 * 1326 * <p>If this method is not called, the default cardinality is 1327 * {@link PropertyConfig#CARDINALITY_OPTIONAL}. 1328 */ 1329 @CanIgnoreReturnValue 1330 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setCardinality( @ardinality int cardinality)1331 public @NonNull LongPropertyConfig.Builder setCardinality( 1332 @Cardinality int cardinality) { 1333 Preconditions.checkArgumentInRange( 1334 cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); 1335 mCardinality = cardinality; 1336 return this; 1337 } 1338 1339 /** 1340 * Configures how a property should be indexed so that it can be retrieved by queries. 1341 * 1342 * <p>If this method is not called, the default indexing type is 1343 * {@link LongPropertyConfig#INDEXING_TYPE_NONE}, so that it will not be indexed 1344 * and cannot be matched by queries. 1345 */ 1346 @CanIgnoreReturnValue setIndexingType( @ongPropertyConfig.IndexingType int indexingType)1347 public @NonNull LongPropertyConfig.Builder setIndexingType( 1348 @LongPropertyConfig.IndexingType int indexingType) { 1349 Preconditions.checkArgumentInRange( 1350 indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_RANGE, "indexingType"); 1351 mIndexingType = indexingType; 1352 return this; 1353 } 1354 1355 /** 1356 * Sets the property enabled or disabled for scoring. 1357 * 1358 * <p>If this method is not called, the default value is false. 1359 * 1360 * <p>If enabled, it can be used in the advanced ranking expression via the function of 1361 * 'getScorableProperty'. 1362 * 1363 * <p>For the detailed documentation, see 1364 * {@link SearchSpec.Builder#setRankingStrategy(String)}. 1365 */ 1366 @CanIgnoreReturnValue 1367 @RequiresFeature( 1368 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1369 name = Features.SCHEMA_SCORABLE_PROPERTY_CONFIG) 1370 @ExperimentalAppSearchApi 1371 @FlaggedApi(Flags.FLAG_ENABLE_SCORABLE_PROPERTY) setScoringEnabled( boolean scoringEnabled)1372 public @NonNull LongPropertyConfig.Builder setScoringEnabled( 1373 boolean scoringEnabled) { 1374 mScoringEnabled = scoringEnabled; 1375 return this; 1376 } 1377 1378 /** Constructs a new {@link LongPropertyConfig} from the contents of this builder. */ build()1379 public @NonNull LongPropertyConfig build() { 1380 return new LongPropertyConfig( 1381 PropertyConfigParcel.createForLong( 1382 mPropertyName, mDescription, mCardinality, mIndexingType, 1383 mScoringEnabled)); 1384 } 1385 } 1386 1387 /** 1388 * Appends a debug string for the {@link LongPropertyConfig} instance to the given 1389 * string builder. 1390 * 1391 * <p>This appends fields specific to a {@link LongPropertyConfig} instance. 1392 * 1393 * @param builder the builder to append to. 1394 */ appendLongPropertyConfigFields(@onNull IndentingStringBuilder builder)1395 void appendLongPropertyConfigFields(@NonNull IndentingStringBuilder builder) { 1396 switch (getIndexingType()) { 1397 case AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE: 1398 builder.append("indexingType: INDEXING_TYPE_NONE,\n"); 1399 break; 1400 case AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE: 1401 builder.append("indexingType: INDEXING_TYPE_RANGE,\n"); 1402 break; 1403 default: 1404 builder.append("indexingType: INDEXING_TYPE_UNKNOWN,\n"); 1405 } 1406 } 1407 } 1408 1409 /** Configuration for a property containing a double-precision decimal number. */ 1410 public static final class DoublePropertyConfig extends PropertyConfig { DoublePropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)1411 DoublePropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 1412 super(propertyConfigParcel); 1413 } 1414 1415 /** Returns if the property is enabled for scoring. */ 1416 @ExperimentalAppSearchApi 1417 @FlaggedApi(Flags.FLAG_ENABLE_SCORABLE_PROPERTY) isScoringEnabled()1418 public boolean isScoringEnabled() { 1419 return mPropertyConfigParcel.isScoringEnabled(); 1420 } 1421 1422 /** Builder for {@link DoublePropertyConfig}. */ 1423 public static final class Builder { 1424 private final String mPropertyName; 1425 private String mDescription = ""; 1426 @Cardinality 1427 private int mCardinality = CARDINALITY_OPTIONAL; 1428 private boolean mScoringEnabled = false; 1429 1430 /** Creates a new {@link DoublePropertyConfig.Builder}. */ Builder(@onNull String propertyName)1431 public Builder(@NonNull String propertyName) { 1432 mPropertyName = Preconditions.checkNotNull(propertyName); 1433 } 1434 1435 /** 1436 * Sets a natural language description of this property. 1437 * 1438 * <p> For more details about the description field, see {@link 1439 * AppSearchSchema.PropertyConfig#getDescription}. 1440 */ 1441 @CanIgnoreReturnValue 1442 @RequiresFeature( 1443 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1444 name = Features.SCHEMA_SET_DESCRIPTION) 1445 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 1446 @ExperimentalAppSearchApi 1447 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setDescription( @onNull String description)1448 public @NonNull DoublePropertyConfig.Builder setDescription( 1449 @NonNull String description) { 1450 mDescription = Preconditions.checkNotNull(description); 1451 return this; 1452 } 1453 1454 /** 1455 * Sets the cardinality of the property (whether it is optional, required or repeated). 1456 * 1457 * <p>If this method is not called, the default cardinality is 1458 * {@link PropertyConfig#CARDINALITY_OPTIONAL}. 1459 */ 1460 @CanIgnoreReturnValue 1461 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setCardinality( @ardinality int cardinality)1462 public @NonNull DoublePropertyConfig.Builder setCardinality( 1463 @Cardinality int cardinality) { 1464 Preconditions.checkArgumentInRange( 1465 cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); 1466 mCardinality = cardinality; 1467 return this; 1468 } 1469 1470 /** 1471 * Sets the property enabled or disabled for scoring. 1472 * 1473 * <p>If this method is not called, the default value is false. 1474 * 1475 * <p>If enabled, it can be used in the advanced ranking expression via the function of 1476 * 'getScorableProperty'. 1477 * 1478 * <p>For the detailed documentation, see 1479 * {@link SearchSpec.Builder#setRankingStrategy(String)}. 1480 */ 1481 @CanIgnoreReturnValue 1482 @RequiresFeature( 1483 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1484 name = Features.SCHEMA_SCORABLE_PROPERTY_CONFIG) 1485 @ExperimentalAppSearchApi 1486 @FlaggedApi(Flags.FLAG_ENABLE_SCORABLE_PROPERTY) setScoringEnabled( boolean scoringEnabled)1487 public @NonNull DoublePropertyConfig.Builder setScoringEnabled( 1488 boolean scoringEnabled) { 1489 mScoringEnabled = scoringEnabled; 1490 return this; 1491 } 1492 1493 /** Constructs a new {@link DoublePropertyConfig} from the contents of this builder. */ build()1494 public @NonNull DoublePropertyConfig build() { 1495 return new DoublePropertyConfig( 1496 PropertyConfigParcel.createForDouble( 1497 mPropertyName, mDescription, mCardinality, mScoringEnabled)); 1498 } 1499 } 1500 } 1501 1502 /** Configuration for a property containing a boolean. */ 1503 public static final class BooleanPropertyConfig extends PropertyConfig { BooleanPropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)1504 BooleanPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 1505 super(propertyConfigParcel); 1506 } 1507 1508 /** Returns if the property is enabled for scoring. */ 1509 @ExperimentalAppSearchApi 1510 @FlaggedApi(Flags.FLAG_ENABLE_SCORABLE_PROPERTY) isScoringEnabled()1511 public boolean isScoringEnabled() { 1512 return mPropertyConfigParcel.isScoringEnabled(); 1513 } 1514 1515 /** Builder for {@link BooleanPropertyConfig}. */ 1516 public static final class Builder { 1517 private final String mPropertyName; 1518 private String mDescription = ""; 1519 @Cardinality 1520 private int mCardinality = CARDINALITY_OPTIONAL; 1521 private boolean mScoringEnabled = false; 1522 1523 /** Creates a new {@link BooleanPropertyConfig.Builder}. */ Builder(@onNull String propertyName)1524 public Builder(@NonNull String propertyName) { 1525 mPropertyName = Preconditions.checkNotNull(propertyName); 1526 } 1527 1528 /** 1529 Sets a natural language description of this property. 1530 * 1531 * <p> For more details about the description field, see {@link 1532 * AppSearchSchema.PropertyConfig#getDescription}. 1533 */ 1534 @CanIgnoreReturnValue 1535 @RequiresFeature( 1536 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1537 name = Features.SCHEMA_SET_DESCRIPTION) 1538 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 1539 @ExperimentalAppSearchApi 1540 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setDescription( @onNull String description)1541 public @NonNull BooleanPropertyConfig.Builder setDescription( 1542 @NonNull String description) { 1543 mDescription = Preconditions.checkNotNull(description); 1544 return this; 1545 } 1546 1547 /** 1548 * Sets the cardinality of the property (whether it is optional, required or repeated). 1549 * 1550 * <p>If this method is not called, the default cardinality is 1551 * {@link PropertyConfig#CARDINALITY_OPTIONAL}. 1552 */ 1553 @CanIgnoreReturnValue 1554 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setCardinality( @ardinality int cardinality)1555 public @NonNull BooleanPropertyConfig.Builder setCardinality( 1556 @Cardinality int cardinality) { 1557 Preconditions.checkArgumentInRange( 1558 cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); 1559 mCardinality = cardinality; 1560 return this; 1561 } 1562 1563 /** 1564 * Sets the property enabled or disabled for scoring. 1565 * 1566 * <p>If this method is not called, the default value is false. 1567 * 1568 * <p>If enabled, it can be used in the advanced ranking expression via the function of 1569 * 'getScorableProperty'. 1570 * 1571 * <p>For the detailed documentation, see 1572 * {@link SearchSpec.Builder#setRankingStrategy(String)}. 1573 */ 1574 @CanIgnoreReturnValue 1575 @RequiresFeature( 1576 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1577 name = Features.SCHEMA_SCORABLE_PROPERTY_CONFIG) 1578 @ExperimentalAppSearchApi 1579 @FlaggedApi(Flags.FLAG_ENABLE_SCORABLE_PROPERTY) setScoringEnabled( boolean scoringEnabled)1580 public @NonNull BooleanPropertyConfig.Builder setScoringEnabled( 1581 boolean scoringEnabled) { 1582 mScoringEnabled = scoringEnabled; 1583 return this; 1584 } 1585 1586 /** Constructs a new {@link BooleanPropertyConfig} from the contents of this builder. */ build()1587 public @NonNull BooleanPropertyConfig build() { 1588 return new BooleanPropertyConfig( 1589 PropertyConfigParcel.createForBoolean( 1590 mPropertyName, mDescription, mCardinality, mScoringEnabled)); 1591 } 1592 } 1593 } 1594 1595 /** Configuration for a property containing a byte array. */ 1596 public static final class BytesPropertyConfig extends PropertyConfig { BytesPropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)1597 BytesPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 1598 super(propertyConfigParcel); 1599 } 1600 1601 /** Builder for {@link BytesPropertyConfig}. */ 1602 public static final class Builder { 1603 private final String mPropertyName; 1604 private String mDescription = ""; 1605 @Cardinality 1606 private int mCardinality = CARDINALITY_OPTIONAL; 1607 1608 /** Creates a new {@link BytesPropertyConfig.Builder}. */ Builder(@onNull String propertyName)1609 public Builder(@NonNull String propertyName) { 1610 mPropertyName = Preconditions.checkNotNull(propertyName); 1611 } 1612 1613 /** 1614 * Sets a natural language description of this property. 1615 * 1616 * <p> For more details about the description field, see {@link 1617 * AppSearchSchema.PropertyConfig#getDescription}. 1618 */ 1619 @CanIgnoreReturnValue 1620 @RequiresFeature( 1621 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1622 name = Features.SCHEMA_SET_DESCRIPTION) 1623 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 1624 @ExperimentalAppSearchApi 1625 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setDescription( @onNull String description)1626 public @NonNull BytesPropertyConfig.Builder setDescription( 1627 @NonNull String description) { 1628 mDescription = Preconditions.checkNotNull(description); 1629 return this; 1630 } 1631 1632 /** 1633 * Sets the cardinality of the property (whether it is optional, required or repeated). 1634 * 1635 * <p>If this method is not called, the default cardinality is 1636 * {@link PropertyConfig#CARDINALITY_OPTIONAL}. 1637 */ 1638 @CanIgnoreReturnValue 1639 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setCardinality( @ardinality int cardinality)1640 public @NonNull BytesPropertyConfig.Builder setCardinality( 1641 @Cardinality int cardinality) { 1642 Preconditions.checkArgumentInRange( 1643 cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); 1644 mCardinality = cardinality; 1645 return this; 1646 } 1647 1648 /** 1649 * Constructs a new {@link BytesPropertyConfig} from the contents of this builder. 1650 */ build()1651 public @NonNull BytesPropertyConfig build() { 1652 return new BytesPropertyConfig( 1653 PropertyConfigParcel.createForBytes( 1654 mPropertyName, mDescription, mCardinality)); 1655 } 1656 } 1657 } 1658 1659 /** Configuration for a property containing another Document. */ 1660 public static final class DocumentPropertyConfig extends PropertyConfig { DocumentPropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)1661 DocumentPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 1662 super(propertyConfigParcel); 1663 } 1664 1665 /** Returns the logical schema-type of the contents of this document property. */ getSchemaType()1666 public @NonNull String getSchemaType() { 1667 return Preconditions.checkNotNull(mPropertyConfigParcel.getSchemaType()); 1668 } 1669 1670 /** 1671 * Returns whether properties in the nested document should be indexed according to that 1672 * document's schema. 1673 * 1674 * <p>If false, the nested document's properties are not indexed regardless of its own 1675 * schema. 1676 * 1677 * @see DocumentPropertyConfig.Builder#addIndexableNestedProperties(Collection) for 1678 * indexing a subset of properties from the nested document. 1679 */ shouldIndexNestedProperties()1680 public boolean shouldIndexNestedProperties() { 1681 DocumentIndexingConfigParcel indexingConfigParcel = 1682 mPropertyConfigParcel.getDocumentIndexingConfigParcel(); 1683 if (indexingConfigParcel == null) { 1684 return false; 1685 } 1686 1687 return indexingConfigParcel.shouldIndexNestedProperties(); 1688 } 1689 1690 /** 1691 * Returns the list of indexable nested properties for the nested document. 1692 */ 1693 @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES) getIndexableNestedProperties()1694 public @NonNull List<String> getIndexableNestedProperties() { 1695 DocumentIndexingConfigParcel indexingConfigParcel = 1696 mPropertyConfigParcel.getDocumentIndexingConfigParcel(); 1697 if (indexingConfigParcel == null) { 1698 return Collections.emptyList(); 1699 } 1700 1701 List<String> indexableNestedPropertiesList = 1702 indexingConfigParcel.getIndexableNestedPropertiesList(); 1703 if (indexableNestedPropertiesList == null) { 1704 return Collections.emptyList(); 1705 } 1706 1707 return Collections.unmodifiableList(indexableNestedPropertiesList); 1708 } 1709 1710 /** Builder for {@link DocumentPropertyConfig}. */ 1711 public static final class Builder { 1712 private final String mPropertyName; 1713 private final String mSchemaType; 1714 private String mDescription = ""; 1715 @Cardinality 1716 private int mCardinality = CARDINALITY_OPTIONAL; 1717 private boolean mShouldIndexNestedProperties = false; 1718 private final Set<String> mIndexableNestedPropertiesList = new ArraySet<>(); 1719 1720 /** 1721 * Creates a new {@link DocumentPropertyConfig.Builder}. 1722 * 1723 * @param propertyName The logical name of the property in the schema, which will be 1724 * used as the key for this property in 1725 * {@link GenericDocument.Builder#setPropertyDocument}. 1726 * @param schemaType The type of documents which will be stored in this property. 1727 * Documents of different types cannot be mixed into a single 1728 * property. 1729 */ Builder(@onNull String propertyName, @NonNull String schemaType)1730 public Builder(@NonNull String propertyName, @NonNull String schemaType) { 1731 mPropertyName = Preconditions.checkNotNull(propertyName); 1732 mSchemaType = Preconditions.checkNotNull(schemaType); 1733 } 1734 1735 /** 1736 * Sets a natural language description of this property. 1737 * 1738 * <p> For more details about the description field, see {@link 1739 * AppSearchSchema.PropertyConfig#getDescription}. 1740 */ 1741 @CanIgnoreReturnValue 1742 @RequiresFeature( 1743 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1744 name = Features.SCHEMA_SET_DESCRIPTION) 1745 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 1746 @ExperimentalAppSearchApi 1747 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setDescription( @onNull String description)1748 public @NonNull DocumentPropertyConfig.Builder setDescription( 1749 @NonNull String description) { 1750 mDescription = Preconditions.checkNotNull(description); 1751 return this; 1752 } 1753 1754 /** 1755 * Sets the cardinality of the property (whether it is optional, required or repeated). 1756 * 1757 * <p>If this method is not called, the default cardinality is 1758 * {@link PropertyConfig#CARDINALITY_OPTIONAL}. 1759 */ 1760 @CanIgnoreReturnValue 1761 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setCardinality( @ardinality int cardinality)1762 public @NonNull DocumentPropertyConfig.Builder setCardinality( 1763 @Cardinality int cardinality) { 1764 Preconditions.checkArgumentInRange( 1765 cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); 1766 mCardinality = cardinality; 1767 return this; 1768 } 1769 1770 /** 1771 * Configures whether properties in the nested document should be indexed according to 1772 * that document's schema. 1773 * 1774 * <p>If false, the nested document's properties are not indexed regardless of its own 1775 * schema. 1776 * 1777 * <p>To index a subset of properties from the nested document, set this to false and 1778 * use {@link #addIndexableNestedProperties(Collection)}. 1779 */ 1780 @CanIgnoreReturnValue setShouldIndexNestedProperties( boolean indexNestedProperties)1781 public @NonNull DocumentPropertyConfig.Builder setShouldIndexNestedProperties( 1782 boolean indexNestedProperties) { 1783 mShouldIndexNestedProperties = indexNestedProperties; 1784 return this; 1785 } 1786 1787 /** 1788 * Adds one or more properties for indexing from the nested document property. 1789 * 1790 * @see #addIndexableNestedProperties(Collection) 1791 */ 1792 @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES) 1793 @CanIgnoreReturnValue 1794 @RequiresFeature( 1795 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1796 name = Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES) addIndexableNestedProperties( @onNull String... indexableNestedProperties)1797 public @NonNull DocumentPropertyConfig.Builder addIndexableNestedProperties( 1798 @NonNull String... indexableNestedProperties) { 1799 Preconditions.checkNotNull(indexableNestedProperties); 1800 return addIndexableNestedProperties(Arrays.asList(indexableNestedProperties)); 1801 } 1802 1803 /** 1804 * Adds one or more property paths for indexing from the nested document property. 1805 * 1806 * @see #addIndexableNestedProperties(Collection) 1807 */ 1808 @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES) 1809 @CanIgnoreReturnValue 1810 @SuppressLint("MissingGetterMatchingBuilder") 1811 @RequiresFeature( 1812 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1813 name = Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES) addIndexableNestedPropertyPaths( @onNull PropertyPath... indexableNestedPropertyPaths)1814 public @NonNull DocumentPropertyConfig.Builder addIndexableNestedPropertyPaths( 1815 @NonNull PropertyPath... indexableNestedPropertyPaths) { 1816 Preconditions.checkNotNull(indexableNestedPropertyPaths); 1817 return addIndexableNestedPropertyPaths(Arrays.asList(indexableNestedPropertyPaths)); 1818 } 1819 1820 /** 1821 * Adds one or more properties for indexing from the nested document. The added property 1822 * will be indexed according to that property's indexing configurations in the 1823 * document's schema definition. All properties in this list will consume a sectionId 1824 * regardless of its actual indexing config -- this includes properties added that 1825 * do not actually exist, as well as properties that are not set as indexable in the 1826 * nested schema type. 1827 * 1828 * <p>Input strings should follow the format of the property path for the nested 1829 * property, with '.' as the path separator. This nested document's property name 1830 * should not be included in the property path. 1831 * 1832 * <p>Ex. Consider an 'Organization' schema type which defines a nested document 1833 * property 'address' (Address schema type), where Address has a nested document 1834 * property 'country' (Country schema type with string 'name' property), and a string 1835 * 'street' property. The 'street' and 'country's name' properties from the 'address' 1836 * document property can be indexed for the 'Organization' schema type by calling: 1837 * <pre>{@code 1838 * OrganizationSchema.addProperty( 1839 * new DocumentPropertyConfig.Builder("address", "Address") 1840 * .addIndexableNestedProperties("street", "country.name") 1841 * .build()). 1842 * }</pre> 1843 * 1844 * <p>{@link DocumentPropertyConfig.Builder#setShouldIndexNestedProperties} is 1845 * required to be false if any indexable nested property is added this way for the 1846 * document property. Attempting to build a DocumentPropertyConfig when this is not 1847 * true throws {@link IllegalArgumentException}. 1848 */ 1849 @CanIgnoreReturnValue 1850 @RequiresFeature( 1851 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1852 name = Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES) addIndexableNestedProperties( @onNull Collection<String> indexableNestedProperties)1853 public @NonNull DocumentPropertyConfig.Builder addIndexableNestedProperties( 1854 @NonNull Collection<String> indexableNestedProperties) { 1855 Preconditions.checkNotNull(indexableNestedProperties); 1856 mIndexableNestedPropertiesList.addAll(indexableNestedProperties); 1857 return this; 1858 } 1859 1860 /** 1861 * Adds one or more property paths for indexing from the nested document property. 1862 * 1863 * @see #addIndexableNestedProperties(Collection) 1864 */ 1865 @FlaggedApi(Flags.FLAG_ENABLE_GET_PARENT_TYPES_AND_INDEXABLE_NESTED_PROPERTIES) 1866 @CanIgnoreReturnValue 1867 @SuppressLint("MissingGetterMatchingBuilder") 1868 @RequiresFeature( 1869 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1870 name = Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES) addIndexableNestedPropertyPaths( @onNull Collection<PropertyPath> indexableNestedPropertyPaths)1871 public @NonNull DocumentPropertyConfig.Builder addIndexableNestedPropertyPaths( 1872 @NonNull Collection<PropertyPath> indexableNestedPropertyPaths) { 1873 Preconditions.checkNotNull(indexableNestedPropertyPaths); 1874 List<PropertyPath> propertyPathList = new ArrayList<>(indexableNestedPropertyPaths); 1875 for (int i = 0; i < indexableNestedPropertyPaths.size(); i++) { 1876 mIndexableNestedPropertiesList.add(propertyPathList.get(i).toString()); 1877 } 1878 return this; 1879 } 1880 1881 /** 1882 * Constructs a new {@link PropertyConfig} from the contents of this builder. 1883 * 1884 * @throws IllegalArgumentException if the provided PropertyConfig sets 1885 * {@link #shouldIndexNestedProperties()} to true and has one or more properties 1886 * defined using {@link #addIndexableNestedProperties(Collection)}. 1887 */ build()1888 public @NonNull DocumentPropertyConfig build() { 1889 if (mShouldIndexNestedProperties && !mIndexableNestedPropertiesList.isEmpty()) { 1890 throw new IllegalArgumentException( 1891 "DocumentIndexingConfig#shouldIndexNestedProperties is required " 1892 + "to be false when one or more indexableNestedProperties are " 1893 + "provided."); 1894 } 1895 return new DocumentPropertyConfig( 1896 PropertyConfigParcel.createForDocument( 1897 mPropertyName, 1898 mDescription, 1899 mCardinality, 1900 mSchemaType, 1901 new DocumentIndexingConfigParcel(mShouldIndexNestedProperties, 1902 new ArrayList<>(mIndexableNestedPropertiesList)))); 1903 } 1904 } 1905 1906 /** 1907 * Appends a debug string for the {@link DocumentPropertyConfig} instance to the given 1908 * string builder. 1909 * 1910 * <p>This appends fields specific to a {@link DocumentPropertyConfig} instance. 1911 * 1912 * @param builder the builder to append to. 1913 */ appendDocumentPropertyConfigFields(@onNull IndentingStringBuilder builder)1914 void appendDocumentPropertyConfigFields(@NonNull IndentingStringBuilder builder) { 1915 builder 1916 .append("shouldIndexNestedProperties: ") 1917 .append(shouldIndexNestedProperties()) 1918 .append(",\n"); 1919 1920 builder.append("indexableNestedProperties: ") 1921 .append(getIndexableNestedProperties()) 1922 .append(",\n"); 1923 1924 builder.append("schemaType: \"").append(getSchemaType()).append("\",\n"); 1925 } 1926 } 1927 1928 /** 1929 * Configuration for a property of type {@link EmbeddingVector} in a Document. 1930 */ 1931 @RequiresFeature( 1932 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 1933 name = Features.SCHEMA_EMBEDDING_PROPERTY_CONFIG) 1934 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) 1935 public static final class EmbeddingPropertyConfig extends PropertyConfig { 1936 /** 1937 * Encapsulates the configurations on how AppSearch should query/index these embedding 1938 * vectors. 1939 * 1940 * @exportToFramework:hide 1941 */ 1942 @IntDef(value = { 1943 INDEXING_TYPE_NONE, 1944 INDEXING_TYPE_SIMILARITY 1945 }) 1946 @RestrictTo(RestrictTo.Scope.LIBRARY) 1947 @Retention(RetentionPolicy.SOURCE) 1948 public @interface IndexingType { 1949 } 1950 1951 /** Content in this property will not be indexed. */ 1952 public static final int INDEXING_TYPE_NONE = 0; 1953 1954 /** 1955 * Embedding vectors in this property will be indexed. 1956 * 1957 * <p>The index offers 100% accuracy, but has linear time complexity based on the number 1958 * of embedding vectors within the index. 1959 */ 1960 public static final int INDEXING_TYPE_SIMILARITY = 1; 1961 1962 /** 1963 * Indicates whether the vector contents of this property should be quantized. 1964 * 1965 * @exportToFramework:hide 1966 */ 1967 @IntDef(value = { 1968 QUANTIZATION_TYPE_NONE, 1969 QUANTIZATION_TYPE_8_BIT, 1970 }) 1971 @RestrictTo(RestrictTo.Scope.LIBRARY) 1972 @Retention(RetentionPolicy.SOURCE) 1973 @ExperimentalAppSearchApi 1974 public @interface QuantizationType { 1975 } 1976 1977 /** Contents in this property will not be quantized. */ 1978 @ExperimentalAppSearchApi 1979 public static final int QUANTIZATION_TYPE_NONE = 0; 1980 1981 /** Contents in this property will be quantized to 8 bits. */ 1982 @ExperimentalAppSearchApi 1983 public static final int QUANTIZATION_TYPE_8_BIT = 1; 1984 EmbeddingPropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)1985 EmbeddingPropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 1986 super(propertyConfigParcel); 1987 } 1988 1989 /** Returns how the property is indexed. */ 1990 @EmbeddingPropertyConfig.IndexingType getIndexingType()1991 public int getIndexingType() { 1992 PropertyConfigParcel.EmbeddingIndexingConfigParcel indexingConfigParcel = 1993 mPropertyConfigParcel.getEmbeddingIndexingConfigParcel(); 1994 if (indexingConfigParcel == null) { 1995 return INDEXING_TYPE_NONE; 1996 } 1997 return indexingConfigParcel.getIndexingType(); 1998 } 1999 2000 /** 2001 * Returns how the embedding contents of this property should be quantized. 2002 * 2003 * <p>If the property isn't indexed, returns {@link #QUANTIZATION_TYPE_NONE}. 2004 */ 2005 @EmbeddingPropertyConfig.QuantizationType 2006 @ExperimentalAppSearchApi 2007 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_QUANTIZATION) getQuantizationType()2008 public int getQuantizationType() { 2009 PropertyConfigParcel.EmbeddingIndexingConfigParcel indexingConfigParcel = 2010 mPropertyConfigParcel.getEmbeddingIndexingConfigParcel(); 2011 if (indexingConfigParcel == null) { 2012 return QUANTIZATION_TYPE_NONE; 2013 } 2014 return indexingConfigParcel.getQuantizationType(); 2015 } 2016 2017 /** Builder for {@link EmbeddingPropertyConfig}. */ 2018 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_PROPERTY_CONFIG) 2019 @OptIn(markerClass = ExperimentalAppSearchApi.class) 2020 public static final class Builder { 2021 private final String mPropertyName; 2022 private String mDescription = ""; 2023 @Cardinality 2024 private int mCardinality = CARDINALITY_OPTIONAL; 2025 @EmbeddingPropertyConfig.IndexingType 2026 private int mIndexingType = INDEXING_TYPE_NONE; 2027 @EmbeddingPropertyConfig.QuantizationType 2028 private int mQuantizationType = QUANTIZATION_TYPE_NONE; 2029 2030 /** Creates a new {@link EmbeddingPropertyConfig.Builder}. */ Builder(@onNull String propertyName)2031 public Builder(@NonNull String propertyName) { 2032 mPropertyName = Preconditions.checkNotNull(propertyName); 2033 } 2034 2035 /** 2036 * Sets a natural language description of this property. 2037 * 2038 * <p> For more details about the description field, see {@link 2039 * AppSearchSchema.PropertyConfig#getDescription}. 2040 */ 2041 @CanIgnoreReturnValue 2042 @RequiresFeature( 2043 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 2044 name = Features.SCHEMA_SET_DESCRIPTION) 2045 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 2046 @ExperimentalAppSearchApi 2047 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setDescription( @onNull String description)2048 public @NonNull EmbeddingPropertyConfig.Builder setDescription( 2049 @NonNull String description) { 2050 mDescription = Preconditions.checkNotNull(description); 2051 return this; 2052 } 2053 2054 /** 2055 * Sets the cardinality of the property (whether it is optional, required or repeated). 2056 * 2057 * <p>If this method is not called, the default cardinality is 2058 * {@link PropertyConfig#CARDINALITY_OPTIONAL}. 2059 */ 2060 @CanIgnoreReturnValue 2061 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setCardinality( @ardinality int cardinality)2062 public @NonNull EmbeddingPropertyConfig.Builder setCardinality( 2063 @Cardinality int cardinality) { 2064 Preconditions.checkArgumentInRange( 2065 cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); 2066 mCardinality = cardinality; 2067 return this; 2068 } 2069 2070 /** 2071 * Configures how a property should be indexed so that it can be retrieved by queries. 2072 * 2073 * <p>If this method is not called, the default indexing type is 2074 * {@link EmbeddingPropertyConfig#INDEXING_TYPE_NONE}, so that it will not be indexed 2075 * and cannot be matched by queries. 2076 */ 2077 @CanIgnoreReturnValue setIndexingType( @mbeddingPropertyConfig.IndexingType int indexingType)2078 public @NonNull EmbeddingPropertyConfig.Builder setIndexingType( 2079 @EmbeddingPropertyConfig.IndexingType int indexingType) { 2080 Preconditions.checkArgumentInRange( 2081 indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_SIMILARITY, 2082 "indexingType"); 2083 mIndexingType = indexingType; 2084 return this; 2085 } 2086 2087 /** 2088 * Configures whether the vector contents of this property should be quantized. 2089 * 2090 * <p>Quantization can reduce the size of the embedding search index, potentially 2091 * leading to faster embedding search due to lower I/O bandwidth. Quantization is 2092 * usually very reliable and in most cases will have a negligible impact on recall. 2093 * Using quantization is strongly recommended. 2094 * 2095 * <p>If this method is not called, the default quantization type is 2096 * {@link EmbeddingPropertyConfig#QUANTIZATION_TYPE_NONE}. 2097 */ 2098 @ExperimentalAppSearchApi 2099 @RequiresFeature( 2100 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 2101 name = Features.SCHEMA_EMBEDDING_QUANTIZATION) 2102 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_EMBEDDING_QUANTIZATION) 2103 @CanIgnoreReturnValue setQuantizationType( @mbeddingPropertyConfig.QuantizationType int quantizationType)2104 public @NonNull EmbeddingPropertyConfig.Builder setQuantizationType( 2105 @EmbeddingPropertyConfig.QuantizationType int quantizationType) { 2106 Preconditions.checkArgumentInRange( 2107 quantizationType, QUANTIZATION_TYPE_NONE, QUANTIZATION_TYPE_8_BIT, 2108 "quantizationType"); 2109 mQuantizationType = quantizationType; 2110 return this; 2111 } 2112 2113 /** 2114 * Constructs a new {@link EmbeddingPropertyConfig} from the contents of this 2115 * builder. 2116 */ build()2117 public @NonNull EmbeddingPropertyConfig build() { 2118 return new EmbeddingPropertyConfig( 2119 PropertyConfigParcel.createForEmbedding( 2120 mPropertyName, mDescription, mCardinality, mIndexingType, 2121 mQuantizationType)); 2122 } 2123 } 2124 } 2125 2126 /** 2127 * Configuration for a property of type {@link AppSearchBlobHandle} in a Document. 2128 */ 2129 @RequiresFeature( 2130 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 2131 name = Features.BLOB_STORAGE) 2132 @FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE) 2133 @ExperimentalAppSearchApi 2134 public static final class BlobHandlePropertyConfig extends PropertyConfig { BlobHandlePropertyConfig(@onNull PropertyConfigParcel propertyConfigParcel)2135 BlobHandlePropertyConfig(@NonNull PropertyConfigParcel propertyConfigParcel) { 2136 super(propertyConfigParcel); 2137 } 2138 2139 /** Builder for {@link BlobHandlePropertyConfig}. */ 2140 @FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE) 2141 public static final class Builder { 2142 private final String mPropertyName; 2143 private String mDescription = ""; 2144 @Cardinality 2145 private int mCardinality = CARDINALITY_OPTIONAL; 2146 2147 /** Creates a new {@link BlobHandlePropertyConfig.Builder}. */ Builder(@onNull String propertyName)2148 public Builder(@NonNull String propertyName) { 2149 mPropertyName = Preconditions.checkNotNull(propertyName); 2150 } 2151 2152 /** 2153 * Sets a natural language description of this property. 2154 * 2155 * <p> For more details about the description field, see {@link 2156 * AppSearchSchema.PropertyConfig#getDescription}. 2157 */ 2158 @CanIgnoreReturnValue 2159 @RequiresFeature( 2160 enforcement = "androidx.appsearch.app.Features#isFeatureSupported", 2161 name = Features.SCHEMA_SET_DESCRIPTION) 2162 @FlaggedApi(Flags.FLAG_ENABLE_SCHEMA_DESCRIPTION) 2163 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setDescription(@onNull String description)2164 public @NonNull Builder setDescription(@NonNull String description) { 2165 mDescription = Preconditions.checkNotNull(description); 2166 return this; 2167 } 2168 2169 /** 2170 * Sets the cardinality of the property (whether it is optional, required or repeated). 2171 * 2172 * <p>If this method is not called, the default cardinality is 2173 * {@link PropertyConfig#CARDINALITY_OPTIONAL}. 2174 */ 2175 @CanIgnoreReturnValue 2176 @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass setCardinality(@ardinality int cardinality)2177 public @NonNull Builder setCardinality(@Cardinality int cardinality) { 2178 Preconditions.checkArgumentInRange( 2179 cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); 2180 mCardinality = cardinality; 2181 return this; 2182 } 2183 2184 /** 2185 * Constructs a new {@link BlobHandlePropertyConfig} from the contents of this 2186 * builder. 2187 */ build()2188 public @NonNull BlobHandlePropertyConfig build() { 2189 return new BlobHandlePropertyConfig( 2190 PropertyConfigParcel.createForBlobHandle( 2191 mPropertyName, mDescription, mCardinality)); 2192 } 2193 } 2194 } 2195 } 2196