Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aff0c2a9 authored by Alexander Dorokhine's avatar Alexander Dorokhine Committed by Android (Google) Code Review
Browse files

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

parents 6091c77d 0a26313a
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -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);
+142 −199
Original line number Diff line number Diff line
@@ -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,59 +58,22 @@ 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;
    }

    /** Builder for {@link AppSearchSchema objects}. */
    public static final class Builder {
        private final SchemaProto.Builder mProtoBuilder = SchemaProto.newBuilder();

        private Builder() {}

        /** Adds a supported type to this app's AppSearch schema. */
        @NonNull
        public AppSearchSchema.Builder addType(@NonNull SchemaType schemaType) {
            mProtoBuilder.addTypes(schemaType.mProto);
            return this;
    @Override
    public String toString() {
        return mProto.toString();
    }

        /**
         * Constructs a new {@link AppSearchSchema} from the contents of this builder.
         *
         * <p>After calling this method, the builder must no longer be used.
         */
        @NonNull
        public AppSearchSchema build() {
            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. */
    /** Builder for {@link AppSearchSchema objects}. */
    public static final class Builder {
        private final SchemaTypeConfigProto.Builder mProtoBuilder =
                SchemaTypeConfigProto.newBuilder();
@@ -124,21 +84,28 @@ public final class AppSearchSchema {

        /** Adds a property to the given type. */
        @NonNull
            public SchemaType.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
        public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
            mProtoBuilder.addProperties(propertyConfig.mProto);
            return this;
        }

        /**
             * Constructs a new {@link SchemaType} from the contents of this builder.
         * Constructs a new {@link AppSearchSchema} 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());
        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());
        }
    }

    /**
@@ -197,12 +164,71 @@ 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 = {
                INDEXING_TYPE_NONE,
                INDEXING_TYPE_EXACT_TERMS,
                INDEXING_TYPE_PREFIXES,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface IndexingType {}

        /**
         * Content in this property will not be tokenized or indexed.
         *
         * <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 indexingType} for the nested properties.
         */
        public static final int INDEXING_TYPE_NONE = 0;

        /**
         * Content in this property should only be returned for queries matching the exact tokens
         * appearing in this property.
         *
         * <p>Ex. A property with "fool" should NOT match a query for "foo".
         */
        public static final int INDEXING_TYPE_EXACT_TERMS = 1;

        /**
         * Content in this property should be returned for queries that are either exact matches or
         * query matches of the tokens appearing in this property.
         *
         * <p>Ex. A property with "fool" <b>should</b> match a query for "foo".
         */
        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
        // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code.
        @IntDef(prefix = {"TOKENIZER_TYPE_"}, value = {
                TOKENIZER_TYPE_NONE,
                TOKENIZER_TYPE_PLAIN,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface TokenizerType {}

        /**
         * It is only valid for tokenizer_type to be 'NONE' if the data type is
         * {@link PropertyConfig#DATA_TYPE_DOCUMENT}.
         */
        public static final int TOKENIZER_TYPE_NONE = 0;

        /** Tokenization for plain text. */
        public static final int TOKENIZER_TYPE_PLAIN = 1;

        private final PropertyConfigProto mProto;

        private PropertyConfig(PropertyConfigProto proto) {
            mProto = proto;
        }

        @Override
        public String toString() {
            return mProto.toString();
        }

        /**
         * Builder for {@link PropertyConfig}.
         *
@@ -217,11 +243,14 @@ public final class AppSearchSchema {
         * is also required.
         */
        public static final class Builder {
            private final PropertyConfigProto.Builder mProtoBuilder =
            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(String propertyName) {
                mProtoBuilder.setPropertyName(propertyName);
                mPropertyConfigProto.setPropertyName(propertyName);
            }

            /**
@@ -236,7 +265,7 @@ public final class AppSearchSchema {
                if (dataTypeProto == null) {
                    throw new IllegalArgumentException("Invalid dataType: " + dataType);
                }
                mProtoBuilder.setDataType(dataTypeProto);
                mPropertyConfigProto.setDataType(dataTypeProto);
                return this;
            }

@@ -248,7 +277,7 @@ public final class AppSearchSchema {
             */
            @NonNull
            public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
                mProtoBuilder.setSchemaType(schemaType);
                mPropertyConfigProto.setSchemaType(schemaType);
                return this;
            }

@@ -264,19 +293,44 @@ public final class AppSearchSchema {
                if (cardinalityProto == null) {
                    throw new IllegalArgumentException("Invalid cardinality: " + cardinality);
                }
                mProtoBuilder.setCardinality(cardinalityProto);
                mPropertyConfigProto.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.
             * Configures how a property should be indexed so that it can be retrieved by queries.
             */
            @NonNull
            public PropertyConfig.Builder setIndexingConfig(
                    @NonNull IndexingConfig indexingConfig) {
                mProtoBuilder.setIndexingConfig(indexingConfig.mProto);
            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 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);
                return this;
            }

@@ -290,136 +344,25 @@ public final class AppSearchSchema {
             */
            @NonNull
            public PropertyConfig build() {
                if (mProtoBuilder.getDataType() == PropertyConfigProto.DataType.Code.UNKNOWN) {
                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 (mProtoBuilder.getSchemaType().isEmpty()
                        && mProtoBuilder.getDataType()
                if (mPropertyConfigProto.getSchemaType().isEmpty()
                        && mPropertyConfigProto.getDataType()
                            == PropertyConfigProto.DataType.Code.DOCUMENT) {
                    throw new IllegalSchemaException(
                            "Missing field: schemaType (required for configs with "
                                    + "dataType = DOCUMENT)");
                }
                if (mProtoBuilder.getCardinality()
                if (mPropertyConfigProto.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,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface TermMatchType {}

        /**
         * Content in this property will not be tokenized or indexed.
         *
         * <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.
         */
        public static final int TERM_MATCH_TYPE_UNKNOWN = 0;

        /**
         * Content in this property should only be returned for queries matching the exact tokens
         * appearing in this property.
         *
         * <p>Ex. A property with "fool" should NOT match a query for "foo".
         */
        public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1;

        /**
         * Content in this property should be returned for queries that are either exact matches or
         * query matches of the tokens appearing in this property.
         *
         * <p>Ex. A property with "fool" <b>should</b> match a query for "foo".
         */
        public static final int TERM_MATCH_TYPE_PREFIX = 2;

        /** Configures how tokens should be extracted from this property. */
        // 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 = {
                TOKENIZER_TYPE_NONE,
                TOKENIZER_TYPE_PLAIN,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface TokenizerType {}

        /**
         * It is only valid for tokenizer_type to be 'NONE' if the data type is
         * {@link PropertyConfig#DATA_TYPE_DOCUMENT}.
         */
        public static final int TOKENIZER_TYPE_NONE = 0;

        /** Tokenization for plain text. */
        public static final int TOKENIZER_TYPE_PLAIN = 1;

        private final com.google.android.icing.proto.IndexingConfig mProto;

        private IndexingConfig(com.google.android.icing.proto.IndexingConfig proto) {
            mProto = proto;
        }

        /**
         * Builder for {@link IndexingConfig} objects.
         *
         * <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.
         */
        public static final class Builder {
            private final com.google.android.icing.proto.IndexingConfig.Builder mProtoBuilder =
                    com.google.android.icing.proto.IndexingConfig.newBuilder();

            private Builder() {}

            /** Configures how the content of this property should be matched in the index. */
            @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);
                }
                mProtoBuilder.setTermMatchType(termMatchTypeProto);
                return this;
            }

            /** Configures how this property should be tokenized (split into words). */
            @NonNull
            public IndexingConfig.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);
                }
                mProtoBuilder.setTokenizerType(tokenizerTypeProto);
                return this;
            }

            /**
             * Constructs a new {@link IndexingConfig} from the contents of this builder.
             *
             * <p>After calling this method, the builder must no longer be used.
             */
            @NonNull
            public IndexingConfig build() {
                return new IndexingConfig(mProtoBuilder.build());
                return new PropertyConfig(mPropertyConfigProto.build());
            }
        }
    }
+95 −83

File changed.

Preview size limit exceeded, changes collapsed.