Merge "Remove IndexingConfig and merge its constants into PropertyConfig."

This commit is contained in:
Alexander Dorokhine
2020-01-18 03:42:02 +00:00
committed by Android (Google) Code Review
3 changed files with 266 additions and 303 deletions

View File

@@ -74,10 +74,10 @@ public class AppSearchManager {
* <li>Adding a 'required' property
* </ul>
*
* @param schema The schema config for this app.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive errors resulting from setting the schema. If the
* operation succeeds, the callback will be invoked with {@code null}.
* @param schemas The schema configs for the types used by the calling app.
*
* @hide
*/
@@ -87,11 +87,19 @@ public class AppSearchManager {
// TODO(b/145635424): Update the documentation above once the Schema mutation APIs of Icing
// Library are finalized
public void setSchema(
@NonNull AppSearchSchema schema,
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<? super Throwable> callback) {
SchemaProto schemaProto = schema.getProto();
byte[] schemaBytes = schemaProto.toByteArray();
@NonNull Consumer<? super Throwable> callback,
@NonNull AppSearchSchema... schemas) {
// Prepare the merged schema for transmission.
SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder();
for (AppSearchSchema schema : schemas) {
schemaProtoBuilder.addTypes(schema.getProto());
}
// Serialize and send the schema.
// TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to
// avoid binder limits.
byte[] schemaBytes = schemaProtoBuilder.build().toByteArray();
AndroidFuture<Void> future = new AndroidFuture<>();
try {
mService.setSchema(schemaBytes, future);

View File

@@ -18,41 +18,38 @@ package android.app.appsearch;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.google.android.icing.proto.PropertyConfigProto;
import com.google.android.icing.proto.SchemaProto;
import com.google.android.icing.proto.SchemaTypeConfigProto;
import com.google.android.icing.proto.TermMatchType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;
/**
* Representation of the AppSearch Schema.
* The AppSearch Schema for a particular type of document.
*
* <p>The schema is the set of document types, properties, and config (like tokenization type)
* understood by AppSearch for this app.
* <p>For example, an e-mail message or a music recording could be a schema type.
*
* <p>The schema consists of type information, properties, and config (like tokenization type).
*
* @hide
*/
public final class AppSearchSchema {
private final SchemaProto mProto;
private final SchemaTypeConfigProto mProto;
private AppSearchSchema(SchemaProto proto) {
private AppSearchSchema(SchemaTypeConfigProto proto) {
mProto = proto;
}
/** Creates a new {@link AppSearchSchema.Builder}. */
@NonNull
public static AppSearchSchema.Builder newBuilder() {
return new AppSearchSchema.Builder();
}
/** Creates a new {@link SchemaType.Builder}. */
@NonNull
public static SchemaType.Builder newSchemaTypeBuilder(@NonNull String typeName) {
return new SchemaType.Builder(typeName);
public static AppSearchSchema.Builder newBuilder(@NonNull String typeName) {
return new AppSearchSchema.Builder(typeName);
}
/** Creates a new {@link PropertyConfig.Builder}. */
@@ -61,32 +58,34 @@ public final class AppSearchSchema {
return new PropertyConfig.Builder(propertyName);
}
/** Creates a new {@link IndexingConfig.Builder}. */
@NonNull
public static IndexingConfig.Builder newIndexingConfigBuilder() {
return new IndexingConfig.Builder();
}
/**
* Returns the schema proto populated by the {@link AppSearchSchema} builders.
* Returns the {@link SchemaTypeConfigProto} populated by this builder.
* @hide
*/
@NonNull
@VisibleForTesting
public SchemaProto getProto() {
public SchemaTypeConfigProto getProto() {
return mProto;
}
@Override
public String toString() {
return mProto.toString();
}
/** Builder for {@link AppSearchSchema objects}. */
public static final class Builder {
private final SchemaProto.Builder mProtoBuilder = SchemaProto.newBuilder();
private final SchemaTypeConfigProto.Builder mProtoBuilder =
SchemaTypeConfigProto.newBuilder();
private Builder() {}
private Builder(@NonNull String typeName) {
mProtoBuilder.setSchemaType(typeName);
}
/** Adds a supported type to this app's AppSearch schema. */
/** Adds a property to the given type. */
@NonNull
public AppSearchSchema.Builder addType(@NonNull SchemaType schemaType) {
mProtoBuilder.addTypes(schemaType.mProto);
public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
mProtoBuilder.addProperties(propertyConfig.mProto);
return this;
}
@@ -97,50 +96,18 @@ public final class AppSearchSchema {
*/
@NonNull
public AppSearchSchema build() {
Set<String> propertyNames = new ArraySet<>();
for (PropertyConfigProto propertyConfigProto : mProtoBuilder.getPropertiesList()) {
if (!propertyNames.add(propertyConfigProto.getPropertyName())) {
throw new IllegalSchemaException(
"Property defined more than once: "
+ propertyConfigProto.getPropertyName());
}
}
return new AppSearchSchema(mProtoBuilder.build());
}
}
/**
* Represents a type of a document.
*
* <p>For example, an e-mail message or a music recording could be a schema type.
*/
public static final class SchemaType {
private final SchemaTypeConfigProto mProto;
private SchemaType(SchemaTypeConfigProto proto) {
mProto = proto;
}
/** Builder for {@link SchemaType} objects. */
public static final class Builder {
private final SchemaTypeConfigProto.Builder mProtoBuilder =
SchemaTypeConfigProto.newBuilder();
private Builder(@NonNull String typeName) {
mProtoBuilder.setSchemaType(typeName);
}
/** Adds a property to the given type. */
@NonNull
public SchemaType.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
mProtoBuilder.addProperties(propertyConfig.mProto);
return this;
}
/**
* Constructs a new {@link SchemaType} from the contents of this builder.
*
* <p>After calling this method, the builder must no longer be used.
*/
@NonNull
public SchemaType build() {
return new SchemaType(mProtoBuilder.build());
}
}
}
/**
* Configuration for a single property (field) of a document type.
*
@@ -197,130 +164,14 @@ public final class AppSearchSchema {
/** Exactly one value [1]. */
public static final int CARDINALITY_REQUIRED = 3;
private final PropertyConfigProto mProto;
private PropertyConfig(PropertyConfigProto proto) {
mProto = proto;
}
/**
* Builder for {@link PropertyConfig}.
*
* <p>The following properties must be set, or {@link PropertyConfig} construction will
* fail:
* <ul>
* <li>dataType
* <li>cardinality
* </ul>
*
* <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType}
* is also required.
*/
public static final class Builder {
private final PropertyConfigProto.Builder mProtoBuilder =
PropertyConfigProto.newBuilder();
private Builder(String propertyName) {
mProtoBuilder.setPropertyName(propertyName);
}
/**
* Type of data the property contains (e.g. string, int, bytes, etc).
*
* <p>This property must be set.
*/
@NonNull
public PropertyConfig.Builder setDataType(@DataType int dataType) {
PropertyConfigProto.DataType.Code dataTypeProto =
PropertyConfigProto.DataType.Code.forNumber(dataType);
if (dataTypeProto == null) {
throw new IllegalArgumentException("Invalid dataType: " + dataType);
}
mProtoBuilder.setDataType(dataTypeProto);
return this;
}
/**
* The logical schema-type of the contents of this property.
*
* <p>Only required when {@link #setDataType(int)} is set to
* {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored.
*/
@NonNull
public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
mProtoBuilder.setSchemaType(schemaType);
return this;
}
/**
* The cardinality of the property (whether it is optional, required or repeated).
*
* <p>This property must be set.
*/
@NonNull
public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
PropertyConfigProto.Cardinality.Code cardinalityProto =
PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
if (cardinalityProto == null) {
throw new IllegalArgumentException("Invalid cardinality: " + cardinality);
}
mProtoBuilder.setCardinality(cardinalityProto);
return this;
}
/**
* Configures how this property should be indexed.
*
* <p>If this is not supplied, the property will not be indexed at all.
*/
@NonNull
public PropertyConfig.Builder setIndexingConfig(
@NonNull IndexingConfig indexingConfig) {
mProtoBuilder.setIndexingConfig(indexingConfig.mProto);
return this;
}
/**
* Constructs a new {@link PropertyConfig} from the contents of this builder.
*
* <p>After calling this method, the builder must no longer be used.
*
* @throws IllegalSchemaException If the property is not correctly populated (e.g.
* missing {@code dataType}).
*/
@NonNull
public PropertyConfig build() {
if (mProtoBuilder.getDataType() == PropertyConfigProto.DataType.Code.UNKNOWN) {
throw new IllegalSchemaException("Missing field: dataType");
}
if (mProtoBuilder.getSchemaType().isEmpty()
&& mProtoBuilder.getDataType()
== PropertyConfigProto.DataType.Code.DOCUMENT) {
throw new IllegalSchemaException(
"Missing field: schemaType (required for configs with "
+ "dataType = DOCUMENT)");
}
if (mProtoBuilder.getCardinality()
== PropertyConfigProto.Cardinality.Code.UNKNOWN) {
throw new IllegalSchemaException("Missing field: cardinality");
}
return new PropertyConfig(mProtoBuilder.build());
}
}
}
/** Configures how a property should be indexed so that it can be retrieved by queries. */
public static final class IndexingConfig {
/** Encapsulates the configurations on how AppSearch should query/index these terms. */
// NOTE: The integer values of these constants must match the proto enum constants in
// com.google.android.icing.proto.TermMatchType.Code.
@IntDef(prefix = {"TERM_MATCH_TYPE_"}, value = {
TERM_MATCH_TYPE_UNKNOWN,
TERM_MATCH_TYPE_EXACT_ONLY,
TERM_MATCH_TYPE_PREFIX,
@IntDef(prefix = {"INDEXING_TYPE_"}, value = {
INDEXING_TYPE_NONE,
INDEXING_TYPE_EXACT_TERMS,
INDEXING_TYPE_PREFIXES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface TermMatchType {}
public @interface IndexingType {}
/**
* Content in this property will not be tokenized or indexed.
@@ -328,9 +179,9 @@ public final class AppSearchSchema {
* <p>Useful if the data type is not made up of terms (e.g.
* {@link PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES}
* type). All the properties inside the nested property won't be indexed regardless of the
* value of {@code termMatchType} for the nested properties.
* value of {@code indexingType} for the nested properties.
*/
public static final int TERM_MATCH_TYPE_UNKNOWN = 0;
public static final int INDEXING_TYPE_NONE = 0;
/**
* Content in this property should only be returned for queries matching the exact tokens
@@ -338,7 +189,7 @@ public final class AppSearchSchema {
*
* <p>Ex. A property with "fool" should NOT match a query for "foo".
*/
public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1;
public static final int INDEXING_TYPE_EXACT_TERMS = 1;
/**
* Content in this property should be returned for queries that are either exact matches or
@@ -346,7 +197,7 @@ public final class AppSearchSchema {
*
* <p>Ex. A property with "fool" <b>should</b> match a query for "foo".
*/
public static final int TERM_MATCH_TYPE_PREFIX = 2;
public static final int INDEXING_TYPE_PREFIXES = 2;
/** Configures how tokens should be extracted from this property. */
// NOTE: The integer values of these constants must match the proto enum constants in
@@ -367,59 +218,151 @@ public final class AppSearchSchema {
/** Tokenization for plain text. */
public static final int TOKENIZER_TYPE_PLAIN = 1;
private final com.google.android.icing.proto.IndexingConfig mProto;
private final PropertyConfigProto mProto;
private IndexingConfig(com.google.android.icing.proto.IndexingConfig proto) {
private PropertyConfig(PropertyConfigProto proto) {
mProto = proto;
}
@Override
public String toString() {
return mProto.toString();
}
/**
* Builder for {@link IndexingConfig} objects.
* Builder for {@link PropertyConfig}.
*
* <p>You may skip adding an {@link IndexingConfig} for a property, which is equivalent to
* an {@link IndexingConfig} having {@code termMatchType} equal to
* {@link #TERM_MATCH_TYPE_UNKNOWN}. In this case the property will not be indexed.
* <p>The following properties must be set, or {@link PropertyConfig} construction will
* fail:
* <ul>
* <li>dataType
* <li>cardinality
* </ul>
*
* <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType}
* is also required.
*/
public static final class Builder {
private final com.google.android.icing.proto.IndexingConfig.Builder mProtoBuilder =
com.google.android.icing.proto.IndexingConfig.newBuilder();
private final PropertyConfigProto.Builder mPropertyConfigProto =
PropertyConfigProto.newBuilder();
private final com.google.android.icing.proto.IndexingConfig.Builder
mIndexingConfigProto =
com.google.android.icing.proto.IndexingConfig.newBuilder();
private Builder() {}
private Builder(String propertyName) {
mPropertyConfigProto.setPropertyName(propertyName);
}
/** Configures how the content of this property should be matched in the index. */
/**
* Type of data the property contains (e.g. string, int, bytes, etc).
*
* <p>This property must be set.
*/
@NonNull
public IndexingConfig.Builder setTermMatchType(@TermMatchType int termMatchType) {
com.google.android.icing.proto.TermMatchType.Code termMatchTypeProto =
com.google.android.icing.proto.TermMatchType.Code.forNumber(termMatchType);
if (termMatchTypeProto == null) {
throw new IllegalArgumentException("Invalid termMatchType: " + termMatchType);
public PropertyConfig.Builder setDataType(@DataType int dataType) {
PropertyConfigProto.DataType.Code dataTypeProto =
PropertyConfigProto.DataType.Code.forNumber(dataType);
if (dataTypeProto == null) {
throw new IllegalArgumentException("Invalid dataType: " + dataType);
}
mProtoBuilder.setTermMatchType(termMatchTypeProto);
mPropertyConfigProto.setDataType(dataTypeProto);
return this;
}
/**
* The logical schema-type of the contents of this property.
*
* <p>Only required when {@link #setDataType(int)} is set to
* {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored.
*/
@NonNull
public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
mPropertyConfigProto.setSchemaType(schemaType);
return this;
}
/**
* The cardinality of the property (whether it is optional, required or repeated).
*
* <p>This property must be set.
*/
@NonNull
public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
PropertyConfigProto.Cardinality.Code cardinalityProto =
PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
if (cardinalityProto == null) {
throw new IllegalArgumentException("Invalid cardinality: " + cardinality);
}
mPropertyConfigProto.setCardinality(cardinalityProto);
return this;
}
/**
* Configures how a property should be indexed so that it can be retrieved by queries.
*/
@NonNull
public PropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
TermMatchType.Code termMatchTypeProto;
switch (indexingType) {
case INDEXING_TYPE_NONE:
termMatchTypeProto = TermMatchType.Code.UNKNOWN;
break;
case INDEXING_TYPE_EXACT_TERMS:
termMatchTypeProto = TermMatchType.Code.EXACT_ONLY;
break;
case INDEXING_TYPE_PREFIXES:
termMatchTypeProto = TermMatchType.Code.PREFIX;
break;
default:
throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
}
mIndexingConfigProto.setTermMatchType(termMatchTypeProto);
return this;
}
/** Configures how this property should be tokenized (split into words). */
@NonNull
public IndexingConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
public PropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
com.google.android.icing.proto.IndexingConfig.TokenizerType.Code
tokenizerTypeProto =
com.google.android.icing.proto.IndexingConfig
.TokenizerType.Code.forNumber(tokenizerType);
.TokenizerType.Code.forNumber(tokenizerType);
if (tokenizerTypeProto == null) {
throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
}
mProtoBuilder.setTokenizerType(tokenizerTypeProto);
mIndexingConfigProto.setTokenizerType(tokenizerTypeProto);
return this;
}
/**
* Constructs a new {@link IndexingConfig} from the contents of this builder.
* Constructs a new {@link PropertyConfig} from the contents of this builder.
*
* <p>After calling this method, the builder must no longer be used.
*
* @throws IllegalSchemaException If the property is not correctly populated (e.g.
* missing {@code dataType}).
*/
@NonNull
public IndexingConfig build() {
return new IndexingConfig(mProtoBuilder.build());
public PropertyConfig build() {
mPropertyConfigProto.setIndexingConfig(mIndexingConfigProto);
// TODO(b/147692920): Send the schema to Icing Lib for official validation, instead
// of partially reimplementing some of the validation Icing does here.
if (mPropertyConfigProto.getDataType()
== PropertyConfigProto.DataType.Code.UNKNOWN) {
throw new IllegalSchemaException("Missing field: dataType");
}
if (mPropertyConfigProto.getSchemaType().isEmpty()
&& mPropertyConfigProto.getDataType()
== PropertyConfigProto.DataType.Code.DOCUMENT) {
throw new IllegalSchemaException(
"Missing field: schemaType (required for configs with "
+ "dataType = DOCUMENT)");
}
if (mPropertyConfigProto.getCardinality()
== PropertyConfigProto.Cardinality.Code.UNKNOWN) {
throw new IllegalSchemaException("Missing field: cardinality");
}
return new PropertyConfig(mPropertyConfigProto.build());
}
}
}

View File

@@ -21,14 +21,12 @@ import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.expectThrows;
import android.app.appsearch.AppSearchSchema.IndexingConfig;
import android.app.appsearch.AppSearchSchema.PropertyConfig;
import androidx.test.filters.SmallTest;
import com.google.android.icing.proto.IndexingConfig.TokenizerType;
import com.google.android.icing.proto.PropertyConfigProto;
import com.google.android.icing.proto.SchemaProto;
import com.google.android.icing.proto.SchemaTypeConfigProto;
import com.google.android.icing.proto.TermMatchType;
@@ -37,94 +35,87 @@ import org.junit.Test;
@SmallTest
public class AppSearchSchemaTest {
@Test
public void testSuccess() {
AppSearchSchema schema = AppSearchSchema.newBuilder()
.addType(AppSearchSchema.newSchemaTypeBuilder("Email")
.addProperty(AppSearchSchema.newPropertyBuilder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder()
.setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN)
.setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX)
.build()
).build()
).addProperty(AppSearchSchema.newPropertyBuilder("body")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder()
.setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN)
.setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX)
.build()
).build()
).build()
).addType(AppSearchSchema.newSchemaTypeBuilder("MusicRecording")
.addProperty(AppSearchSchema.newPropertyBuilder("artist")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
.setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder()
.setTokenizerType(IndexingConfig.TOKENIZER_TYPE_PLAIN)
.setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_PREFIX)
.build()
).build()
).addProperty(AppSearchSchema.newPropertyBuilder("pubDate")
.setDataType(PropertyConfig.DATA_TYPE_INT64)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingConfig(AppSearchSchema.newIndexingConfigBuilder()
.setTokenizerType(IndexingConfig.TOKENIZER_TYPE_NONE)
.setTermMatchType(IndexingConfig.TERM_MATCH_TYPE_UNKNOWN)
.build()
).build()
).build()
public void testGetProto_Email() {
AppSearchSchema emailSchema = AppSearchSchema.newBuilder("Email")
.addProperty(AppSearchSchema.newPropertyBuilder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build()
).addProperty(AppSearchSchema.newPropertyBuilder("body")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build()
).build();
SchemaProto expectedProto = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("Email")
.addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setIndexingConfig(
com.google.android.icing.proto.IndexingConfig.newBuilder()
.setTokenizerType(TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
).addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("body")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setIndexingConfig(
com.google.android.icing.proto.IndexingConfig.newBuilder()
.setTokenizerType(TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder()
.setSchemaType("Email")
.addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setIndexingConfig(
com.google.android.icing.proto.IndexingConfig.newBuilder()
.setTokenizerType(TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
).addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("MusicRecording")
.addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("artist")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
.setIndexingConfig(
com.google.android.icing.proto.IndexingConfig.newBuilder()
.setTokenizerType(TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
).addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("pubDate")
.setDataType(PropertyConfigProto.DataType.Code.INT64)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setIndexingConfig(
com.google.android.icing.proto.IndexingConfig.newBuilder()
.setTokenizerType(TokenizerType.Code.NONE)
.setTermMatchType(TermMatchType.Code.UNKNOWN)
)
).addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("body")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setIndexingConfig(
com.google.android.icing.proto.IndexingConfig.newBuilder()
.setTokenizerType(TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
).build();
assertThat(schema.getProto()).isEqualTo(expectedProto);
assertThat(emailSchema.getProto()).isEqualTo(expectedEmailProto);
}
@Test
public void testGetProto_MusicRecording() {
AppSearchSchema musicRecordingSchema = AppSearchSchema.newBuilder("MusicRecording")
.addProperty(AppSearchSchema.newPropertyBuilder("artist")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build()
).addProperty(AppSearchSchema.newPropertyBuilder("pubDate")
.setDataType(PropertyConfig.DATA_TYPE_INT64)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_NONE)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_NONE)
.build()
).build();
SchemaTypeConfigProto expectedMusicRecordingProto = SchemaTypeConfigProto.newBuilder()
.setSchemaType("MusicRecording")
.addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("artist")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
.setIndexingConfig(
com.google.android.icing.proto.IndexingConfig.newBuilder()
.setTokenizerType(TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
).addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("pubDate")
.setDataType(PropertyConfigProto.DataType.Code.INT64)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setIndexingConfig(
com.google.android.icing.proto.IndexingConfig.newBuilder()
.setTokenizerType(TokenizerType.Code.NONE)
.setTermMatchType(TermMatchType.Code.UNKNOWN)
)
).build();
assertThat(musicRecordingSchema.getProto()).isEqualTo(expectedMusicRecordingProto);
}
@Test
@@ -151,4 +142,25 @@ public class AppSearchSchemaTest {
builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
builder.build();
}
@Test
public void testDuplicateProperties() {
AppSearchSchema.Builder builder = AppSearchSchema.newBuilder("Email")
.addProperty(AppSearchSchema.newPropertyBuilder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build()
).addProperty(AppSearchSchema.newPropertyBuilder("subject")
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.build()
);
Exception e = expectThrows(IllegalSchemaException.class, builder::build);
assertThat(e).hasMessageThat().contains("Property defined more than once: subject");
}
}