• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &lt;: T2 and T2 &lt;: T3 are
295          * known, then T1 &lt;: T3 will be inferred automatically, where &lt;: 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 &lt;: 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 &lt;: 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 &lt; OPTIONAL &lt; 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