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