Merge Jetpack AppSearchSchema work from last two quarters.
Bug: 162450968 Test: AppSearchManagerTest, AppSearchSchemaTest Change-Id: Iec02a202e01db5d92fe04b5c4abca3aff87e7000
This commit is contained in:
@@ -18,12 +18,12 @@ package android.app.appsearch;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.android.internal.infra.AndroidFuture;
|
||||
|
||||
import com.google.android.icing.proto.DocumentProto;
|
||||
import com.google.android.icing.proto.SchemaProto;
|
||||
import com.google.android.icing.proto.SearchResultProto;
|
||||
import com.google.android.icing.proto.SearchSpecProto;
|
||||
import com.google.android.icing.proto.StatusProto;
|
||||
@@ -133,19 +133,15 @@ public class AppSearchManager {
|
||||
@NonNull
|
||||
public AppSearchResult<Void> setSchema(
|
||||
@NonNull List<AppSearchSchema> schemas, boolean forceOverride) {
|
||||
// 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();
|
||||
List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
|
||||
for (AppSearchSchema schema : schemas) {
|
||||
schemaBundles.add(schema.getBundle());
|
||||
}
|
||||
AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
|
||||
try {
|
||||
mService.setSchema(schemaBytes, forceOverride, future);
|
||||
mService.setSchema(schemaBundles, forceOverride, future);
|
||||
} catch (RemoteException e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,18 +16,18 @@
|
||||
|
||||
package android.app.appsearch;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import android.app.appsearch.exceptions.IllegalSchemaException;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import com.google.android.icing.proto.PropertyConfigProto;
|
||||
import com.google.android.icing.proto.SchemaTypeConfigProto;
|
||||
import com.google.android.icing.proto.TermMatchType;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -36,45 +36,64 @@ import java.util.Set;
|
||||
* <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 SchemaTypeConfigProto mProto;
|
||||
/** @hide */
|
||||
|
||||
public static final String SCHEMA_TYPE_FIELD = "schemaType";
|
||||
|
||||
private AppSearchSchema(SchemaTypeConfigProto proto) {
|
||||
mProto = proto;
|
||||
/** @hide */
|
||||
|
||||
public static final String PROPERTIES_FIELD = "properties";
|
||||
|
||||
private final Bundle mBundle;
|
||||
|
||||
/** @hide */
|
||||
|
||||
public AppSearchSchema(@NonNull Bundle bundle) {
|
||||
Preconditions.checkNotNull(bundle);
|
||||
mBundle = bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link SchemaTypeConfigProto} populated by this builder.
|
||||
* Returns the {@link Bundle} populated by this builder.
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@NonNull
|
||||
@VisibleForTesting
|
||||
public SchemaTypeConfigProto getProto() {
|
||||
return mProto;
|
||||
public Bundle getBundle() {
|
||||
return mBundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mProto.toString();
|
||||
return mBundle.toString();
|
||||
}
|
||||
|
||||
/** Builder for {@link AppSearchSchema objects}. */
|
||||
public static final class Builder {
|
||||
private final SchemaTypeConfigProto.Builder mProtoBuilder =
|
||||
SchemaTypeConfigProto.newBuilder();
|
||||
private final String mTypeName;
|
||||
private final ArrayList<Bundle> mProperties = new ArrayList<>();
|
||||
private final Set<String> mPropertyNames = new ArraySet<>();
|
||||
private boolean mBuilt = false;
|
||||
|
||||
/** Creates a new {@link AppSearchSchema.Builder}. */
|
||||
public Builder(@NonNull String typeName) {
|
||||
mProtoBuilder.setSchemaType(typeName);
|
||||
Preconditions.checkNotNull(typeName);
|
||||
mTypeName = typeName;
|
||||
}
|
||||
|
||||
/** Adds a property to the given type. */
|
||||
@NonNull
|
||||
public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
|
||||
mProtoBuilder.addProperties(propertyConfig.mProto);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkNotNull(propertyConfig);
|
||||
if (!mPropertyNames.add(propertyConfig.mName)) {
|
||||
throw new IllegalSchemaException(
|
||||
"Property defined more than once: " + propertyConfig.mName);
|
||||
}
|
||||
mProperties.add(propertyConfig.mBundle);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -85,15 +104,12 @@ 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());
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mTypeName);
|
||||
bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mProperties);
|
||||
mBuilt = true;
|
||||
return new AppSearchSchema(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,10 +120,37 @@ public final class AppSearchSchema {
|
||||
* a property.
|
||||
*/
|
||||
public static final class PropertyConfig {
|
||||
/** Physical data-types of the contents of the property. */
|
||||
/** @hide */
|
||||
|
||||
public static final String NAME_FIELD = "name";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String DATA_TYPE_FIELD = "dataType";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String SCHEMA_TYPE_FIELD = "schemaType";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String CARDINALITY_FIELD = "cardinality";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String INDEXING_TYPE_FIELD = "indexingType";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String TOKENIZER_TYPE_FIELD = "tokenizerType";
|
||||
|
||||
/**
|
||||
* Physical data-types of the contents of the property.
|
||||
* @hide
|
||||
*/
|
||||
// NOTE: The integer values of these constants must match the proto enum constants in
|
||||
// com.google.android.icing.proto.PropertyConfigProto.DataType.Code.
|
||||
@IntDef(prefix = {"DATA_TYPE_"}, value = {
|
||||
@IntDef(value = {
|
||||
DATA_TYPE_STRING,
|
||||
DATA_TYPE_INT64,
|
||||
DATA_TYPE_DOUBLE,
|
||||
@@ -133,10 +176,13 @@ public final class AppSearchSchema {
|
||||
*/
|
||||
public static final int DATA_TYPE_DOCUMENT = 6;
|
||||
|
||||
/** The cardinality of the property (whether it is required, optional or repeated). */
|
||||
/**
|
||||
* The cardinality of the property (whether it is required, optional or repeated).
|
||||
* @hide
|
||||
*/
|
||||
// NOTE: The integer values of these constants must match the proto enum constants in
|
||||
// com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code.
|
||||
@IntDef(prefix = {"CARDINALITY_"}, value = {
|
||||
@IntDef(value = {
|
||||
CARDINALITY_REPEATED,
|
||||
CARDINALITY_OPTIONAL,
|
||||
CARDINALITY_REQUIRED,
|
||||
@@ -153,8 +199,11 @@ public final class AppSearchSchema {
|
||||
/** Exactly one value [1]. */
|
||||
public static final int CARDINALITY_REQUIRED = 3;
|
||||
|
||||
/** Encapsulates the configurations on how AppSearch should query/index these terms. */
|
||||
@IntDef(prefix = {"INDEXING_TYPE_"}, value = {
|
||||
/**
|
||||
* Encapsulates the configurations on how AppSearch should query/index these terms.
|
||||
* @hide
|
||||
*/
|
||||
@IntDef(value = {
|
||||
INDEXING_TYPE_NONE,
|
||||
INDEXING_TYPE_EXACT_TERMS,
|
||||
INDEXING_TYPE_PREFIXES,
|
||||
@@ -188,10 +237,13 @@ public final class AppSearchSchema {
|
||||
*/
|
||||
public static final int INDEXING_TYPE_PREFIXES = 2;
|
||||
|
||||
/** Configures how tokens should be extracted from this property. */
|
||||
/**
|
||||
* Configures how tokens should be extracted from this property.
|
||||
* @hide
|
||||
*/
|
||||
// NOTE: The integer values of these constants must match the proto enum constants in
|
||||
// com.google.android.icing.proto.IndexingConfig.TokenizerType.Code.
|
||||
@IntDef(prefix = {"TOKENIZER_TYPE_"}, value = {
|
||||
@IntDef(value = {
|
||||
TOKENIZER_TYPE_NONE,
|
||||
TOKENIZER_TYPE_PLAIN,
|
||||
})
|
||||
@@ -207,15 +259,17 @@ public final class AppSearchSchema {
|
||||
/** Tokenization for plain text. */
|
||||
public static final int TOKENIZER_TYPE_PLAIN = 1;
|
||||
|
||||
private final PropertyConfigProto mProto;
|
||||
final String mName;
|
||||
final Bundle mBundle;
|
||||
|
||||
private PropertyConfig(PropertyConfigProto proto) {
|
||||
mProto = proto;
|
||||
PropertyConfig(@NonNull String name, @NonNull Bundle bundle) {
|
||||
mName = Preconditions.checkNotNull(name);
|
||||
mBundle = Preconditions.checkNotNull(bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mProto.toString();
|
||||
return mBundle.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,15 +286,14 @@ public final class AppSearchSchema {
|
||||
* is also required.
|
||||
*/
|
||||
public static final class Builder {
|
||||
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 final String mName;
|
||||
private final Bundle mBundle = new Bundle();
|
||||
private boolean mBuilt = false;
|
||||
|
||||
/** Creates a new {@link PropertyConfig.Builder}. */
|
||||
public Builder(@NonNull String propertyName) {
|
||||
mPropertyConfigProto.setPropertyName(propertyName);
|
||||
mName = Preconditions.checkNotNull(propertyName);
|
||||
mBundle.putString(NAME_FIELD, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,24 +303,24 @@ public final class AppSearchSchema {
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
mPropertyConfigProto.setDataType(dataTypeProto);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(
|
||||
dataType, DATA_TYPE_STRING, DATA_TYPE_DOCUMENT, "dataType");
|
||||
mBundle.putInt(DATA_TYPE_FIELD, dataType);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The logical schema-type of the contents of this property.
|
||||
*
|
||||
* <p>Only required when {@link #setDataType(int)} is set to
|
||||
* <p>Only required when {@link #setDataType} is set to
|
||||
* {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored.
|
||||
*/
|
||||
@NonNull
|
||||
public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
|
||||
mPropertyConfigProto.setSchemaType(schemaType);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkNotNull(schemaType);
|
||||
mBundle.putString(SCHEMA_TYPE_FIELD, schemaType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -278,12 +331,10 @@ public final class AppSearchSchema {
|
||||
*/
|
||||
@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);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(
|
||||
cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
|
||||
mBundle.putInt(CARDINALITY_FIELD, cardinality);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -292,35 +343,20 @@ public final class AppSearchSchema {
|
||||
*/
|
||||
@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);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(
|
||||
indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType");
|
||||
mBundle.putInt(INDEXING_TYPE_FIELD, indexingType);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configures how this property should be tokenized (split into words). */
|
||||
@NonNull
|
||||
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);
|
||||
if (tokenizerTypeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
|
||||
}
|
||||
mIndexingConfigProto.setTokenizerType(tokenizerTypeProto);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(
|
||||
tokenizerType, TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, "tokenizerType");
|
||||
mBundle.putInt(TOKENIZER_TYPE_FIELD, tokenizerType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -334,25 +370,23 @@ public final class AppSearchSchema {
|
||||
*/
|
||||
@NonNull
|
||||
public PropertyConfig build() {
|
||||
mPropertyConfigProto.setIndexingConfig(mIndexingConfigProto);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
// 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) {
|
||||
if (!mBundle.containsKey(DATA_TYPE_FIELD)) {
|
||||
throw new IllegalSchemaException("Missing field: dataType");
|
||||
}
|
||||
if (mPropertyConfigProto.getSchemaType().isEmpty()
|
||||
&& mPropertyConfigProto.getDataType()
|
||||
== PropertyConfigProto.DataType.Code.DOCUMENT) {
|
||||
if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty()
|
||||
&& mBundle.getInt(DATA_TYPE_FIELD) == DATA_TYPE_DOCUMENT) {
|
||||
throw new IllegalSchemaException(
|
||||
"Missing field: schemaType (required for configs with "
|
||||
+ "dataType = DOCUMENT)");
|
||||
}
|
||||
if (mPropertyConfigProto.getCardinality()
|
||||
== PropertyConfigProto.Cardinality.Code.UNKNOWN) {
|
||||
if (!mBundle.containsKey(CARDINALITY_FIELD)) {
|
||||
throw new IllegalSchemaException("Missing field: cardinality");
|
||||
}
|
||||
return new PropertyConfig(mPropertyConfigProto.build());
|
||||
mBuilt = true;
|
||||
return new PropertyConfig(mName, mBundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package android.app.appsearch;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.internal.infra.AndroidFuture;
|
||||
|
||||
parcelable AppSearchResult;
|
||||
@@ -25,14 +27,16 @@ interface IAppSearchManager {
|
||||
/**
|
||||
* Sets the schema.
|
||||
*
|
||||
* @param schemaBytes Serialized SchemaProto.
|
||||
* @param schemaBundles List of AppSearchSchema bundles.
|
||||
* @param forceOverride Whether to apply the new schema even if it is incompatible. All
|
||||
* incompatible documents will be deleted.
|
||||
* @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link Void}>>.
|
||||
* The results of the call.
|
||||
*/
|
||||
void setSchema(
|
||||
in byte[] schemaBytes, boolean forceOverride, in AndroidFuture<AppSearchResult> callback);
|
||||
in List<Bundle> schemaBundles,
|
||||
boolean forceOverride,
|
||||
in AndroidFuture<AppSearchResult> callback);
|
||||
|
||||
/**
|
||||
* Inserts documents into the index.
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.app.appsearch;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.appsearch.exceptions.IllegalSearchSpecException;
|
||||
|
||||
import com.google.android.icing.proto.ResultSpecProto;
|
||||
import com.google.android.icing.proto.ScoringSpecProto;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,16 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.appsearch;
|
||||
package android.app.appsearch.exceptions;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such
|
||||
* as unpopulated mandatory fields or illegal combinations of parameters.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
|
||||
public class IllegalSchemaException extends IllegalArgumentException {
|
||||
/**
|
||||
* Constructs a new {@link IllegalSchemaException}.
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,16 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.appsearch;
|
||||
package android.app.appsearch.exceptions;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that a {@link android.app.appsearch.SearchResults} has logical inconsistencies such
|
||||
* Indicates that a {@link android.app.appsearch.SearchResult} has logical inconsistencies such
|
||||
* as unpopulated mandatory fields or illegal combinations of parameters.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
|
||||
public class IllegalSearchSpecException extends IllegalArgumentException {
|
||||
/**
|
||||
* Constructs a new {@link IllegalSearchSpecException}.
|
||||
@@ -19,20 +19,24 @@ import android.annotation.NonNull;
|
||||
import android.app.appsearch.AppSearchBatchResult;
|
||||
import android.app.appsearch.AppSearchDocument;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.IAppSearchManager;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.internal.infra.AndroidFuture;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.appsearch.external.localbackend.AppSearchImpl;
|
||||
import com.android.server.appsearch.external.localbackend.converter.SchemaToProtoConverter;
|
||||
|
||||
import com.google.android.icing.proto.DocumentProto;
|
||||
import com.google.android.icing.proto.ResultSpecProto;
|
||||
import com.google.android.icing.proto.SchemaProto;
|
||||
import com.google.android.icing.proto.SchemaTypeConfigProto;
|
||||
import com.google.android.icing.proto.ScoringSpecProto;
|
||||
import com.google.android.icing.proto.SearchResultProto;
|
||||
import com.google.android.icing.proto.SearchSpecProto;
|
||||
@@ -59,19 +63,24 @@ public class AppSearchManagerService extends SystemService {
|
||||
private class Stub extends IAppSearchManager.Stub {
|
||||
@Override
|
||||
public void setSchema(
|
||||
@NonNull byte[] schemaBytes,
|
||||
@NonNull List<Bundle> schemaBundles,
|
||||
boolean forceOverride,
|
||||
@NonNull AndroidFuture<AppSearchResult> callback) {
|
||||
Preconditions.checkNotNull(schemaBytes);
|
||||
Preconditions.checkNotNull(schemaBundles);
|
||||
Preconditions.checkNotNull(callback);
|
||||
int callingUid = Binder.getCallingUidOrThrow();
|
||||
int callingUserId = UserHandle.getUserId(callingUid);
|
||||
long callingIdentity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
SchemaProto schema = SchemaProto.parseFrom(schemaBytes);
|
||||
SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder();
|
||||
for (int i = 0; i < schemaBundles.size(); i++) {
|
||||
AppSearchSchema schema = new AppSearchSchema(schemaBundles.get(i));
|
||||
SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
|
||||
schemaProtoBuilder.addTypes(schemaTypeProto);
|
||||
}
|
||||
AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
|
||||
String databaseName = makeDatabaseName(callingUid);
|
||||
impl.setSchema(databaseName, schema, forceOverride);
|
||||
impl.setSchema(databaseName, schemaProtoBuilder.build(), forceOverride);
|
||||
callback.complete(AppSearchResult.newSuccessfulResult(/*value=*/ null));
|
||||
} catch (Throwable t) {
|
||||
callback.complete(throwableToFailedResult(t));
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.appsearch.external.localbackend.converter;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import com.google.android.icing.proto.IndexingConfig;
|
||||
import com.google.android.icing.proto.PropertyConfigProto;
|
||||
import com.google.android.icing.proto.SchemaTypeConfigProto;
|
||||
import com.google.android.icing.proto.TermMatchType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}.
|
||||
* @hide
|
||||
*/
|
||||
|
||||
public final class SchemaToProtoConverter {
|
||||
private SchemaToProtoConverter() {}
|
||||
|
||||
/**
|
||||
* Converts an {@link android.app.appsearch.AppSearchSchema} into a
|
||||
* {@link SchemaTypeConfigProto}.
|
||||
*/
|
||||
@NonNull
|
||||
public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) {
|
||||
Preconditions.checkNotNull(schema);
|
||||
Bundle bundle = schema.getBundle();
|
||||
SchemaTypeConfigProto.Builder protoBuilder =
|
||||
SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType(bundle.getString(AppSearchSchema.SCHEMA_TYPE_FIELD, ""));
|
||||
ArrayList<Bundle> properties =
|
||||
bundle.getParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD);
|
||||
if (properties != null) {
|
||||
for (int i = 0; i < properties.size(); i++) {
|
||||
PropertyConfigProto propertyProto = convertProperty(properties.get(i));
|
||||
protoBuilder.addProperties(propertyProto);
|
||||
}
|
||||
}
|
||||
return protoBuilder.build();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static PropertyConfigProto convertProperty(@NonNull Bundle bundle) {
|
||||
Preconditions.checkNotNull(bundle);
|
||||
PropertyConfigProto.Builder propertyConfigProto = PropertyConfigProto.newBuilder()
|
||||
.setPropertyName(bundle.getString(AppSearchSchema.PropertyConfig.NAME_FIELD, ""));
|
||||
IndexingConfig.Builder indexingConfig = IndexingConfig.newBuilder();
|
||||
|
||||
// Set dataType
|
||||
@AppSearchSchema.PropertyConfig.DataType int dataType =
|
||||
bundle.getInt(AppSearchSchema.PropertyConfig.DATA_TYPE_FIELD);
|
||||
PropertyConfigProto.DataType.Code dataTypeProto =
|
||||
PropertyConfigProto.DataType.Code.forNumber(dataType);
|
||||
if (dataTypeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid dataType: " + dataType);
|
||||
}
|
||||
propertyConfigProto.setDataType(dataTypeProto);
|
||||
|
||||
// Set schemaType
|
||||
propertyConfigProto.setSchemaType(
|
||||
bundle.getString(AppSearchSchema.PropertyConfig.SCHEMA_TYPE_FIELD, ""));
|
||||
|
||||
// Set cardinality
|
||||
@AppSearchSchema.PropertyConfig.Cardinality int cardinality =
|
||||
bundle.getInt(AppSearchSchema.PropertyConfig.CARDINALITY_FIELD);
|
||||
PropertyConfigProto.Cardinality.Code cardinalityProto =
|
||||
PropertyConfigProto.Cardinality.Code.forNumber(cardinality);
|
||||
if (cardinalityProto == null) {
|
||||
throw new IllegalArgumentException("Invalid cardinality: " + dataType);
|
||||
}
|
||||
propertyConfigProto.setCardinality(cardinalityProto);
|
||||
|
||||
// Set indexingType
|
||||
@AppSearchSchema.PropertyConfig.IndexingType int indexingType =
|
||||
bundle.getInt(AppSearchSchema.PropertyConfig.INDEXING_TYPE_FIELD);
|
||||
TermMatchType.Code termMatchTypeProto;
|
||||
switch (indexingType) {
|
||||
case AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE:
|
||||
termMatchTypeProto = TermMatchType.Code.UNKNOWN;
|
||||
break;
|
||||
case AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS:
|
||||
termMatchTypeProto = TermMatchType.Code.EXACT_ONLY;
|
||||
break;
|
||||
case AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES:
|
||||
termMatchTypeProto = TermMatchType.Code.PREFIX;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
|
||||
}
|
||||
indexingConfig.setTermMatchType(termMatchTypeProto);
|
||||
|
||||
// Set tokenizerType
|
||||
@AppSearchSchema.PropertyConfig.TokenizerType int tokenizerType =
|
||||
bundle.getInt(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_FIELD);
|
||||
IndexingConfig.TokenizerType.Code tokenizerTypeProto =
|
||||
IndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
|
||||
if (tokenizerTypeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
|
||||
}
|
||||
indexingConfig.setTokenizerType(tokenizerTypeProto);
|
||||
|
||||
// Build!
|
||||
propertyConfigProto.setIndexingConfig(indexingConfig);
|
||||
return propertyConfigProto.build();
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.appsearch;
|
||||
|
||||
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.PropertyConfig;
|
||||
import android.app.appsearch.proto.IndexingConfig;
|
||||
import android.app.appsearch.proto.PropertyConfigProto;
|
||||
import android.app.appsearch.proto.SchemaTypeConfigProto;
|
||||
import android.app.appsearch.proto.TermMatchType;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@SmallTest
|
||||
public class AppSearchSchemaTest {
|
||||
@Test
|
||||
public void testGetProto_Email() {
|
||||
AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
|
||||
.addProperty(new AppSearchSchema.PropertyConfig.Builder("subject")
|
||||
.setDataType(PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()
|
||||
).addProperty(new AppSearchSchema.PropertyConfig.Builder("body")
|
||||
.setDataType(PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()
|
||||
).build();
|
||||
|
||||
SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("Email")
|
||||
.addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("subject")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
android.app.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("body")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
android.app.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
android.app.appsearch.proto.IndexingConfig
|
||||
.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).build();
|
||||
|
||||
assertThat(emailSchema.getProto()).isEqualTo(expectedEmailProto);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProto_MusicRecording() {
|
||||
AppSearchSchema musicRecordingSchema = new AppSearchSchema.Builder("MusicRecording")
|
||||
.addProperty(new AppSearchSchema.PropertyConfig.Builder("artist")
|
||||
.setDataType(PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
|
||||
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()
|
||||
).addProperty(new AppSearchSchema.PropertyConfig.Builder("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(
|
||||
android.app.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
android.app.appsearch.proto.IndexingConfig
|
||||
.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("pubDate")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.INT64)
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
android.app.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(
|
||||
android.app.appsearch.proto.IndexingConfig
|
||||
.TokenizerType.Code.NONE)
|
||||
.setTermMatchType(TermMatchType.Code.UNKNOWN)
|
||||
)
|
||||
).build();
|
||||
|
||||
assertThat(musicRecordingSchema.getProto()).isEqualTo(expectedMusicRecordingProto);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidEnums() {
|
||||
PropertyConfig.Builder builder = new AppSearchSchema.PropertyConfig.Builder("test");
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingFields() {
|
||||
PropertyConfig.Builder builder = new AppSearchSchema.PropertyConfig.Builder("test");
|
||||
Exception e = expectThrows(IllegalSchemaException.class, builder::build);
|
||||
assertThat(e).hasMessageThat().contains("Missing field: dataType");
|
||||
|
||||
builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
|
||||
e = expectThrows(IllegalSchemaException.class, builder::build);
|
||||
assertThat(e).hasMessageThat().contains("Missing field: schemaType");
|
||||
|
||||
builder.setSchemaType("TestType");
|
||||
e = expectThrows(IllegalSchemaException.class, builder::build);
|
||||
assertThat(e).hasMessageThat().contains("Missing field: cardinality");
|
||||
|
||||
builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
|
||||
builder.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateProperties() {
|
||||
AppSearchSchema.Builder builder = new AppSearchSchema.Builder("Email")
|
||||
.addProperty(new AppSearchSchema.PropertyConfig.Builder("subject")
|
||||
.setDataType(PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()
|
||||
).addProperty(new AppSearchSchema.PropertyConfig.Builder("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");
|
||||
}
|
||||
}
|
||||
74
core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java
vendored
Normal file
74
core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.appsearch;
|
||||
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
import android.app.appsearch.AppSearchSchema.PropertyConfig;
|
||||
import android.app.appsearch.exceptions.IllegalSchemaException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class AppSearchSchemaTest {
|
||||
@Test
|
||||
public void testInvalidEnums() {
|
||||
PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
|
||||
expectThrows(IllegalArgumentException.class, () -> builder.setDataType(99));
|
||||
expectThrows(IllegalArgumentException.class, () -> builder.setCardinality(99));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingFields() {
|
||||
PropertyConfig.Builder builder = new PropertyConfig.Builder("test");
|
||||
IllegalSchemaException e = expectThrows(IllegalSchemaException.class, builder::build);
|
||||
assertThat(e).hasMessageThat().contains("Missing field: dataType");
|
||||
|
||||
builder.setDataType(PropertyConfig.DATA_TYPE_DOCUMENT);
|
||||
e = expectThrows(IllegalSchemaException.class, builder::build);
|
||||
assertThat(e).hasMessageThat().contains("Missing field: schemaType");
|
||||
|
||||
builder.setSchemaType("TestType");
|
||||
e = expectThrows(IllegalSchemaException.class, builder::build);
|
||||
assertThat(e).hasMessageThat().contains("Missing field: cardinality");
|
||||
|
||||
builder.setCardinality(PropertyConfig.CARDINALITY_REPEATED);
|
||||
builder.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateProperties() {
|
||||
AppSearchSchema.Builder builder = new AppSearchSchema.Builder("Email")
|
||||
.addProperty(new PropertyConfig.Builder("subject")
|
||||
.setDataType(PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()
|
||||
);
|
||||
IllegalSchemaException e = expectThrows(IllegalSchemaException.class,
|
||||
() -> builder.addProperty(new PropertyConfig.Builder("subject")
|
||||
.setDataType(PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()));
|
||||
assertThat(e).hasMessageThat().contains("Property defined more than once: subject");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.appsearch.external.localbackend.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
|
||||
import com.android.server.appsearch.proto.IndexingConfig;
|
||||
import com.android.server.appsearch.proto.PropertyConfigProto;
|
||||
import com.android.server.appsearch.proto.SchemaTypeConfigProto;
|
||||
import com.android.server.appsearch.proto.TermMatchType;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class SchemaToProtoConverterTest {
|
||||
@Test
|
||||
public void testGetProto_Email() {
|
||||
AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
|
||||
.addProperty(new AppSearchSchema.PropertyConfig.Builder("subject")
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()
|
||||
).addProperty(new AppSearchSchema.PropertyConfig.Builder("body")
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()
|
||||
).build();
|
||||
|
||||
SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("Email")
|
||||
.addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("subject")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setSchemaType("")
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
com.android.server.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("body")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setSchemaType("")
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
com.android.server.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).build();
|
||||
|
||||
assertThat(SchemaToProtoConverter.convert(emailSchema)).isEqualTo(expectedEmailProto);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProto_MusicRecording() {
|
||||
AppSearchSchema musicRecordingSchema = new AppSearchSchema.Builder("MusicRecording")
|
||||
.addProperty(new AppSearchSchema.PropertyConfig.Builder("artist")
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
|
||||
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
|
||||
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
|
||||
.setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
|
||||
.build()
|
||||
).addProperty(new AppSearchSchema.PropertyConfig.Builder("pubDate")
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
|
||||
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
|
||||
.setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
|
||||
.build()
|
||||
).build();
|
||||
|
||||
SchemaTypeConfigProto expectedMusicRecordingProto = SchemaTypeConfigProto.newBuilder()
|
||||
.setSchemaType("MusicRecording")
|
||||
.addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("artist")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.STRING)
|
||||
.setSchemaType("")
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
|
||||
.setIndexingConfig(
|
||||
com.android.server.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN)
|
||||
.setTermMatchType(TermMatchType.Code.PREFIX)
|
||||
)
|
||||
).addProperties(PropertyConfigProto.newBuilder()
|
||||
.setPropertyName("pubDate")
|
||||
.setDataType(PropertyConfigProto.DataType.Code.INT64)
|
||||
.setSchemaType("")
|
||||
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
|
||||
.setIndexingConfig(
|
||||
com.android.server.appsearch.proto.IndexingConfig.newBuilder()
|
||||
.setTokenizerType(IndexingConfig.TokenizerType.Code.NONE)
|
||||
.setTermMatchType(TermMatchType.Code.UNKNOWN)
|
||||
)
|
||||
).build();
|
||||
|
||||
assertThat(SchemaToProtoConverter.convert(musicRecordingSchema))
|
||||
.isEqualTo(expectedMusicRecordingProto);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user