1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.appsearch.localstorage.converter;
18 
19 import android.util.Log;
20 
21 import androidx.annotation.OptIn;
22 import androidx.annotation.RestrictTo;
23 import androidx.appsearch.app.AppSearchSchema;
24 import androidx.appsearch.app.ExperimentalAppSearchApi;
25 import androidx.core.util.Preconditions;
26 
27 import com.google.android.icing.proto.DocumentIndexingConfig;
28 import com.google.android.icing.proto.EmbeddingIndexingConfig;
29 import com.google.android.icing.proto.IntegerIndexingConfig;
30 import com.google.android.icing.proto.JoinableConfig;
31 import com.google.android.icing.proto.PropertyConfigProto;
32 import com.google.android.icing.proto.SchemaTypeConfigProto;
33 import com.google.android.icing.proto.SchemaTypeConfigProtoOrBuilder;
34 import com.google.android.icing.proto.StringIndexingConfig;
35 import com.google.android.icing.proto.TermMatchType;
36 
37 import org.jspecify.annotations.NonNull;
38 
39 import java.util.List;
40 
41 /**
42  * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}.
43  * @exportToFramework:hide
44  */
45 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
46 public final class SchemaToProtoConverter {
47     private static final String TAG = "AppSearchSchemaToProtoC";
48 
SchemaToProtoConverter()49     private SchemaToProtoConverter() {}
50 
51     /**
52      * Converts an {@link androidx.appsearch.app.AppSearchSchema} into a
53      * {@link SchemaTypeConfigProto}.
54      */
55     // TODO(b/284356266): Consider handling addition of schema name prefixes in this function.
56     @OptIn(markerClass = ExperimentalAppSearchApi.class)
toSchemaTypeConfigProto( @onNull AppSearchSchema schema, int version)57     public static @NonNull SchemaTypeConfigProto toSchemaTypeConfigProto(
58             @NonNull AppSearchSchema schema, int version) {
59         Preconditions.checkNotNull(schema);
60         SchemaTypeConfigProto.Builder protoBuilder = SchemaTypeConfigProto.newBuilder()
61                 .setSchemaType(schema.getSchemaType())
62                 .setDescription(schema.getDescription())
63                 .setVersion(version);
64         List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
65         for (int i = 0; i < properties.size(); i++) {
66             PropertyConfigProto propertyProto = toPropertyConfigProto(properties.get(i));
67             protoBuilder.addProperties(propertyProto);
68         }
69         protoBuilder.addAllParentTypes(schema.getParentTypes());
70         return protoBuilder.build();
71     }
72 
73     @OptIn(markerClass = ExperimentalAppSearchApi.class)
toPropertyConfigProto( AppSearchSchema.@onNull PropertyConfig property)74     private static @NonNull PropertyConfigProto toPropertyConfigProto(
75             AppSearchSchema.@NonNull PropertyConfig property) {
76         Preconditions.checkNotNull(property);
77         PropertyConfigProto.Builder builder = PropertyConfigProto.newBuilder()
78                 .setPropertyName(property.getName())
79                 .setDescription(property.getDescription());
80 
81         // Set dataType
82         @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType();
83         PropertyConfigProto.DataType.Code dataTypeProto =
84                 PropertyConfigProto.DataType.Code.forNumber(dataType);
85         if (dataTypeProto == null) {
86             throw new IllegalArgumentException("Invalid dataType: " + dataType);
87         }
88         builder.setDataType(dataTypeProto);
89 
90         // Set cardinality
91         @AppSearchSchema.PropertyConfig.Cardinality int cardinality = property.getCardinality();
92         PropertyConfigProto.Cardinality.Code cardinalityProto =
93                 PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
94         if (cardinalityProto == null) {
95             throw new IllegalArgumentException("Invalid cardinality: " + dataType);
96         }
97         builder.setCardinality(cardinalityProto);
98 
99         if (property instanceof AppSearchSchema.StringPropertyConfig) {
100             AppSearchSchema.StringPropertyConfig stringProperty =
101                     (AppSearchSchema.StringPropertyConfig) property;
102             // No need to check against delete propagation type vs joinable value type here, because
103             // the builder has already enforced the restriction.
104 
105             // Set JoinableConfig only if it is joinable (i.e. joinableValueType is not NONE).
106             if (stringProperty.getJoinableValueType()
107                     != AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE) {
108                 JoinableConfig joinableConfig = JoinableConfig.newBuilder()
109                         .setValueType(
110                                 convertJoinableValueTypeToProto(
111                                         stringProperty.getJoinableValueType()))
112                         .setDeletePropagationType(
113                                 convertDeletePropagationTypeToProto(
114                                         stringProperty.getDeletePropagationType()))
115                         .build();
116                 builder.setJoinableConfig(joinableConfig);
117             }
118             StringIndexingConfig stringIndexingConfig = StringIndexingConfig.newBuilder()
119                     .setTermMatchType(convertTermMatchTypeToProto(stringProperty.getIndexingType()))
120                     .setTokenizerType(
121                             convertTokenizerTypeToProto(stringProperty.getTokenizerType()))
122                     .build();
123             builder.setStringIndexingConfig(stringIndexingConfig);
124         } else if (property instanceof AppSearchSchema.DocumentPropertyConfig) {
125             AppSearchSchema.DocumentPropertyConfig documentProperty =
126                     (AppSearchSchema.DocumentPropertyConfig) property;
127             builder
128                     .setSchemaType(documentProperty.getSchemaType())
129                     .setDocumentIndexingConfig(
130                             DocumentIndexingConfig.newBuilder()
131                                     .setIndexNestedProperties(
132                                             documentProperty.shouldIndexNestedProperties())
133                                     .addAllIndexableNestedPropertiesList(
134                                             documentProperty.getIndexableNestedProperties()));
135         } else if (property instanceof AppSearchSchema.LongPropertyConfig) {
136             AppSearchSchema.LongPropertyConfig longProperty =
137                     (AppSearchSchema.LongPropertyConfig) property;
138             // Set integer indexing config only if it is indexable (i.e. not INDEXING_TYPE_NONE).
139             if (longProperty.getIndexingType()
140                     != AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE) {
141                 IntegerIndexingConfig integerIndexingConfig = IntegerIndexingConfig.newBuilder()
142                         .setNumericMatchType(
143                                 convertNumericMatchTypeToProto(longProperty.getIndexingType()))
144                         .build();
145                 builder.setIntegerIndexingConfig(integerIndexingConfig);
146             }
147             builder.setScorableType(toScorableTypeCode(longProperty.isScoringEnabled()));
148         } else if (property instanceof AppSearchSchema.EmbeddingPropertyConfig) {
149             AppSearchSchema.EmbeddingPropertyConfig embeddingProperty =
150                     (AppSearchSchema.EmbeddingPropertyConfig) property;
151             // Set embedding indexing config only if it is indexable (i.e. not INDEXING_TYPE_NONE).
152             // Non-indexable embedding property only requires to builder.setDataType, without the
153             // need to set an EmbeddingIndexingConfig.
154             if (embeddingProperty.getIndexingType()
155                     != AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE) {
156                 EmbeddingIndexingConfig embeddingIndexingConfig =
157                         EmbeddingIndexingConfig.newBuilder()
158                                 .setEmbeddingIndexingType(
159                                         convertEmbeddingIndexingTypeToProto(
160                                                 embeddingProperty.getIndexingType()))
161                                 .setQuantizationType(
162                                         convertEmbeddingQuantizationTypeToProto(
163                                                 embeddingProperty.getQuantizationType()))
164                                 .build();
165                 builder.setEmbeddingIndexingConfig(embeddingIndexingConfig);
166             }
167         } else if (property instanceof AppSearchSchema.DoublePropertyConfig) {
168             AppSearchSchema.DoublePropertyConfig doubleProperty =
169                     (AppSearchSchema.DoublePropertyConfig) property;
170             builder.setScorableType(toScorableTypeCode(doubleProperty.isScoringEnabled()));
171         } else if (property instanceof AppSearchSchema.BooleanPropertyConfig) {
172             AppSearchSchema.BooleanPropertyConfig booleanProperty =
173                     (AppSearchSchema.BooleanPropertyConfig) property;
174             builder.setScorableType(toScorableTypeCode(booleanProperty.isScoringEnabled()));
175         }
176         return builder.build();
177     }
178 
179     /**
180      * Converts a {@link SchemaTypeConfigProto} into an
181      * {@link androidx.appsearch.app.AppSearchSchema}.
182      */
183     // TODO(b/284356266): Consider handling removal of schema name prefixes in this function.
184     @OptIn(markerClass = ExperimentalAppSearchApi.class)
toAppSearchSchema( @onNull SchemaTypeConfigProtoOrBuilder proto)185     public static @NonNull AppSearchSchema toAppSearchSchema(
186             @NonNull SchemaTypeConfigProtoOrBuilder proto) {
187         Preconditions.checkNotNull(proto);
188         AppSearchSchema.Builder builder =
189                 new AppSearchSchema.Builder(proto.getSchemaType());
190         builder.setDescription(proto.getDescription());
191         List<PropertyConfigProto> properties = proto.getPropertiesList();
192         for (int i = 0; i < properties.size(); i++) {
193             AppSearchSchema.PropertyConfig propertyConfig = toPropertyConfig(properties.get(i));
194             builder.addProperty(propertyConfig);
195         }
196         List<String> parentTypes = proto.getParentTypesList();
197         for (int i = 0; i < parentTypes.size(); i++) {
198             builder.addParentType(parentTypes.get(i));
199         }
200         return builder.build();
201     }
202 
203     @OptIn(markerClass = ExperimentalAppSearchApi.class)
toPropertyConfig( @onNull PropertyConfigProto proto)204     private static AppSearchSchema.@NonNull PropertyConfig toPropertyConfig(
205             @NonNull PropertyConfigProto proto) {
206         Preconditions.checkNotNull(proto);
207         switch (proto.getDataType()) {
208             case STRING:
209                 return toStringPropertyConfig(proto);
210             case INT64:
211                 return toLongPropertyConfig(proto);
212             case DOUBLE:
213                 return new AppSearchSchema.DoublePropertyConfig.Builder(proto.getPropertyName())
214                         .setDescription(proto.getDescription())
215                         .setCardinality(proto.getCardinality().getNumber())
216                         .setScoringEnabled(
217                                 proto.getScorableType() ==
218                                         PropertyConfigProto.ScorableType.Code.ENABLED)
219                         .build();
220             case BOOLEAN:
221                 return new AppSearchSchema.BooleanPropertyConfig.Builder(proto.getPropertyName())
222                         .setDescription(proto.getDescription())
223                         .setCardinality(proto.getCardinality().getNumber())
224                         .setScoringEnabled(
225                                 proto.getScorableType() ==
226                                         PropertyConfigProto.ScorableType.Code.ENABLED)
227                         .build();
228             case BYTES:
229                 return new AppSearchSchema.BytesPropertyConfig.Builder(proto.getPropertyName())
230                         .setDescription(proto.getDescription())
231                         .setCardinality(proto.getCardinality().getNumber())
232                         .build();
233             case DOCUMENT:
234                 return toDocumentPropertyConfig(proto);
235             case VECTOR:
236                 return toEmbeddingPropertyConfig(proto);
237             case BLOB_HANDLE:
238                 return new AppSearchSchema.BlobHandlePropertyConfig.Builder(proto.getPropertyName())
239                         .setDescription(proto.getDescription())
240                         .setCardinality(proto.getCardinality().getNumber())
241                         .build();
242             default:
243                 throw new IllegalArgumentException(
244                         "Invalid dataType code: " + proto.getDataType().getNumber());
245         }
246     }
247 
248     @OptIn(markerClass = ExperimentalAppSearchApi.class)
toStringPropertyConfig( @onNull PropertyConfigProto proto)249     private static AppSearchSchema.@NonNull StringPropertyConfig toStringPropertyConfig(
250             @NonNull PropertyConfigProto proto) {
251         AppSearchSchema.StringPropertyConfig.Builder builder =
252                 new AppSearchSchema.StringPropertyConfig.Builder(proto.getPropertyName())
253                         .setDescription(proto.getDescription())
254                         .setCardinality(proto.getCardinality().getNumber())
255                         .setJoinableValueType(
256                                 convertJoinableValueTypeFromProto(
257                                         proto.getJoinableConfig().getValueType()))
258                         .setDeletePropagationType(
259                                 convertDeletePropagationTypeFromProto(
260                                         proto.getJoinableConfig().getDeletePropagationType()))
261                         .setTokenizerType(
262                                 proto.getStringIndexingConfig().getTokenizerType().getNumber());
263 
264         // Set indexingType
265         TermMatchType.Code termMatchTypeProto = proto.getStringIndexingConfig().getTermMatchType();
266         builder.setIndexingType(convertTermMatchTypeFromProto(termMatchTypeProto));
267 
268         return builder.build();
269     }
270 
271     @OptIn(markerClass = ExperimentalAppSearchApi.class)
toDocumentPropertyConfig( @onNull PropertyConfigProto proto)272     private static AppSearchSchema.@NonNull DocumentPropertyConfig toDocumentPropertyConfig(
273             @NonNull PropertyConfigProto proto) {
274         AppSearchSchema.DocumentPropertyConfig.Builder builder =
275                 new AppSearchSchema.DocumentPropertyConfig.Builder(
276                                 proto.getPropertyName(), proto.getSchemaType())
277                         .setDescription(proto.getDescription())
278                         .setCardinality(proto.getCardinality().getNumber())
279                         .setShouldIndexNestedProperties(
280                                 proto.getDocumentIndexingConfig().getIndexNestedProperties());
281         builder.addIndexableNestedProperties(
282                 proto.getDocumentIndexingConfig().getIndexableNestedPropertiesListList());
283         return builder.build();
284     }
285 
286     @OptIn(markerClass = ExperimentalAppSearchApi.class)
toLongPropertyConfig( @onNull PropertyConfigProto proto)287     private static AppSearchSchema.@NonNull LongPropertyConfig toLongPropertyConfig(
288             @NonNull PropertyConfigProto proto) {
289         AppSearchSchema.LongPropertyConfig.Builder builder =
290                 new AppSearchSchema.LongPropertyConfig.Builder(proto.getPropertyName())
291                         .setDescription(proto.getDescription())
292                         .setCardinality(proto.getCardinality().getNumber())
293                         .setScoringEnabled(
294                                 proto.getScorableType() ==
295                                         PropertyConfigProto.ScorableType.Code.ENABLED);
296         // Set indexingType
297         IntegerIndexingConfig.NumericMatchType.Code numericMatchTypeProto =
298                 proto.getIntegerIndexingConfig().getNumericMatchType();
299         builder.setIndexingType(convertNumericMatchTypeFromProto(numericMatchTypeProto));
300 
301         return builder.build();
302     }
303 
304     @OptIn(markerClass = ExperimentalAppSearchApi.class)
toEmbeddingPropertyConfig( @onNull PropertyConfigProto proto)305     private static AppSearchSchema.@NonNull EmbeddingPropertyConfig toEmbeddingPropertyConfig(
306             @NonNull PropertyConfigProto proto) {
307         AppSearchSchema.EmbeddingPropertyConfig.Builder builder =
308                 new AppSearchSchema.EmbeddingPropertyConfig.Builder(proto.getPropertyName())
309                         .setDescription(proto.getDescription())
310                         .setCardinality(proto.getCardinality().getNumber());
311 
312         // Set indexingType
313         EmbeddingIndexingConfig.EmbeddingIndexingType.Code embeddingIndexingType =
314                 proto.getEmbeddingIndexingConfig().getEmbeddingIndexingType();
315         builder.setIndexingType(convertEmbeddingIndexingTypeFromProto(embeddingIndexingType));
316 
317         // Set quantizationType
318         if (embeddingIndexingType != EmbeddingIndexingConfig.EmbeddingIndexingType.Code.UNKNOWN) {
319             EmbeddingIndexingConfig.QuantizationType.Code embeddingQuantizationType =
320                     proto.getEmbeddingIndexingConfig().getQuantizationType();
321             builder.setQuantizationType(
322                     convertEmbeddingQuantizationTypeTypeFromProto(embeddingQuantizationType));
323         }
324 
325         return builder.build();
326     }
327 
convertJoinableValueTypeToProto( @ppSearchSchema.StringPropertyConfig.JoinableValueType int joinableValueType)328     private static JoinableConfig.ValueType.@NonNull Code convertJoinableValueTypeToProto(
329             @AppSearchSchema.StringPropertyConfig.JoinableValueType int joinableValueType) {
330         switch (joinableValueType) {
331             case AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE:
332                 return JoinableConfig.ValueType.Code.NONE;
333             case AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID:
334                 return JoinableConfig.ValueType.Code.QUALIFIED_ID;
335             default:
336                 throw new IllegalArgumentException(
337                         "Invalid joinableValueType: " + joinableValueType);
338         }
339     }
340 
341     @AppSearchSchema.StringPropertyConfig.JoinableValueType
convertJoinableValueTypeFromProto( JoinableConfig.ValueType.@onNull Code joinableValueType)342     private static int convertJoinableValueTypeFromProto(
343             JoinableConfig.ValueType.@NonNull Code joinableValueType) {
344         switch (joinableValueType) {
345             case NONE:
346                 return AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
347             case QUALIFIED_ID:
348                 return AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID;
349         }
350         // Avoid crashing in the 'read' path; we should try to interpret the document to the
351         // extent possible.
352         Log.w(TAG, "Invalid joinableValueType: " + joinableValueType.getNumber());
353         return AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
354     }
355 
356     @OptIn(markerClass = ExperimentalAppSearchApi.class)
357     private static JoinableConfig.DeletePropagationType.@NonNull Code
convertDeletePropagationTypeToProto( @ppSearchSchema.StringPropertyConfig.DeletePropagationType int deletePropagationType)358             convertDeletePropagationTypeToProto(
359                     @AppSearchSchema.StringPropertyConfig.DeletePropagationType
360                     int deletePropagationType) {
361         switch (deletePropagationType) {
362             case AppSearchSchema.StringPropertyConfig.DELETE_PROPAGATION_TYPE_NONE:
363                 return JoinableConfig.DeletePropagationType.Code.NONE;
364             case AppSearchSchema.StringPropertyConfig.DELETE_PROPAGATION_TYPE_PROPAGATE_FROM:
365                 return JoinableConfig.DeletePropagationType.Code.PROPAGATE_FROM;
366             default:
367                 throw new IllegalArgumentException(
368                         "Invalid deletePropagationType: " + deletePropagationType);
369         }
370     }
371 
372     @AppSearchSchema.StringPropertyConfig.DeletePropagationType
373     @OptIn(markerClass = ExperimentalAppSearchApi.class)
convertDeletePropagationTypeFromProto( JoinableConfig.DeletePropagationType.@onNull Code deletePropagationType)374     private static int convertDeletePropagationTypeFromProto(
375             JoinableConfig.DeletePropagationType.@NonNull Code deletePropagationType) {
376         switch (deletePropagationType) {
377             case NONE:
378                 return AppSearchSchema.StringPropertyConfig.DELETE_PROPAGATION_TYPE_NONE;
379             case PROPAGATE_FROM:
380                 return AppSearchSchema.StringPropertyConfig.DELETE_PROPAGATION_TYPE_PROPAGATE_FROM;
381         }
382         // Avoid crashing in the 'read' path; we should try to interpret the schema to the
383         // extent possible.
384         Log.w(TAG, "Invalid deletePropagationType: " + deletePropagationType.getNumber());
385         return AppSearchSchema.StringPropertyConfig.DELETE_PROPAGATION_TYPE_NONE;
386     }
387 
convertTermMatchTypeToProto( @ppSearchSchema.StringPropertyConfig.IndexingType int indexingType)388     private static TermMatchType.@NonNull Code convertTermMatchTypeToProto(
389             @AppSearchSchema.StringPropertyConfig.IndexingType int indexingType) {
390         switch (indexingType) {
391             case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE:
392                 return TermMatchType.Code.UNKNOWN;
393             case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS:
394                 return TermMatchType.Code.EXACT_ONLY;
395             case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES:
396                 return TermMatchType.Code.PREFIX;
397             default:
398                 throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
399         }
400     }
401 
402     @AppSearchSchema.StringPropertyConfig.IndexingType
convertTermMatchTypeFromProto(TermMatchType.@onNull Code termMatchType)403     private static int convertTermMatchTypeFromProto(TermMatchType.@NonNull Code termMatchType) {
404         switch (termMatchType) {
405             case UNKNOWN:
406                 return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
407             case EXACT_ONLY:
408                 return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS;
409             case PREFIX:
410                 return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES;
411             default:
412                 // Avoid crashing in the 'read' path; we should try to interpret the document to the
413                 // extent possible.
414                 Log.w(TAG, "Invalid indexingType: " + termMatchType.getNumber());
415                 return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
416         }
417     }
418 
convertTokenizerTypeToProto( @ppSearchSchema.StringPropertyConfig.TokenizerType int tokenizerType)419     private static StringIndexingConfig.TokenizerType.@NonNull Code convertTokenizerTypeToProto(
420             @AppSearchSchema.StringPropertyConfig.TokenizerType int tokenizerType) {
421         StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
422                 StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
423         if (tokenizerTypeProto == null) {
424             throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
425         }
426         return tokenizerTypeProto;
427     }
428 
429     private static IntegerIndexingConfig.NumericMatchType.@NonNull Code
convertNumericMatchTypeToProto( @ppSearchSchema.LongPropertyConfig.IndexingType int indexingType)430             convertNumericMatchTypeToProto(
431                     @AppSearchSchema.LongPropertyConfig.IndexingType int indexingType) {
432         switch (indexingType) {
433             case AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE:
434                 return IntegerIndexingConfig.NumericMatchType.Code.UNKNOWN;
435             case AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE:
436                 return IntegerIndexingConfig.NumericMatchType.Code.RANGE;
437             default:
438                 throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
439         }
440     }
441 
442     @AppSearchSchema.LongPropertyConfig.IndexingType
convertNumericMatchTypeFromProto( IntegerIndexingConfig.NumericMatchType.@onNull Code numericMatchType)443     private static int convertNumericMatchTypeFromProto(
444             IntegerIndexingConfig.NumericMatchType.@NonNull Code numericMatchType) {
445         switch (numericMatchType) {
446             case UNKNOWN:
447                 return AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE;
448             case RANGE:
449                 return AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE;
450             default:
451                 // Avoid crashing in the 'read' path; we should try to interpret the document to the
452                 // extent possible.
453                 Log.w(TAG, "Invalid indexingType: " + numericMatchType.getNumber());
454                 return AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE;
455         }
456     }
457 
458     private static EmbeddingIndexingConfig.EmbeddingIndexingType.@NonNull Code
convertEmbeddingIndexingTypeToProto( @ppSearchSchema.EmbeddingPropertyConfig.IndexingType int indexingType)459             convertEmbeddingIndexingTypeToProto(
460             @AppSearchSchema.EmbeddingPropertyConfig.IndexingType int indexingType) {
461         switch (indexingType) {
462             case AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE:
463                 return EmbeddingIndexingConfig.EmbeddingIndexingType.Code.UNKNOWN;
464             case AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY:
465                 return EmbeddingIndexingConfig.EmbeddingIndexingType.Code.LINEAR_SEARCH;
466             default:
467                 throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
468         }
469     }
470 
471     @AppSearchSchema.EmbeddingPropertyConfig.IndexingType
convertEmbeddingIndexingTypeFromProto( EmbeddingIndexingConfig.EmbeddingIndexingType.@onNull Code indexingType)472     private static int convertEmbeddingIndexingTypeFromProto(
473             EmbeddingIndexingConfig.EmbeddingIndexingType.@NonNull Code indexingType) {
474         switch (indexingType) {
475             case UNKNOWN:
476                 return AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE;
477             case LINEAR_SEARCH:
478                 return AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY;
479             default:
480                 // Avoid crashing in the 'read' path; we should try to interpret the document to the
481                 // extent possible.
482                 Log.w(TAG, "Invalid indexingType: " + indexingType.getNumber());
483                 return AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_NONE;
484         }
485     }
486 
487     @OptIn(markerClass = ExperimentalAppSearchApi.class)
488     private static EmbeddingIndexingConfig.QuantizationType.@NonNull Code
convertEmbeddingQuantizationTypeToProto( @ppSearchSchema.EmbeddingPropertyConfig.QuantizationType int quantizationType)489             convertEmbeddingQuantizationTypeToProto(
490             @AppSearchSchema.EmbeddingPropertyConfig.QuantizationType int quantizationType) {
491         switch (quantizationType) {
492             case AppSearchSchema.EmbeddingPropertyConfig.QUANTIZATION_TYPE_NONE:
493                 return EmbeddingIndexingConfig.QuantizationType.Code.NONE;
494             case AppSearchSchema.EmbeddingPropertyConfig.QUANTIZATION_TYPE_8_BIT:
495                 return EmbeddingIndexingConfig.QuantizationType.Code.QUANTIZE_8_BIT;
496             default:
497                 throw new IllegalArgumentException("Invalid quantizationType: " + quantizationType);
498         }
499     }
500 
501     @AppSearchSchema.EmbeddingPropertyConfig.QuantizationType
502     @OptIn(markerClass = ExperimentalAppSearchApi.class)
convertEmbeddingQuantizationTypeTypeFromProto( EmbeddingIndexingConfig.QuantizationType.@onNull Code quantizationType)503     private static int convertEmbeddingQuantizationTypeTypeFromProto(
504             EmbeddingIndexingConfig.QuantizationType.@NonNull Code quantizationType) {
505         switch (quantizationType) {
506             case NONE:
507                 return AppSearchSchema.EmbeddingPropertyConfig.QUANTIZATION_TYPE_NONE;
508             case QUANTIZE_8_BIT:
509                 return AppSearchSchema.EmbeddingPropertyConfig.QUANTIZATION_TYPE_8_BIT;
510             default:
511                 // Avoid crashing in the 'read' path; we should try to interpret the document to the
512                 // extent possible.
513                 Log.w(TAG, "Invalid quantizationType: " + quantizationType.getNumber());
514                 return AppSearchSchema.EmbeddingPropertyConfig.QUANTIZATION_TYPE_NONE;
515         }
516     }
517 
toScorableTypeCode( boolean isScoringEnabled)518     private static PropertyConfigProto.ScorableType.Code toScorableTypeCode(
519             boolean isScoringEnabled) {
520         if (isScoringEnabled) {
521             return PropertyConfigProto.ScorableType.Code.ENABLED;
522         } else {
523             return PropertyConfigProto.ScorableType.Code.DISABLED;
524         }
525     }
526 }
527