Re-export with google-java-format --aosp running on all exported classes

This cleans up formatting issues arising from automatic rewriting.

There are no functional changes in this CL.

Bug: 162450968
Test: Builds
Change-Id: I58a6f7af5ca48c6989b00f1afb8110d49f7552a7
This commit is contained in:
Alexander Dorokhine
2020-11-20 14:03:41 -08:00
parent 2c94499193
commit f4a8941ff8
31 changed files with 1525 additions and 1277 deletions

View File

@@ -16,10 +16,8 @@
package android.app.appsearch;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.appsearch.AppSearchSchema.PropertyConfig;
/**
@@ -29,9 +27,8 @@ import android.app.appsearch.AppSearchSchema.PropertyConfig;
*
* @hide
*/
public class AppSearchEmail extends GenericDocument {
/** The name of the schema type for {@link AppSearchEmail} documents.*/
/** The name of the schema type for {@link AppSearchEmail} documents. */
public static final String SCHEMA_TYPE = "builtin:Email";
private static final String KEY_FROM = "from";
@@ -41,54 +38,55 @@ public class AppSearchEmail extends GenericDocument {
private static final String KEY_SUBJECT = "subject";
private static final String KEY_BODY = "body";
public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
.addProperty(new PropertyConfig.Builder(KEY_FROM)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
).addProperty(new PropertyConfig.Builder(KEY_TO)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
).addProperty(new PropertyConfig.Builder(KEY_CC)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
).addProperty(new PropertyConfig.Builder(KEY_BCC)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
).addProperty(new PropertyConfig.Builder(KEY_SUBJECT)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
).addProperty(new PropertyConfig.Builder(KEY_BODY)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build()
).build();
public static final AppSearchSchema SCHEMA =
new AppSearchSchema.Builder(SCHEMA_TYPE)
.addProperty(
new PropertyConfig.Builder(KEY_FROM)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build())
.addProperty(
new PropertyConfig.Builder(KEY_TO)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build())
.addProperty(
new PropertyConfig.Builder(KEY_CC)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build())
.addProperty(
new PropertyConfig.Builder(KEY_BCC)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_REPEATED)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build())
.addProperty(
new PropertyConfig.Builder(KEY_SUBJECT)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build())
.addProperty(
new PropertyConfig.Builder(KEY_BODY)
.setDataType(PropertyConfig.DATA_TYPE_STRING)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
.setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
.build())
.build();
/**
* Creates a new {@link AppSearchEmail} from the contents of an existing
* {@link GenericDocument}.
* Creates a new {@link AppSearchEmail} from the contents of an existing {@link
* GenericDocument}.
*
* @param document The {@link GenericDocument} containing the email content.
*/
@@ -109,8 +107,8 @@ public class AppSearchEmail extends GenericDocument {
/**
* Gets the destination addresses of {@link AppSearchEmail}.
*
* @return The destination addresses of {@link AppSearchEmail} or {@code null} if it's not
* been set yet.
* @return The destination addresses of {@link AppSearchEmail} or {@code null} if it's not been
* set yet.
*/
@Nullable
public String[] getTo() {
@@ -157,9 +155,7 @@ public class AppSearchEmail extends GenericDocument {
return getPropertyString(KEY_BODY);
}
/**
* The builder class for {@link AppSearchEmail}.
*/
/** The builder class for {@link AppSearchEmail}. */
public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> {
/**
@@ -171,54 +167,42 @@ public class AppSearchEmail extends GenericDocument {
super(uri, SCHEMA_TYPE);
}
/**
* Sets the from address of {@link AppSearchEmail}
*/
/** Sets the from address of {@link AppSearchEmail} */
@NonNull
public AppSearchEmail.Builder setFrom(@NonNull String from) {
setPropertyString(KEY_FROM, from);
return this;
}
/**
* Sets the destination address of {@link AppSearchEmail}
*/
/** Sets the destination address of {@link AppSearchEmail} */
@NonNull
public AppSearchEmail.Builder setTo(@NonNull String... to) {
setPropertyString(KEY_TO, to);
return this;
}
/**
* Sets the CC list of {@link AppSearchEmail}
*/
/** Sets the CC list of {@link AppSearchEmail} */
@NonNull
public AppSearchEmail.Builder setCc(@NonNull String... cc) {
setPropertyString(KEY_CC, cc);
return this;
}
/**
* Sets the BCC list of {@link AppSearchEmail}
*/
/** Sets the BCC list of {@link AppSearchEmail} */
@NonNull
public AppSearchEmail.Builder setBcc(@NonNull String... bcc) {
setPropertyString(KEY_BCC, bcc);
return this;
}
/**
* Sets the subject of {@link AppSearchEmail}
*/
/** Sets the subject of {@link AppSearchEmail} */
@NonNull
public AppSearchEmail.Builder setSubject(@NonNull String subject) {
setPropertyString(KEY_SUBJECT, subject);
return this;
}
/**
* Sets the body of {@link AppSearchEmail}
*/
/** Sets the body of {@link AppSearchEmail} */
@NonNull
public AppSearchEmail.Builder setBody(@NonNull String body) {
setPropertyString(KEY_BODY, body);

View File

@@ -16,15 +16,14 @@
package android.app.appsearch;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.IllegalSchemaException;
import android.os.Bundle;
import android.util.ArraySet;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -51,7 +50,6 @@ public final class AppSearchSchema {
private final Bundle mBundle;
/** @hide */
public AppSearchSchema(@NonNull Bundle bundle) {
Preconditions.checkNotNull(bundle);
mBundle = bundle;
@@ -59,9 +57,9 @@ public final class AppSearchSchema {
/**
* Returns the {@link Bundle} populated by this builder.
*
* @hide
*/
@NonNull
public Bundle getBundle() {
return mBundle;
@@ -146,8 +144,8 @@ public final class AppSearchSchema {
/**
* Configuration for a single property (field) of a document type.
*
* <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be
* a property.
* <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be a
* property.
*/
public static final class PropertyConfig {
private static final String NAME_FIELD = "name";
@@ -159,18 +157,20 @@ public final class AppSearchSchema {
/**
* 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(value = {
DATA_TYPE_STRING,
DATA_TYPE_INT64,
DATA_TYPE_DOUBLE,
DATA_TYPE_BOOLEAN,
DATA_TYPE_BYTES,
DATA_TYPE_DOCUMENT,
})
@IntDef(
value = {
DATA_TYPE_STRING,
DATA_TYPE_INT64,
DATA_TYPE_DOUBLE,
DATA_TYPE_BOOLEAN,
DATA_TYPE_BYTES,
DATA_TYPE_DOCUMENT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DataType {}
@@ -183,23 +183,24 @@ public final class AppSearchSchema {
public static final int DATA_TYPE_BYTES = 5;
/**
* Indicates that the property itself is an Document, making it part a hierarchical
* Document schema. Any property using this DataType MUST have a valid
* {@code schemaType}.
* Indicates that the property itself is an Document, making it part a hierarchical Document
* schema. Any property using this DataType MUST have a valid {@code schemaType}.
*/
public static final int DATA_TYPE_DOCUMENT = 6;
/**
* 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(value = {
CARDINALITY_REPEATED,
CARDINALITY_OPTIONAL,
CARDINALITY_REQUIRED,
})
@IntDef(
value = {
CARDINALITY_REPEATED,
CARDINALITY_OPTIONAL,
CARDINALITY_REQUIRED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Cardinality {}
@@ -214,23 +215,25 @@ public final class AppSearchSchema {
/**
* Encapsulates the configurations on how AppSearch should query/index these terms.
*
* @hide
*/
@IntDef(value = {
INDEXING_TYPE_NONE,
INDEXING_TYPE_EXACT_TERMS,
INDEXING_TYPE_PREFIXES,
})
@IntDef(
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.
* <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;
@@ -252,20 +255,22 @@ public final class AppSearchSchema {
/**
* 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(value = {
TOKENIZER_TYPE_NONE,
TOKENIZER_TYPE_PLAIN,
})
@IntDef(
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}.
* 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;
@@ -297,8 +302,8 @@ public final class AppSearchSchema {
/**
* Returns the logical schema-type of the contents of this property.
*
* <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}.
* Otherwise, it is {@code null}.
* <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}. Otherwise,
* it is {@code null}.
*/
@Nullable
public String getSchemaType() {
@@ -327,9 +332,10 @@ public final class AppSearchSchema {
*
* <p>The following properties must be set, or {@link PropertyConfig} construction will
* fail:
*
* <ul>
* <li>dataType
* <li>cardinality
* <li>dataType
* <li>cardinality
* </ul>
*
* <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType}
@@ -361,8 +367,8 @@ public final class AppSearchSchema {
/**
* The logical schema-type of the contents of this property.
*
* <p>Only required when {@link #setDataType} is set to
* {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored.
* <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) {

View File

@@ -16,15 +16,14 @@
package android.app.appsearch;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.util.Log;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.AppSearchException;
import android.os.Bundle;
import android.util.Log;
import com.android.internal.util.Preconditions;
import java.lang.reflect.Array;
@@ -46,12 +45,12 @@ import java.util.Set;
public class GenericDocument {
private static final String TAG = "GenericDocument";
/** The default empty namespace.*/
/** The default empty namespace. */
public static final String DEFAULT_NAMESPACE = "";
/**
* The maximum number of elements in a repeatable field. Will reject the request if exceed
* this limit.
* The maximum number of elements in a repeatable field. Will reject the request if exceed this
* limit.
*/
private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
@@ -82,46 +81,43 @@ public class GenericDocument {
/**
* The maximum number of indexed properties a document can have.
*
* <p>Indexed properties are properties where the
* {@link AppSearchSchema.PropertyConfig#getIndexingType()} constant is anything other than
* {@link AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
* <p>Indexed properties are properties where the {@link
* AppSearchSchema.PropertyConfig#getIndexingType()} constant is anything other than {@link
* AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
*/
public static int getMaxIndexedProperties() {
return MAX_INDEXED_PROPERTIES;
}
/** Contains {@link GenericDocument} basic information (uri, schemaType etc).*/
@NonNull
final Bundle mBundle;
/** Contains {@link GenericDocument} basic information (uri, schemaType etc). */
@NonNull final Bundle mBundle;
/** Contains all properties in {@link GenericDocument} to support getting properties via keys.*/
@NonNull
private final Bundle mProperties;
/**
* Contains all properties in {@link GenericDocument} to support getting properties via keys.
*/
@NonNull private final Bundle mProperties;
@NonNull
private final String mUri;
@NonNull
private final String mSchemaType;
@NonNull private final String mUri;
@NonNull private final String mSchemaType;
private final long mCreationTimestampMillis;
@Nullable
private Integer mHashCode;
@Nullable private Integer mHashCode;
/**
* Rebuilds a {@link GenericDocument} by the a bundle.
* @param bundle Contains {@link GenericDocument} basic information (uri, schemaType etc) and
* a properties bundle contains all properties in {@link GenericDocument} to
* support getting properties via keys.
*
* @param bundle Contains {@link GenericDocument} basic information (uri, schemaType etc) and a
* properties bundle contains all properties in {@link GenericDocument} to support getting
* properties via keys.
* @hide
*/
public GenericDocument(@NonNull Bundle bundle) {
Preconditions.checkNotNull(bundle);
mBundle = bundle;
mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD));
mUri = Preconditions.checkNotNull(mBundle.getString(URI_FIELD));
mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
mCreationTimestampMillis = mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD,
System.currentTimeMillis());
mCreationTimestampMillis =
mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
}
/**
@@ -135,9 +131,9 @@ public class GenericDocument {
/**
* Returns the {@link Bundle} populated by this builder.
*
* @hide
*/
@NonNull
public Bundle getBundle() {
return mBundle;
@@ -178,7 +174,7 @@ public class GenericDocument {
* time base, the document will be auto-deleted.
*
* <p>The default value is 0, which means the document is permanent and won't be auto-deleted
* until the app is uninstalled.
* until the app is uninstalled.
*/
public long getTtlMillis() {
return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
@@ -187,8 +183,8 @@ public class GenericDocument {
/**
* Returns the score of the {@link GenericDocument}.
*
* <p>The score is a query-independent measure of the document's quality, relative to
* other {@link GenericDocument}s of the same type.
* <p>The score is a query-independent measure of the document's quality, relative to other
* {@link GenericDocument}s of the same type.
*
* <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}.
* Documents with higher scores are considered better than documents with lower scores.
@@ -209,8 +205,8 @@ public class GenericDocument {
* Retrieves a {@link String} value by key.
*
* @param key The key to look for.
* @return The first {@link String} associated with the given key or {@code null} if there
* is no such key or the value is of a different type.
* @return The first {@link String} associated with the given key or {@code null} if there is no
* such key or the value is of a different type.
*/
@Nullable
public String getPropertyString(@NonNull String key) {
@@ -228,7 +224,7 @@ public class GenericDocument {
*
* @param key The key to look for.
* @return The first {@code long} associated with the given key or default value {@code 0} if
* there is no such key or the value is of a different type.
* there is no such key or the value is of a different type.
*/
public long getPropertyLong(@NonNull String key) {
Preconditions.checkNotNull(key);
@@ -245,7 +241,7 @@ public class GenericDocument {
*
* @param key The key to look for.
* @return The first {@code double} associated with the given key or default value {@code 0.0}
* if there is no such key or the value is of a different type.
* if there is no such key or the value is of a different type.
*/
public double getPropertyDouble(@NonNull String key) {
Preconditions.checkNotNull(key);
@@ -261,8 +257,8 @@ public class GenericDocument {
* Retrieves a {@code boolean} value by key.
*
* @param key The key to look for.
* @return The first {@code boolean} associated with the given key or default value
* {@code false} if there is no such key or the value is of a different type.
* @return The first {@code boolean} associated with the given key or default value {@code
* false} if there is no such key or the value is of a different type.
*/
public boolean getPropertyBoolean(@NonNull String key) {
Preconditions.checkNotNull(key);
@@ -278,8 +274,8 @@ public class GenericDocument {
* Retrieves a {@code byte[]} value by key.
*
* @param key The key to look for.
* @return The first {@code byte[]} associated with the given key or {@code null} if there
* is no such key or the value is of a different type.
* @return The first {@code byte[]} associated with the given key or {@code null} if there is no
* such key or the value is of a different type.
*/
@Nullable
public byte[] getPropertyBytes(@NonNull String key) {
@@ -297,7 +293,7 @@ public class GenericDocument {
*
* @param key The key to look for.
* @return The first {@link GenericDocument} associated with the given key or {@code null} if
* there is no such key or the value is of a different type.
* there is no such key or the value is of a different type.
*/
@Nullable
public GenericDocument getPropertyDocument(@NonNull String key) {
@@ -314,10 +310,18 @@ public class GenericDocument {
private static void warnIfSinglePropertyTooLong(
@NonNull String propertyType, @NonNull String key, int propertyLength) {
if (propertyLength > 1) {
Log.w(TAG, "The value for \"" + key + "\" contains " + propertyLength
+ " elements. Only the first one will be returned from "
+ "getProperty" + propertyType + "(). Try getProperty" + propertyType
+ "Array().");
Log.w(
TAG,
"The value for \""
+ key
+ "\" contains "
+ propertyLength
+ " elements. Only the first one will be returned from "
+ "getProperty"
+ propertyType
+ "(). Try getProperty"
+ propertyType
+ "Array().");
}
}
@@ -325,8 +329,8 @@ public class GenericDocument {
* Retrieves a repeated {@code String} property by key.
*
* @param key The key to look for.
* @return The {@code String[]} associated with the given key, or {@code null} if no value
* is set or the value is of a different type.
* @return The {@code String[]} associated with the given key, or {@code null} if no value is
* set or the value is of a different type.
*/
@Nullable
public String[] getPropertyStringArray(@NonNull String key) {
@@ -338,8 +342,8 @@ public class GenericDocument {
* Retrieves a repeated {@link String} property by key.
*
* @param key The key to look for.
* @return The {@code long[]} associated with the given key, or {@code null} if no value is
* set or the value is of a different type.
* @return The {@code long[]} associated with the given key, or {@code null} if no value is set
* or the value is of a different type.
*/
@Nullable
public long[] getPropertyLongArray(@NonNull String key) {
@@ -351,8 +355,8 @@ public class GenericDocument {
* Retrieves a repeated {@code double} property by key.
*
* @param key The key to look for.
* @return The {@code double[]} associated with the given key, or {@code null} if no value
* is set or the value is of a different type.
* @return The {@code double[]} associated with the given key, or {@code null} if no value is
* set or the value is of a different type.
*/
@Nullable
public double[] getPropertyDoubleArray(@NonNull String key) {
@@ -364,8 +368,8 @@ public class GenericDocument {
* Retrieves a repeated {@code boolean} property by key.
*
* @param key The key to look for.
* @return The {@code boolean[]} associated with the given key, or {@code null} if no value
* is set or the value is of a different type.
* @return The {@code boolean[]} associated with the given key, or {@code null} if no value is
* set or the value is of a different type.
*/
@Nullable
public boolean[] getPropertyBooleanArray(@NonNull String key) {
@@ -377,8 +381,8 @@ public class GenericDocument {
* Retrieves a {@code byte[][]} property by key.
*
* @param key The key to look for.
* @return The {@code byte[][]} associated with the given key, or {@code null} if no value
* is set or the value is of a different type.
* @return The {@code byte[][]} associated with the given key, or {@code null} if no value is
* set or the value is of a different type.
*/
@SuppressLint("ArrayReturn")
@Nullable
@@ -411,7 +415,7 @@ public class GenericDocument {
*
* @param key The key to look for.
* @return The {@link GenericDocument}[] associated with the given key, or {@code null} if no
* value is set or the value is of a different type.
* value is set or the value is of a different type.
*/
@SuppressLint("ArrayReturn")
@Nullable
@@ -433,8 +437,8 @@ public class GenericDocument {
}
/**
* Gets a repeated property of the given key, and casts it to the given class type, which
* must be an array class type.
* Gets a repeated property of the given key, and casts it to the given class type, which must
* be an array class type.
*/
@Nullable
private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) {
@@ -464,6 +468,7 @@ public class GenericDocument {
/**
* Deeply checks whether two bundles are equal.
*
* <p>Two bundles will be considered equal if they contain the same content.
*/
@SuppressWarnings("unchecked")
@@ -551,8 +556,9 @@ public class GenericDocument {
/**
* Calculates the hash code for a bundle.
* <p> The hash code is only effected by the contents in the bundle. Bundles will get
* consistent hash code if they have same contents.
*
* <p>The hash code is only effected by the contents in the bundle. Bundles will get consistent
* hash code if they have same contents.
*/
@SuppressWarnings("unchecked")
private static int bundleHashCode(Bundle bundle) {
@@ -677,11 +683,11 @@ public class GenericDocument {
* Create a new {@link GenericDocument.Builder}.
*
* @param uri The uri of {@link GenericDocument}.
* @param schemaType The schema type of the {@link GenericDocument}. The passed-in
* {@code schemaType} must be defined using {@link AppSearchSession#setSchema} prior
* to inserting a document of this {@code schemaType} into the AppSearch index using
* {@link AppSearchSession#putDocuments}. Otherwise, the document will be
* rejected by {@link AppSearchSession#putDocuments}.
* @param schemaType The schema type of the {@link GenericDocument}. The passed-in {@code
* schemaType} must be defined using {@link AppSearchSession#setSchema} prior to
* inserting a document of this {@code schemaType} into the AppSearch index using {@link
* AppSearchSession#putDocuments}. Otherwise, the document will be rejected by {@link
* AppSearchSession#putDocuments}.
*/
@SuppressWarnings("unchecked")
public Builder(@NonNull String uri, @NonNull String schemaType) {
@@ -692,16 +698,16 @@ public class GenericDocument {
mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
mBundle.putString(GenericDocument.NAMESPACE_FIELD, DEFAULT_NAMESPACE);
// Set current timestamp for creation timestamp by default.
mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD,
System.currentTimeMillis());
mBundle.putLong(
GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
mBundle.putBundle(PROPERTIES_FIELD, mProperties);
}
/**
* Sets the app-defined namespace this Document resides in. No special values are
* reserved or understood by the infrastructure.
* Sets the app-defined namespace this Document resides in. No special values are reserved
* or understood by the infrastructure.
*
* <p>URIs are unique within a namespace.
*
@@ -716,8 +722,8 @@ public class GenericDocument {
/**
* Sets the score of the {@link GenericDocument}.
*
* <p>The score is a query-independent measure of the document's quality, relative to
* other {@link GenericDocument}s of the same type.
* <p>The score is a query-independent measure of the document's quality, relative to other
* {@link GenericDocument}s of the same type.
*
* <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}.
* Documents with higher scores are considered better than documents with lower scores.
@@ -745,8 +751,8 @@ public class GenericDocument {
@NonNull
public BuilderType setCreationTimestampMillis(long creationTimestampMillis) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD,
creationTimestampMillis);
mBundle.putLong(
GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, creationTimestampMillis);
return mBuilderTypeInstance;
}
@@ -754,8 +760,8 @@ public class GenericDocument {
* Sets the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds.
*
* <p>The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of
* {@code creationTimestampMillis + ttlMillis}, measured in the
* {@link System#currentTimeMillis} time base, the document will be auto-deleted.
* {@code creationTimestampMillis + ttlMillis}, measured in the {@link
* System#currentTimeMillis} time base, the document will be auto-deleted.
*
* <p>The default value is 0, which means the document is permanent and won't be
* auto-deleted until the app is uninstalled.
@@ -774,8 +780,7 @@ public class GenericDocument {
}
/**
* Sets one or multiple {@code String} values for a property, replacing its previous
* values.
* Sets one or multiple {@code String} values for a property, replacing its previous values.
*
* @param key The key associated with the {@code values}.
* @param values The {@code String} values of the property.
@@ -806,8 +811,7 @@ public class GenericDocument {
}
/**
* Sets one or multiple {@code long} values for a property, replacing its previous
* values.
* Sets one or multiple {@code long} values for a property, replacing its previous values.
*
* @param key The key associated with the {@code values}.
* @param values The {@code long} values of the property.
@@ -822,8 +826,7 @@ public class GenericDocument {
}
/**
* Sets one or multiple {@code double} values for a property, replacing its previous
* values.
* Sets one or multiple {@code double} values for a property, replacing its previous values.
*
* @param key The key associated with the {@code values}.
* @param values The {@code double} values of the property.
@@ -876,9 +879,14 @@ public class GenericDocument {
if (values[i] == null) {
throw new IllegalArgumentException("The String at " + i + " is null.");
} else if (values[i].length() > MAX_STRING_LENGTH) {
throw new IllegalArgumentException("The String at " + i + " length is: "
+ values[i].length() + ", which exceeds length limit: "
+ MAX_STRING_LENGTH + ".");
throw new IllegalArgumentException(
"The String at "
+ i
+ " length is: "
+ values[i].length()
+ ", which exceeds length limit: "
+ MAX_STRING_LENGTH
+ ".");
}
}
mProperties.putStringArray(key, values);
@@ -936,7 +944,10 @@ public class GenericDocument {
throw new IllegalArgumentException("The input array is empty.");
} else if (length > MAX_REPEATED_PROPERTY_LENGTH) {
throw new IllegalArgumentException(
"Repeated property \"" + key + "\" has length " + length
"Repeated property \""
+ key
+ "\" has length "
+ length
+ ", which exceeds the limit of "
+ MAX_REPEATED_PROPERTY_LENGTH);
}

View File

@@ -18,6 +18,7 @@ package android.app.appsearch;
import android.annotation.NonNull;
import android.util.ArraySet;
import com.android.internal.util.Preconditions;
import java.util.Arrays;

View File

@@ -16,10 +16,10 @@
package android.app.appsearch;
import android.annotation.SuppressLint;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.AppSearchException;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
@@ -53,7 +53,7 @@ public final class PutDocumentsRequest {
private boolean mBuilt = false;
/** Adds one or more documents to the request. */
@SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@NonNull
public Builder addGenericDocument(@NonNull GenericDocument... documents) {
Preconditions.checkNotNull(documents);
@@ -61,7 +61,7 @@ public final class PutDocumentsRequest {
}
/** Adds one or more documents to the request. */
@SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
@NonNull
public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) {
Preconditions.checkState(!mBuilt, "Builder has already been used");

View File

@@ -18,6 +18,7 @@ package android.app.appsearch;
import android.annotation.NonNull;
import android.util.ArraySet;
import com.android.internal.util.Preconditions;
import java.util.Arrays;

View File

@@ -16,26 +16,26 @@
package android.app.appsearch;
import android.os.Bundle;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import java.util.Objects;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* This class represents one of the results obtained from an AppSearch query.
*
* <p>This allows clients to obtain:
*
* <ul>
* <li>The document which matched, using {@link #getDocument}
* <li>Information about which properties in the document matched, and "snippet" information
* containing textual summaries of the document's matches, using {@link #getMatches}
* </ul>
* </ul>
*
* <p>"Snippet" refers to a substring of text from the content of document that is returned as a
* part of search result.
@@ -45,40 +45,32 @@ import java.util.List;
*/
public final class SearchResult {
/** @hide */
public static final String DOCUMENT_FIELD = "document";
/** @hide */
public static final String MATCHES_FIELD = "matches";
@NonNull
private final Bundle mBundle;
@NonNull private final Bundle mBundle;
@NonNull
private final Bundle mDocumentBundle;
@NonNull private final Bundle mDocumentBundle;
/** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */
@Nullable
private GenericDocument mDocument;
@Nullable private GenericDocument mDocument;
/**
* Contains a list of MatchInfo bundles that matched the request.
*
* Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and
* <p>Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and
* {@link SearchSpec.Builder#setSnippetCountPerProperty}.
*
* @see #getMatches()
*/
@NonNull
private final List<Bundle> mMatchBundles;
@NonNull private final List<Bundle> mMatchBundles;
/** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */
@Nullable
private List<MatchInfo> mMatches;
@Nullable private List<MatchInfo> mMatches;
/** @hide */
public SearchResult(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle);
mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD));
@@ -86,7 +78,6 @@ public final class SearchResult {
}
/** @hide */
@NonNull
public Bundle getBundle() {
return mBundle;
@@ -94,6 +85,7 @@ public final class SearchResult {
/**
* Contains the matching {@link GenericDocument}.
*
* @return Document object which matched the query.
*/
@NonNull
@@ -107,10 +99,10 @@ public final class SearchResult {
/**
* Contains a list of Snippets that matched the request.
*
* @return List of matches based on {@link SearchSpec}. If snippeting is disabled using
* {@link SearchSpec.Builder#setSnippetCount} or
* {@link SearchSpec.Builder#setSnippetCountPerProperty}, for all results after that
* value, this method returns an empty list.
* @return List of matches based on {@link SearchSpec}. If snippeting is disabled using {@link
* SearchSpec.Builder#setSnippetCount} or {@link
* SearchSpec.Builder#setSnippetCountPerProperty}, for all results after that value, this
* method returns an empty list.
*/
@NonNull
public List<MatchInfo> getMatches() {
@@ -125,77 +117,94 @@ public final class SearchResult {
}
/**
* This class represents a match objects for any Snippets that might be present in
* {@link SearchResults} from query. Using this class
* user can get the full text, exact matches and Snippets of document content for a given match.
* This class represents a match objects for any Snippets that might be present in {@link
* SearchResults} from query. Using this class user can get the full text, exact matches and
* Snippets of document content for a given match.
*
* <p>Class Example 1: A document contains following text in property subject:
*
* <p>Class Example 1:
* A document contains following text in property subject:
* <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar.
*
* <p>If the queryExpression is "foo".
*
* <p>{@link MatchInfo#getPropertyPath()} returns "subject"
*
* <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another
* nonsense word that’s used a lot is bar."
*
* <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
*
* <p>{@link MatchInfo#getExactMatch()} returns "foo"
*
* <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33]
*
* <p>{@link MatchInfo#getSnippet()} returns "is foo."
*
* <p>
* <p>Class Example 2:
* A document contains a property name sender which contains 2 property names name and email, so
* we will have 2 property paths: {@code sender.name} and {@code sender.email}.
* <p>Let {@code sender.name = "Test Name Jr."} and
* {@code sender.email = "TestNameJr@gmail.com"}
*
* <p>Class Example 2: A document contains a property name sender which contains 2 property
* names name and email, so we will have 2 property paths: {@code sender.name} and {@code
* sender.email}.
*
* <p>Let {@code sender.name = "Test Name Jr."} and {@code sender.email =
* "TestNameJr@gmail.com"}
*
* <p>If the queryExpression is "Test". We will have 2 matches.
*
* <p> Match-1
* <p>Match-1
*
* <p>{@link MatchInfo#getPropertyPath()} returns "sender.name"
*
* <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
*
* <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
*
* <p>{@link MatchInfo#getExactMatch()} returns "Test"
*
* <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
*
* <p>{@link MatchInfo#getSnippet()} returns "Test Name"
* <p> Match-2
*
* <p>Match-2
*
* <p>{@link MatchInfo#getPropertyPath()} returns "sender.email"
*
* <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
*
* <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
*
* <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
*
* <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
*
* <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
*/
public static final class MatchInfo {
/**
* The path of the matching snippet property.
*
* @hide
*/
public static final String PROPERTY_PATH_FIELD = "propertyPath";
/**
* The index of matching value in its property. A property may have multiple values. This
* index indicates which value is the match.
*
* @hide
*/
public static final String VALUES_INDEX_FIELD = "valuesIndex";
/** @hide */
public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower";
/** @hide */
public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper";
/** @hide */
public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower";
/** @hide */
public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper";
private final String mFullText;
@@ -208,16 +217,18 @@ public final class SearchResult {
mBundle = Preconditions.checkNotNull(bundle);
Preconditions.checkNotNull(document);
mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
mFullText = getPropertyValues(
document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD));
mFullText =
getPropertyValues(document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD));
}
/**
* Gets the property path corresponding to the given entry.
*
* <p>Property Path: '.' - delimited sequence of property names indicating which property in
* the Document these snippets correspond to.
* <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc.
* For class example 1 this returns "subject"
*
* <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class
* example 1 this returns "subject"
*/
@NonNull
public String getPropertyPath() {
@@ -226,6 +237,7 @@ public final class SearchResult {
/**
* Gets the full text corresponding to the given entry.
*
* <p>For class example this returns "A commonly used fake word is foo. Another nonsense
* word that's used a lot is bar."
*/
@@ -236,20 +248,23 @@ public final class SearchResult {
/**
* Gets the exact {@link MatchRange} corresponding to the given entry.
*
* <p>For class example 1 this returns [29, 32]
*/
@NonNull
public MatchRange getExactMatchPosition() {
if (mExactMatchRange == null) {
mExactMatchRange = new MatchRange(
mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD),
mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD));
mExactMatchRange =
new MatchRange(
mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD),
mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD));
}
return mExactMatchRange;
}
/**
* Gets the {@link MatchRange} corresponding to the given entry.
* Gets the {@link MatchRange} corresponding to the given entry.
*
* <p>For class example 1 this returns "foo"
*/
@NonNull
@@ -259,26 +274,31 @@ public final class SearchResult {
/**
* Gets the snippet {@link MatchRange} corresponding to the given entry.
* <p>Only populated when set maxSnippetSize > 0 in
* {@link SearchSpec.Builder#setMaxSnippetSize}.
*
* <p>Only populated when set maxSnippetSize > 0 in {@link
* SearchSpec.Builder#setMaxSnippetSize}.
*
* <p>For class example 1 this returns [29, 41].
*/
@NonNull
public MatchRange getSnippetPosition() {
if (mWindowRange == null) {
mWindowRange = new MatchRange(
mBundle.getInt(WINDOW_POSITION_LOWER_FIELD),
mBundle.getInt(WINDOW_POSITION_UPPER_FIELD));
mWindowRange =
new MatchRange(
mBundle.getInt(WINDOW_POSITION_LOWER_FIELD),
mBundle.getInt(WINDOW_POSITION_UPPER_FIELD));
}
return mWindowRange;
}
/**
* Gets the snippet corresponding to the given entry.
*
* <p>Snippet - Provides a subset of the content to display. Only populated when requested
* maxSnippetSize > 0. The size of this content can be changed by
* {@link SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of
* the matched token with content on either side clipped to token boundaries.
* maxSnippetSize > 0. The size of this content can be changed by {@link
* SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of the
* matched token with content on either side clipped to token boundaries.
*
* <p>For class example 1 this returns "foo. Another"
*/
@NonNull
@@ -309,11 +329,10 @@ public final class SearchResult {
/**
* Class providing the position range of matching information.
*
* <p> All ranges are finite, and the left side of the range is always {@code <=} the right
* side of the range.
*
* <p> Example: MatchRange(0, 100) represent a hundred ints from 0 to 99."
* <p>All ranges are finite, and the left side of the range is always {@code <=} the right side
* of the range.
*
* <p>Example: MatchRange(0, 100) represent a hundred ints from 0 to 99."
*/
public static final class MatchRange {
private final int mEnd;
@@ -321,18 +340,18 @@ public final class SearchResult {
/**
* Creates a new immutable range.
* <p> The endpoints are {@code [start, end)}; that is the range is bounded. {@code start}
*
* <p>The endpoints are {@code [start, end)}; that is the range is bounded. {@code start}
* must be lesser or equal to {@code end}.
*
* @param start The start point (inclusive)
* @param end The end point (exclusive)
* @hide
*/
public MatchRange(int start, int end) {
if (start > end) {
throw new IllegalArgumentException("Start point must be less than or equal to "
+ "end point");
throw new IllegalArgumentException(
"Start point must be less than or equal to " + "end point");
}
mStart = start;
mEnd = end;

View File

@@ -16,10 +16,9 @@
package android.app.appsearch;
import android.os.Bundle;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import com.android.internal.util.Preconditions;
@@ -29,19 +28,17 @@ import java.util.List;
/**
* This class represents a page of {@link SearchResult}s
*
* @hide
*/
public class SearchResultPage {
public static final String RESULTS_FIELD = "results";
public static final String NEXT_PAGE_TOKEN_FIELD = "nextPageToken";
private final long mNextPageToken;
@Nullable
private List<SearchResult> mResults;
@Nullable private List<SearchResult> mResults;
@NonNull
private final Bundle mBundle;
@NonNull private final Bundle mBundle;
public SearchResultPage(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle);

View File

@@ -16,15 +16,14 @@
package android.app.appsearch;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.exceptions.IllegalSearchSpecException;
import android.os.Bundle;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -53,7 +52,6 @@ public final class SearchSpec {
static final String MAX_SNIPPET_FIELD = "maxSnippet";
/** @hide */
public static final int DEFAULT_NUM_PER_PAGE = 10;
// TODO(b/170371356): In framework, we may want these limits to be flag controlled.
@@ -65,43 +63,45 @@ public final class SearchSpec {
/**
* Term Match Type for the query.
*
* @hide
*/
// NOTE: The integer values of these constants must match the proto enum constants in
// {@link com.google.android.icing.proto.SearchSpecProto.termMatchType}
@IntDef(value = {
TERM_MATCH_EXACT_ONLY,
TERM_MATCH_PREFIX
})
@IntDef(value = {TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX})
@Retention(RetentionPolicy.SOURCE)
public @interface TermMatch {}
/**
* Query terms will only match exact tokens in the index.
*
* <p>Ex. A query term "foo" will only match indexed token "foo", and not "foot" or "football".
*/
public static final int TERM_MATCH_EXACT_ONLY = 1;
/**
* Query terms will match indexed tokens when the query term is a prefix of the token.
*
* <p>Ex. A query term "foo" will match indexed tokens like "foo", "foot", and "football".
*/
public static final int TERM_MATCH_PREFIX = 2;
/**
* Ranking Strategy for query result.
*
* @hide
*/
// NOTE: The integer values of these constants must match the proto enum constants in
// {@link ScoringSpecProto.RankingStrategy.Code}
@IntDef(value = {
RANKING_STRATEGY_NONE,
RANKING_STRATEGY_DOCUMENT_SCORE,
RANKING_STRATEGY_CREATION_TIMESTAMP
})
@IntDef(
value = {
RANKING_STRATEGY_NONE,
RANKING_STRATEGY_DOCUMENT_SCORE,
RANKING_STRATEGY_CREATION_TIMESTAMP
})
@Retention(RetentionPolicy.SOURCE)
public @interface RankingStrategy {}
/** No Ranking, results are returned in arbitrary order.*/
/** No Ranking, results are returned in arbitrary order. */
public static final int RANKING_STRATEGY_NONE = 0;
/** Ranked by app-provided document scores. */
public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1;
@@ -110,14 +110,12 @@ public final class SearchSpec {
/**
* Order for query result.
*
* @hide
*/
// NOTE: The integer values of these constants must match the proto enum constants in
// {@link ScoringSpecProto.Order.Code}
@IntDef(value = {
ORDER_DESCENDING,
ORDER_ASCENDING
})
@IntDef(value = {ORDER_DESCENDING, ORDER_ASCENDING})
@Retention(RetentionPolicy.SOURCE)
public @interface Order {}
@@ -129,7 +127,6 @@ public final class SearchSpec {
private final Bundle mBundle;
/** @hide */
public SearchSpec(@NonNull Bundle bundle) {
Preconditions.checkNotNull(bundle);
mBundle = bundle;
@@ -137,9 +134,9 @@ public final class SearchSpec {
/**
* Returns the {@link Bundle} populated by this builder.
*
* @hide
*/
@NonNull
public Bundle getBundle() {
return mBundle;
@@ -224,14 +221,12 @@ public final class SearchSpec {
mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
}
/**
* Indicates how the query terms should match {@code TermMatchCode} in the index.
*/
/** Indicates how the query terms should match {@code TermMatchCode} in the index. */
@NonNull
public Builder setTermMatch(@TermMatch int termMatchTypeCode) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkArgumentInRange(termMatchTypeCode, TERM_MATCH_EXACT_ONLY,
TERM_MATCH_PREFIX, "Term match type");
Preconditions.checkArgumentInRange(
termMatchTypeCode, TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX, "Term match type");
mBundle.putInt(TERM_MATCH_TYPE_FIELD, termMatchTypeCode);
return this;
}
@@ -264,8 +259,9 @@ public final class SearchSpec {
}
/**
* Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that
* have the specified namespaces.
* Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have
* the specified namespaces.
*
* <p>If unset, the query will search over all namespaces.
*/
@NonNull
@@ -276,8 +272,9 @@ public final class SearchSpec {
}
/**
* Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that
* have the specified namespaces.
* Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have
* the specified namespaces.
*
* <p>If unset, the query will search over all namespaces.
*/
@NonNull
@@ -302,34 +299,37 @@ public final class SearchSpec {
return this;
}
/** Sets ranking strategy for AppSearch results.*/
/** Sets ranking strategy for AppSearch results. */
@NonNull
public Builder setRankingStrategy(@RankingStrategy int rankingStrategy) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkArgumentInRange(rankingStrategy, RANKING_STRATEGY_NONE,
RANKING_STRATEGY_CREATION_TIMESTAMP, "Result ranking strategy");
Preconditions.checkArgumentInRange(
rankingStrategy,
RANKING_STRATEGY_NONE,
RANKING_STRATEGY_CREATION_TIMESTAMP,
"Result ranking strategy");
mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy);
return this;
}
/**
* Indicates the order of returned search results, the default is
* {@link #ORDER_DESCENDING}, meaning that results with higher scores come first.
* Indicates the order of returned search results, the default is {@link #ORDER_DESCENDING},
* meaning that results with higher scores come first.
*
* <p>This order field will be ignored if RankingStrategy = {@code RANKING_STRATEGY_NONE}.
*/
@NonNull
public Builder setOrder(@Order int order) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkArgumentInRange(order, ORDER_DESCENDING, ORDER_ASCENDING,
"Result ranking order");
Preconditions.checkArgumentInRange(
order, ORDER_DESCENDING, ORDER_ASCENDING, "Result ranking order");
mBundle.putInt(ORDER_FIELD, order);
return this;
}
/**
* Only the first {@code snippetCount} documents based on the ranking strategy
* will have snippet information provided.
* Only the first {@code snippetCount} documents based on the ranking strategy will have
* snippet information provided.
*
* <p>If set to 0 (default), snippeting is disabled and {@link SearchResult#getMatches} will
* return {@code null} for that result.
@@ -347,28 +347,31 @@ public final class SearchSpec {
* Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty}
* snippets for each property of {@link GenericDocument} will contain snippet information.
*
* <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches}
* will return {@code null} for that result.
* <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return
* {@code null} for that result.
*/
@NonNull
public SearchSpec.Builder setSnippetCountPerProperty(
@IntRange(from = 0, to = MAX_SNIPPET_PER_PROPERTY_COUNT)
int snippetCountPerProperty) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkArgumentInRange(snippetCountPerProperty,
0, MAX_SNIPPET_PER_PROPERTY_COUNT, "snippetCountPerProperty");
Preconditions.checkArgumentInRange(
snippetCountPerProperty,
0,
MAX_SNIPPET_PER_PROPERTY_COUNT,
"snippetCountPerProperty");
mBundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, snippetCountPerProperty);
return this;
}
/**
* Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at
* {@code maxSnippetSize/2} bytes before the middle of the matching token and end at
* {@code maxSnippetSize/2} bytes after the middle of the matching token. It respects
* token boundaries, therefore the returned window may be smaller than requested.
* Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at {@code
* maxSnippetSize/2} bytes before the middle of the matching token and end at {@code
* maxSnippetSize/2} bytes after the middle of the matching token. It respects token
* boundaries, therefore the returned window may be smaller than requested.
*
* <p> Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will
* be returned. If matches enabled is also set to false, then snippeting is disabled.
* <p>Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will be
* returned. If matches enabled is also set to false, then snippeting is disabled.
*
* <p>Ex. {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" will
* return a window of "bar baz bat" which is only 11 bytes long.

View File

@@ -16,11 +16,11 @@
package android.app.appsearch;
import android.annotation.SuppressLint;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.AppSearchException;
import android.util.ArraySet;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
@@ -81,8 +81,8 @@ public final class SetSchemaRequest {
* Configures the {@link SetSchemaRequest} to delete any existing documents that don't
* follow the new schema.
*
* <p>By default, this is {@code false} and schema incompatibility causes the
* {@link AppSearchSession#setSchema} call to fail.
* <p>By default, this is {@code false} and schema incompatibility causes the {@link
* AppSearchSession#setSchema} call to fail.
*
* @see AppSearchSession#setSchema
*/

View File

@@ -23,8 +23,9 @@ import android.app.appsearch.AppSearchResult;
/**
* An exception thrown by {@link android.app.appsearch.AppSearchSession} or a subcomponent.
*
* <p>These exceptions can be converted into a failed {@link AppSearchResult}
* for propagating to the client.
* <p>These exceptions can be converted into a failed {@link AppSearchResult} for propagating to the
* client.
*
* @hide
*/
public class AppSearchException extends Exception {
@@ -32,6 +33,7 @@ public class AppSearchException extends Exception {
/**
* Initializes an {@link AppSearchException} with no message.
*
* @hide
*/
public AppSearchException(@AppSearchResult.ResultCode int resultCode) {
@@ -58,9 +60,7 @@ public class AppSearchException extends Exception {
return mResultCode;
}
/**
* Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}
*/
/** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */
@NonNull
public <T> AppSearchResult<T> toAppSearchResult() {
return AppSearchResult.newFailedResult(mResultCode, getMessage());

View File

@@ -18,14 +18,12 @@ 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}.

View File

@@ -18,14 +18,12 @@ package android.app.appsearch.exceptions;
import android.annotation.NonNull;
/**
* Indicates that a {@link android.app.appsearch.SearchResult} has logical inconsistencies such
* as unpopulated mandatory fields or illegal combinations of parameters.
* 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}.

View File

@@ -16,13 +16,7 @@
package com.android.server.appsearch.external.localstorage;
import android.os.Bundle;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import android.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import android.annotation.WorkerThread;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
@@ -30,12 +24,17 @@ import android.app.appsearch.GenericDocument;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.exceptions.AppSearchException;
import android.os.Bundle;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter;
import android.util.ArraySet;
import com.android.internal.util.Preconditions;
import com.google.android.icing.IcingSearchEngine;
import com.google.android.icing.proto.DeleteResultProto;
@@ -77,43 +76,40 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
*
* <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents
* are physically saved together in {@link IcingSearchEngine}, but logically isolated:
*
* <ul>
* <li>Rewrite SchemaType in SchemaProto by adding database name prefix and save into
* SchemaTypes set in {@link #setSchema}.
* <li>Rewrite namespace and SchemaType in DocumentProto by adding database name prefix and
* save to namespaces set in {@link #putDocument}.
* <li>Remove database name prefix when retrieve documents in {@link #getDocument} and
* {@link #query}.
* <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of
* the queried database when user using empty filters in {@link #query}.
* <li>Rewrite SchemaType in SchemaProto by adding database name prefix and save into SchemaTypes
* set in {@link #setSchema}.
* <li>Rewrite namespace and SchemaType in DocumentProto by adding database name prefix and save
* to namespaces set in {@link #putDocument}.
* <li>Remove database name prefix when retrieve documents in {@link #getDocument} and {@link
* #query}.
* <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of the
* queried database when user using empty filters in {@link #query}.
* </ul>
*
* <p>Methods in this class belong to two groups, the query group and the mutate group.
*
* <ul>
* <li>All methods are going to modify global parameters and data in Icing are executed under
* WRITE lock to keep thread safety.
* <li>All methods are going to access global parameters or query data from Icing are executed
* under READ lock to improve query performance.
* <li>All methods are going to modify global parameters and data in Icing are executed under
* WRITE lock to keep thread safety.
* <li>All methods are going to access global parameters or query data from Icing are executed
* under READ lock to improve query performance.
* </ul>
*
* <p>This class is thread safe.
*
* @hide
*/
@WorkerThread
public final class AppSearchImpl {
private static final String TAG = "AppSearchImpl";
@VisibleForTesting
static final char DATABASE_DELIMITER = '/';
@VisibleForTesting static final char DATABASE_DELIMITER = '/';
@VisibleForTesting
static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
@VisibleForTesting
static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB
@VisibleForTesting
static final int CHECK_OPTIMIZE_INTERVAL = 100;
@VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
@VisibleForTesting static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB
@VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100;
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
@@ -134,8 +130,7 @@ public final class AppSearchImpl {
private final Map<String, Set<String>> mNamespaceMapLocked = new HashMap<>();
/**
* The counter to check when to call {@link #checkForOptimizeLocked(boolean)}. The
* interval is
* The counter to check when to call {@link #checkForOptimizeLocked(boolean)}. The interval is
* {@link #CHECK_OPTIMIZE_INTERVAL}.
*/
@GuardedBy("mReadWriteLock")
@@ -160,8 +155,10 @@ public final class AppSearchImpl {
try {
// We synchronize here because we don't want to call IcingSearchEngine.initialize() more
// than once. It's unnecessary and can be a costly operation.
IcingSearchEngineOptions options = IcingSearchEngineOptions.newBuilder()
.setBaseDir(icingDir.getAbsolutePath()).build();
IcingSearchEngineOptions options =
IcingSearchEngineOptions.newBuilder()
.setBaseDir(icingDir.getAbsolutePath())
.build();
mIcingSearchEngineLocked = new IcingSearchEngine(options);
InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
@@ -182,13 +179,17 @@ public final class AppSearchImpl {
// Populate schema map
for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
String qualifiedSchemaType = schema.getSchemaType();
addToMap(mSchemaMapLocked, getDatabaseName(qualifiedSchemaType),
addToMap(
mSchemaMapLocked,
getDatabaseName(qualifiedSchemaType),
qualifiedSchemaType);
}
// Populate namespace map
for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
addToMap(mNamespaceMapLocked, getDatabaseName(qualifiedNamespace),
addToMap(
mNamespaceMapLocked,
getDatabaseName(qualifiedNamespace),
qualifiedNamespace);
}
@@ -223,14 +224,17 @@ public final class AppSearchImpl {
*
* <p>This method belongs to mutate group.
*
* @param databaseName The name of the database where this schema lives.
* @param schemas Schemas to set for this app.
* @param databaseName The name of the database where this schema lives.
* @param schemas Schemas to set for this app.
* @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
* which do not comply with the new schema will be deleted.
* which do not comply with the new schema will be deleted.
* @throws AppSearchException on IcingSearchEngine error.
*/
public void setSchema(@NonNull String databaseName, @NonNull Set<AppSearchSchema> schemas,
boolean forceOverride) throws AppSearchException {
public void setSchema(
@NonNull String databaseName,
@NonNull Set<AppSearchSchema> schemas,
boolean forceOverride)
throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder();
@@ -243,14 +247,13 @@ public final class AppSearchImpl {
// Combine the existing schema (which may have types from other databases) with this
// database's new schema. Modifies the existingSchemaBuilder.
RewrittenSchemaResults rewrittenSchemaResults = rewriteSchema(databaseName,
existingSchemaBuilder,
newSchemaBuilder.build());
RewrittenSchemaResults rewrittenSchemaResults =
rewriteSchema(databaseName, existingSchemaBuilder, newSchemaBuilder.build());
// Apply schema
SetSchemaResultProto setSchemaResultProto =
mIcingSearchEngineLocked.setSchema(existingSchemaBuilder.build(),
forceOverride);
mIcingSearchEngineLocked.setSchema(
existingSchemaBuilder.build(), forceOverride);
// Determine whether it succeeded.
try {
@@ -259,11 +262,12 @@ public final class AppSearchImpl {
// Improve the error message by merging in information about incompatible types.
if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
|| setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0) {
String newMessage = e.getMessage()
+ "\n Deleted types: "
+ setSchemaResultProto.getDeletedSchemaTypesList()
+ "\n Incompatible types: "
+ setSchemaResultProto.getIncompatibleSchemaTypesList();
String newMessage =
e.getMessage()
+ "\n Deleted types: "
+ setSchemaResultProto.getDeletedSchemaTypesList()
+ "\n Incompatible types: "
+ setSchemaResultProto.getIncompatibleSchemaTypesList();
throw new AppSearchException(e.getResultCode(), newMessage, e.getCause());
} else {
throw e;
@@ -272,17 +276,17 @@ public final class AppSearchImpl {
// Update derived data structures.
mSchemaMapLocked.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes);
mVisibilityStoreLocked.updateSchemas(databaseName,
rewrittenSchemaResults.mDeletedQualifiedTypes);
mVisibilityStoreLocked.updateSchemas(
databaseName, rewrittenSchemaResults.mDeletedQualifiedTypes);
// Determine whether to schedule an immediate optimize.
if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
|| (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0
&& forceOverride)) {
&& forceOverride)) {
// Any existing schemas which is not in 'schemas' will be deleted, and all
// documents of these types were also deleted. And so well if we force override
// incompatible schemas.
checkForOptimizeLocked(/* force= */true);
checkForOptimizeLocked(/* force= */ true);
}
} finally {
mReadWriteLock.writeLock().unlock();
@@ -294,14 +298,12 @@ public final class AppSearchImpl {
*
* <p>This method belongs to the mutate group
*
* @param databaseName The name of the database where the
* visibility settings will apply.
* @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform
* surfaces
* @param databaseName The name of the database where the visibility settings will apply.
* @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform surfaces
* @throws AppSearchException on IcingSearchEngine error
*/
public void setVisibility(@NonNull String databaseName,
@NonNull Set<String> schemasHiddenFromPlatformSurfaces)
public void setVisibility(
@NonNull String databaseName, @NonNull Set<String> schemasHiddenFromPlatformSurfaces)
throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
@@ -311,14 +313,16 @@ public final class AppSearchImpl {
for (String schema : schemasHiddenFromPlatformSurfaces) {
Set<String> existingSchemas = mSchemaMapLocked.get(databaseName);
if (existingSchemas == null || !existingSchemas.contains(databasePrefix + schema)) {
throw new AppSearchException(AppSearchResult.RESULT_NOT_FOUND,
"Unknown schema(s): " + schemasHiddenFromPlatformSurfaces
throw new AppSearchException(
AppSearchResult.RESULT_NOT_FOUND,
"Unknown schema(s): "
+ schemasHiddenFromPlatformSurfaces
+ " provided during setVisibility.");
}
qualifiedSchemasHiddenFromPlatformSurface.add(databasePrefix + schema);
}
mVisibilityStoreLocked.setVisibility(databaseName,
qualifiedSchemasHiddenFromPlatformSurface);
mVisibilityStoreLocked.setVisibility(
databaseName, qualifiedSchemasHiddenFromPlatformSurface);
} finally {
mReadWriteLock.writeLock().lock();
}
@@ -330,13 +334,13 @@ public final class AppSearchImpl {
* <p>This method belongs to mutate group.
*
* @param databaseName The databaseName this document resides in.
* @param document The document to index.
* @param document The document to index.
* @throws AppSearchException on IcingSearchEngine error.
*/
public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document)
throws AppSearchException {
DocumentProto.Builder documentBuilder = GenericDocumentToProtoConverter.convert(
document).toBuilder();
DocumentProto.Builder documentBuilder =
GenericDocumentToProtoConverter.convert(document).toBuilder();
addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName));
PutResultProto putResultProto;
@@ -346,7 +350,7 @@ public final class AppSearchImpl {
addToMap(mNamespaceMapLocked, databaseName, documentBuilder.getNamespace());
// The existing documents with same URI will be deleted, so there maybe some resources
// could be released after optimize().
checkForOptimizeLocked(/* force= */false);
checkForOptimizeLocked(/* force= */ false);
} finally {
mReadWriteLock.writeLock().unlock();
}
@@ -359,19 +363,20 @@ public final class AppSearchImpl {
* <p>This method belongs to query group.
*
* @param databaseName The databaseName this document resides in.
* @param namespace The namespace this document resides in.
* @param uri The URI of the document to get.
* @param namespace The namespace this document resides in.
* @param uri The URI of the document to get.
* @return The Document contents
* @throws AppSearchException on IcingSearchEngine error.
*/
@NonNull
public GenericDocument getDocument(@NonNull String databaseName, @NonNull String namespace,
@NonNull String uri) throws AppSearchException {
public GenericDocument getDocument(
@NonNull String databaseName, @NonNull String namespace, @NonNull String uri)
throws AppSearchException {
GetResultProto getResultProto;
mReadWriteLock.readLock().lock();
try {
getResultProto = mIcingSearchEngineLocked.get(
getDatabasePrefix(databaseName) + namespace, uri);
getResultProto =
mIcingSearchEngineLocked.get(getDatabasePrefix(databaseName) + namespace, uri);
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -387,22 +392,22 @@ public final class AppSearchImpl {
*
* <p>This method belongs to query group.
*
* @param databaseName The databaseName this query for.
* @param databaseName The databaseName this query for.
* @param queryExpression Query String to search.
* @param searchSpec Spec for setting filters, raw query etc.
* @return The results of performing this search. It may contain an empty list of results if
* no documents matched the query.
* @param searchSpec Spec for setting filters, raw query etc.
* @return The results of performing this search. It may contain an empty list of results if no
* documents matched the query.
* @throws AppSearchException on IcingSearchEngine error.
*/
@NonNull
public SearchResultPage query(
@NonNull String databaseName,
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec) throws AppSearchException {
@NonNull SearchSpec searchSpec)
throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
return doQueryLocked(Collections.singleton(databaseName), queryExpression,
searchSpec);
return doQueryLocked(Collections.singleton(databaseName), queryExpression, searchSpec);
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -415,15 +420,15 @@ public final class AppSearchImpl {
* <p>This method belongs to query group.
*
* @param queryExpression Query String to search.
* @param searchSpec Spec for setting filters, raw query etc.
* @return The results of performing this search. It may contain an empty list of results if
* no documents matched the query.
* @param searchSpec Spec for setting filters, raw query etc.
* @return The results of performing this search. It may contain an empty list of results if no
* documents matched the query.
* @throws AppSearchException on IcingSearchEngine error.
*/
@NonNull
public SearchResultPage globalQuery(
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec) throws AppSearchException {
@NonNull String queryExpression, @NonNull SearchSpec searchSpec)
throws AppSearchException {
// TODO(b/169883602): Check if the platform is querying us at a higher level. At this
// point, we should add all platform-surfaceable schemas assuming the querier has been
// verified.
@@ -439,13 +444,13 @@ public final class AppSearchImpl {
@GuardedBy("mReadWriteLock")
private SearchResultPage doQueryLocked(
@NonNull Set<String> databases, @NonNull String queryExpression,
@NonNull Set<String> databases,
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
SearchSpecProto searchSpecProto =
SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder()
.setQuery(queryExpression);
SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
SearchSpecProto.Builder searchSpecBuilder =
searchSpecProto.toBuilder().setQuery(queryExpression);
ResultSpecProto resultSpec = SearchSpecToProtoConverter.toResultSpecProto(searchSpec);
ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
@@ -459,8 +464,8 @@ public final class AppSearchImpl {
if (!rewriteSearchSpecForDatabasesLocked(searchSpecBuilder, databases)) {
return new SearchResultPage(Bundle.EMPTY);
}
searchResultProto = mIcingSearchEngineLocked.search(
searchSpecBuilder.build(), scoringSpec, resultSpec);
searchResultProto =
mIcingSearchEngineLocked.search(searchSpecBuilder.build(), scoringSpec, resultSpec);
checkSuccess(searchResultProto.getStatus());
return rewriteSearchResultProto(searchResultProto);
@@ -477,12 +482,11 @@ public final class AppSearchImpl {
* @throws AppSearchException on IcingSearchEngine error.
*/
@NonNull
public SearchResultPage getNextPage(long nextPageToken)
throws AppSearchException {
public SearchResultPage getNextPage(long nextPageToken) throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
SearchResultProto searchResultProto = mIcingSearchEngineLocked.getNextPage(
nextPageToken);
SearchResultProto searchResultProto =
mIcingSearchEngineLocked.getNextPage(nextPageToken);
checkSuccess(searchResultProto.getStatus());
return rewriteSearchResultProto(searchResultProto);
} finally {
@@ -496,7 +500,7 @@ public final class AppSearchImpl {
* <p>This method belongs to query group.
*
* @param nextPageToken The token of pre-loaded results of previously executed query to be
* Invalidated.
* Invalidated.
*/
public void invalidateNextPageToken(long nextPageToken) {
mReadWriteLock.readLock().lock();
@@ -513,18 +517,18 @@ public final class AppSearchImpl {
* <p>This method belongs to mutate group.
*
* @param databaseName The databaseName the document is in.
* @param namespace Namespace of the document to remove.
* @param uri URI of the document to remove.
* @param namespace Namespace of the document to remove.
* @param uri URI of the document to remove.
* @throws AppSearchException on IcingSearchEngine error.
*/
public void remove(@NonNull String databaseName, @NonNull String namespace,
@NonNull String uri) throws AppSearchException {
public void remove(@NonNull String databaseName, @NonNull String namespace, @NonNull String uri)
throws AppSearchException {
String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
DeleteResultProto deleteResultProto;
mReadWriteLock.writeLock().lock();
try {
deleteResultProto = mIcingSearchEngineLocked.delete(qualifiedNamespace, uri);
checkForOptimizeLocked(/* force= */false);
checkForOptimizeLocked(/* force= */ false);
} finally {
mReadWriteLock.writeLock().unlock();
}
@@ -536,38 +540,38 @@ public final class AppSearchImpl {
*
* <p>This method belongs to mutate group.
*
* @param databaseName The databaseName the document is in.
* @param databaseName The databaseName the document is in.
* @param queryExpression Query String to search.
* @param searchSpec Defines what and how to remove
* @param searchSpec Defines what and how to remove
* @throws AppSearchException on IcingSearchEngine error.
*/
public void removeByQuery(@NonNull String databaseName, @NonNull String queryExpression,
public void removeByQuery(
@NonNull String databaseName,
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
SearchSpecProto searchSpecProto =
SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder()
.setQuery(queryExpression);
SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
SearchSpecProto.Builder searchSpecBuilder =
searchSpecProto.toBuilder().setQuery(queryExpression);
DeleteResultProto deleteResultProto;
mReadWriteLock.writeLock().lock();
try {
// Only rewrite SearchSpec for non empty database.
// rewriteSearchSpecForNonEmptyDatabase will return false for empty database, we
// should skip sending request to Icing and return in here.
if (!rewriteSearchSpecForDatabasesLocked(searchSpecBuilder,
Collections.singleton(databaseName))) {
if (!rewriteSearchSpecForDatabasesLocked(
searchSpecBuilder, Collections.singleton(databaseName))) {
return;
}
deleteResultProto = mIcingSearchEngineLocked.deleteByQuery(
searchSpecBuilder.build());
checkForOptimizeLocked(/* force= */true);
deleteResultProto = mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build());
checkForOptimizeLocked(/* force= */ true);
} finally {
mReadWriteLock.writeLock().unlock();
}
// It seems that the caller wants to get success if the data matching the query is not in
// the DB because it was not there or was successfully deleted.
checkCodeOneOf(deleteResultProto.getStatus(),
StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
checkCodeOneOf(
deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
}
/**
@@ -610,18 +614,20 @@ public final class AppSearchImpl {
* Rewrites all types mentioned in the given {@code newSchema} to prepend {@code prefix}.
* Rewritten types will be added to the {@code existingSchema}.
*
* @param databaseName The name of the database where this schema lives.
* @param databaseName The name of the database where this schema lives.
* @param existingSchema A schema that may contain existing types from across all database
* instances. Will be mutated to contain the properly rewritten schema
* types from {@code newSchema}.
* @param newSchema Schema with types to add to the {@code existingSchema}.
* instances. Will be mutated to contain the properly rewritten schema types from {@code
* newSchema}.
* @param newSchema Schema with types to add to the {@code existingSchema}.
* @return a RewrittenSchemaResults contains all qualified schema type names in the given
* database as well as a set of schema types that were deleted from the database.
* database as well as a set of schema types that were deleted from the database.
*/
@VisibleForTesting
static RewrittenSchemaResults rewriteSchema(@NonNull String databaseName,
static RewrittenSchemaResults rewriteSchema(
@NonNull String databaseName,
@NonNull SchemaProto.Builder existingSchema,
@NonNull SchemaProto newSchema) throws AppSearchException {
@NonNull SchemaProto newSchema)
throws AppSearchException {
String prefix = getDatabasePrefix(databaseName);
HashMap<String, SchemaTypeConfigProto> newTypesToProto = new HashMap<>();
// Rewrite the schema type to include the typePrefix.
@@ -640,8 +646,7 @@ public final class AppSearchImpl {
PropertyConfigProto.Builder propertyConfigBuilder =
typeConfigBuilder.getProperties(propertyIdx).toBuilder();
if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
String newPropertySchemaType =
prefix + propertyConfigBuilder.getSchemaType();
String newPropertySchemaType = prefix + propertyConfigBuilder.getSchemaType();
propertyConfigBuilder.setSchemaType(newPropertySchemaType);
typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
}
@@ -678,16 +683,15 @@ public final class AppSearchImpl {
}
/**
* Prepends {@code prefix} to all types and namespaces mentioned anywhere in
* {@code documentBuilder}.
* Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code
* documentBuilder}.
*
* @param documentBuilder The document to mutate
* @param prefix The prefix to add
* @param prefix The prefix to add
*/
@VisibleForTesting
static void addPrefixToDocument(
@NonNull DocumentProto.Builder documentBuilder,
@NonNull String prefix) {
@NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) {
// Rewrite the type name to include/remove the prefix.
String newSchema = prefix + documentBuilder.getSchema();
documentBuilder.setSchema(newSchema);
@@ -715,8 +719,8 @@ public final class AppSearchImpl {
}
/**
* Removes any database names from types and namespaces mentioned anywhere in
* {@code documentBuilder}.
* Removes any database names from types and namespaces mentioned anywhere in {@code
* documentBuilder}.
*
* @param documentBuilder The document to mutate
*/
@@ -749,8 +753,9 @@ public final class AppSearchImpl {
/**
* Rewrites the schemaTypeFilters and namespacesFilters that exist in {@code databaseNames}.
*
* <p>If the searchSpec has empty filter lists, all existing databases from
* {@code databaseNames} will be added.
* <p>If the searchSpec has empty filter lists, all existing databases from {@code
* databaseNames} will be added.
*
* <p>This method should be only called in query methods and get the READ lock to keep thread
* safety.
*
@@ -857,7 +862,8 @@ public final class AppSearchImpl {
// Add 1 to include the char size of the DATABASE_DELIMITER
return prefixedString.substring(delimiterIndex + 1);
}
throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
throw new AppSearchException(
AppSearchResult.RESULT_UNKNOWN_ERROR,
"The prefixed value doesn't contains a valid database name.");
}
@@ -865,14 +871,15 @@ public final class AppSearchImpl {
private static String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException {
int delimiterIndex = prefixedValue.indexOf(DATABASE_DELIMITER);
if (delimiterIndex == -1) {
throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
throw new AppSearchException(
AppSearchResult.RESULT_UNKNOWN_ERROR,
"The databaseName prefixed value doesn't contains a valid database name.");
}
return prefixedValue.substring(0, delimiterIndex);
}
private static void addToMap(Map<String, Set<String>> map, String databaseName,
String prefixedValue) {
private static void addToMap(
Map<String, Set<String>> map, String databaseName, String prefixedValue) {
Set<String> values = map.get(databaseName);
if (values == null) {
values = new ArraySet<>();
@@ -891,8 +898,8 @@ public final class AppSearchImpl {
}
/**
* Checks the given status code is one of the provided codes, and throws an
* {@link AppSearchException} if it is not.
* Checks the given status code is one of the provided codes, and throws an {@link
* AppSearchException} if it is not.
*/
private static void checkCodeOneOf(StatusProto statusProto, StatusProto.Code... codes)
throws AppSearchException {
@@ -918,10 +925,12 @@ public final class AppSearchImpl {
*
* <p>This method should be only called in mutate methods and get the WRITE lock to keep thread
* safety.
* <p>{@link IcingSearchEngine#optimize()} should be called only if
* {@link GetOptimizeInfoResultProto} shows there is enough resources could be released.
* <p>{@link IcingSearchEngine#getOptimizeInfo()} should be called once per
* {@link #CHECK_OPTIMIZE_INTERVAL} of remove executions.
*
* <p>{@link IcingSearchEngine#optimize()} should be called only if {@link
* GetOptimizeInfoResultProto} shows there is enough resources could be released.
*
* <p>{@link IcingSearchEngine#getOptimizeInfo()} should be called once per {@link
* #CHECK_OPTIMIZE_INTERVAL} of remove executions.
*
* @param force whether we should directly call {@link IcingSearchEngine#getOptimizeInfo()}.
*/
@@ -934,8 +943,7 @@ public final class AppSearchImpl {
checkSuccess(optimizeInfo.getStatus());
// Second threshold, decide when to call optimize().
if (optimizeInfo.getOptimizableDocs() >= OPTIMIZE_THRESHOLD_DOC_COUNT
|| optimizeInfo.getEstimatedOptimizableBytes()
>= OPTIMIZE_THRESHOLD_BYTES) {
|| optimizeInfo.getEstimatedOptimizableBytes() >= OPTIMIZE_THRESHOLD_BYTES) {
// TODO(b/155939114): call optimize in the same thread will slow down api calls
// significantly. Move this call to background.
OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize();
@@ -977,34 +985,35 @@ public final class AppSearchImpl {
}
/**
* Converts an erroneous status code to an AppSearchException. Callers should ensure that
* the status code is not OK or WARNING_DATA_LOSS.
* Converts an erroneous status code to an AppSearchException. Callers should ensure that the
* status code is not OK or WARNING_DATA_LOSS.
*
* @param statusProto StatusProto with error code and message to translate into
* AppSearchException.
* AppSearchException.
* @return AppSearchException with the parallel error code.
*/
private static AppSearchException statusProtoToAppSearchException(StatusProto statusProto) {
switch (statusProto.getCode()) {
case INVALID_ARGUMENT:
return new AppSearchException(AppSearchResult.RESULT_INVALID_ARGUMENT,
statusProto.getMessage());
return new AppSearchException(
AppSearchResult.RESULT_INVALID_ARGUMENT, statusProto.getMessage());
case NOT_FOUND:
return new AppSearchException(AppSearchResult.RESULT_NOT_FOUND,
statusProto.getMessage());
return new AppSearchException(
AppSearchResult.RESULT_NOT_FOUND, statusProto.getMessage());
case FAILED_PRECONDITION:
// Fallthrough
case ABORTED:
// Fallthrough
case INTERNAL:
return new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR,
statusProto.getMessage());
return new AppSearchException(
AppSearchResult.RESULT_INTERNAL_ERROR, statusProto.getMessage());
case OUT_OF_SPACE:
return new AppSearchException(AppSearchResult.RESULT_OUT_OF_SPACE,
statusProto.getMessage());
return new AppSearchException(
AppSearchResult.RESULT_OUT_OF_SPACE, statusProto.getMessage());
default:
// Some unknown/unsupported error
return new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
return new AppSearchException(
AppSearchResult.RESULT_UNKNOWN_ERROR,
"Unknown IcingSearchEngine status code: " + statusProto.getCode());
}
}

View File

@@ -17,13 +17,14 @@
package com.android.server.appsearch.external.localstorage;
import android.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.exceptions.AppSearchException;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.Arrays;
@@ -37,11 +38,11 @@ import java.util.Set;
*
* <p>The VisibilityStore creates a document for each database. This document holds the visibility
* settings that apply to that database. The VisibilityStore also creates a schema for these
* documents and has its own database so that its data doesn't interfere with any clients' data.
* It persists the document and schema through AppSearchImpl.
* documents and has its own database so that its data doesn't interfere with any clients' data. It
* persists the document and schema through AppSearchImpl.
*
* <p>These visibility settings are used to ensure AppSearch queries respect the clients'
* settings on who their data is visible to.
* <p>These visibility settings are used to ensure AppSearch queries respect the clients' settings
* on who their data is visible to.
*
* <p>This class doesn't handle any locking itself. Its callers should handle the locking at a
* higher level.
@@ -51,16 +52,13 @@ import java.util.Set;
*/
class VisibilityStore {
// Schema type for documents that hold AppSearch's metadata, e.g. visibility settings
@VisibleForTesting
static final String SCHEMA_TYPE = "Visibility";
@VisibleForTesting static final String SCHEMA_TYPE = "Visibility";
// Property that holds the list of platform-hidden schemas, as part of the visibility
// settings.
@VisibleForTesting
static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden";
@VisibleForTesting static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden";
// Database name to prefix all visibility schemas and documents with. Special-cased to
// minimize the chance of collision with a client-supplied database.
@VisibleForTesting
static final String DATABASE_NAME = "$$__AppSearch__Database";
@VisibleForTesting static final String DATABASE_NAME = "$$__AppSearch__Database";
// Namespace of documents that contain visibility settings
private static final String NAMESPACE = "namespace";
private final AppSearchImpl mAppSearchImpl;
@@ -92,15 +90,21 @@ class VisibilityStore {
public void initialize() throws AppSearchException {
if (!mAppSearchImpl.hasSchemaTypeLocked(DATABASE_NAME, SCHEMA_TYPE)) {
// Schema type doesn't exist yet. Add it.
mAppSearchImpl.setSchema(DATABASE_NAME,
Collections.singleton(new AppSearchSchema.Builder(SCHEMA_TYPE)
.addProperty(new AppSearchSchema.PropertyConfig.Builder(
PLATFORM_HIDDEN_PROPERTY)
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.build())
.build()),
mAppSearchImpl.setSchema(
DATABASE_NAME,
Collections.singleton(
new AppSearchSchema.Builder(SCHEMA_TYPE)
.addProperty(
new AppSearchSchema.PropertyConfig.Builder(
PLATFORM_HIDDEN_PROPERTY)
.setDataType(
AppSearchSchema.PropertyConfig
.DATA_TYPE_STRING)
.setCardinality(
AppSearchSchema.PropertyConfig
.CARDINALITY_REPEATED)
.build())
.build()),
/*forceOverride=*/ false);
}
@@ -113,8 +117,8 @@ class VisibilityStore {
try {
// Note: We use the other clients' database names as uris
GenericDocument document = mAppSearchImpl.getDocument(
DATABASE_NAME, NAMESPACE, /*uri=*/ database);
GenericDocument document =
mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ database);
String[] schemas = document.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
mPlatformHiddenMap.put(database, new ArraySet<>(Arrays.asList(schemas)));
@@ -136,22 +140,24 @@ class VisibilityStore {
*
* @param schemasToRemove Database-prefixed schemas that should be removed
*/
public void updateSchemas(@NonNull String databaseName,
@NonNull Set<String> schemasToRemove) throws AppSearchException {
public void updateSchemas(@NonNull String databaseName, @NonNull Set<String> schemasToRemove)
throws AppSearchException {
Preconditions.checkNotNull(databaseName);
Preconditions.checkNotNull(schemasToRemove);
GenericDocument visibilityDocument;
try {
visibilityDocument = mAppSearchImpl.getDocument(
DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName);
visibilityDocument =
mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName);
} catch (AppSearchException e) {
if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
// This might be the first time we're seeing visibility changes for a database.
// Create a new visibility document.
mAppSearchImpl.putDocument(DATABASE_NAME, new GenericDocument.Builder(
/*uri=*/ databaseName, SCHEMA_TYPE)
.setNamespace(NAMESPACE).build());
mAppSearchImpl.putDocument(
DATABASE_NAME,
new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
.setNamespace(NAMESPACE)
.build());
// Since we know there was nothing that existed before, we don't need to remove
// anything either. Return early.
@@ -178,12 +184,12 @@ class VisibilityStore {
// Update our persisted document
// TODO(b/171882200): Switch to a .toBuilder API when it's available.
GenericDocument.Builder newVisibilityDocument = new GenericDocument.Builder(
/*uri=*/ databaseName, SCHEMA_TYPE)
.setNamespace(NAMESPACE);
GenericDocument.Builder newVisibilityDocument =
new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
.setNamespace(NAMESPACE);
if (!remainingSchemas.isEmpty()) {
newVisibilityDocument.setPropertyString(PLATFORM_HIDDEN_PROPERTY,
remainingSchemas.toArray(new String[0]));
newVisibilityDocument.setPropertyString(
PLATFORM_HIDDEN_PROPERTY, remainingSchemas.toArray(new String[0]));
}
mAppSearchImpl.putDocument(DATABASE_NAME, newVisibilityDocument.build());
@@ -195,23 +201,24 @@ class VisibilityStore {
* Sets visibility settings for {@code databaseName}. Any previous visibility settings will be
* overwritten.
*
* @param databaseName Database name that owns the {@code platformHiddenSchemas}.
* @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from
* the platform.
* @param databaseName Database name that owns the {@code platformHiddenSchemas}.
* @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from the
* platform.
* @throws AppSearchException on AppSearchImpl error.
*/
public void setVisibility(@NonNull String databaseName,
@NonNull Set<String> platformHiddenSchemas) throws AppSearchException {
public void setVisibility(
@NonNull String databaseName, @NonNull Set<String> platformHiddenSchemas)
throws AppSearchException {
Preconditions.checkNotNull(databaseName);
Preconditions.checkNotNull(platformHiddenSchemas);
// Persist the document
GenericDocument.Builder visibilityDocument = new GenericDocument.Builder(
/*uri=*/ databaseName, SCHEMA_TYPE)
.setNamespace(NAMESPACE);
GenericDocument.Builder visibilityDocument =
new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
.setNamespace(NAMESPACE);
if (!platformHiddenSchemas.isEmpty()) {
visibilityDocument.setPropertyString(PLATFORM_HIDDEN_PROPERTY,
platformHiddenSchemas.toArray(new String[0]));
visibilityDocument.setPropertyString(
PLATFORM_HIDDEN_PROPERTY, platformHiddenSchemas.toArray(new String[0]));
}
mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build());
@@ -225,7 +232,7 @@ class VisibilityStore {
*
* @param databaseName Database name to retrieve schemas for
* @return Set of database-qualified schemas that are hidden from the platform. Empty set if
* none exist.
* none exist.
*/
@NonNull
public Set<String> getPlatformHiddenSchemas(@NonNull String databaseName) {

View File

@@ -17,8 +17,8 @@
package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull;
import android.app.appsearch.GenericDocument;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.DocumentProto;
@@ -30,9 +30,9 @@ import java.util.Collections;
/**
* Translates a {@link GenericDocument} into a {@link DocumentProto}.
*
* @hide
*/
public final class GenericDocumentToProtoConverter {
private GenericDocumentToProtoConverter() {}
@@ -42,7 +42,8 @@ public final class GenericDocumentToProtoConverter {
public static DocumentProto convert(@NonNull GenericDocument document) {
Preconditions.checkNotNull(document);
DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
mProtoBuilder.setUri(document.getUri())
mProtoBuilder
.setUri(document.getUri())
.setSchema(document.getSchemaType())
.setNamespace(document.getNamespace())
.setScore(document.getScore())

View File

@@ -17,8 +17,8 @@
package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchSchema;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.PropertyConfigProto;
@@ -30,15 +30,15 @@ import java.util.List;
/**
* 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}.
* Converts an {@link android.app.appsearch.AppSearchSchema} into a {@link
* SchemaTypeConfigProto}.
*/
@NonNull
public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) {
@@ -57,8 +57,8 @@ public final class SchemaToProtoConverter {
private static PropertyConfigProto convertProperty(
@NonNull AppSearchSchema.PropertyConfig property) {
Preconditions.checkNotNull(property);
PropertyConfigProto.Builder propertyConfigProto = PropertyConfigProto.newBuilder()
.setPropertyName(property.getName());
PropertyConfigProto.Builder propertyConfigProto =
PropertyConfigProto.newBuilder().setPropertyName(property.getName());
StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder();
// Set dataType
@@ -104,8 +104,8 @@ public final class SchemaToProtoConverter {
indexingConfig.setTermMatchType(termMatchTypeProto);
// Set tokenizerType
@AppSearchSchema.PropertyConfig.TokenizerType int tokenizerType =
property.getTokenizerType();
@AppSearchSchema.PropertyConfig.TokenizerType
int tokenizerType = property.getTokenizerType();
StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
if (tokenizerTypeProto == null) {

View File

@@ -16,13 +16,11 @@
package com.android.server.appsearch.external.localstorage.converter;
import android.os.Bundle;
import android.annotation.NonNull;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResultPage;
import android.os.Bundle;
import com.google.android.icing.proto.SearchResultProto;
import com.google.android.icing.proto.SearchResultProtoOrBuilder;
@@ -36,7 +34,6 @@ import java.util.ArrayList;
*
* @hide
*/
public class SearchResultToProtoConverter {
private SearchResultToProtoConverter() {}
@@ -67,8 +64,9 @@ public class SearchResultToProtoConverter {
for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) {
SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i);
for (int j = 0; j < entry.getSnippetMatchesCount(); j++) {
Bundle matchInfoBundle = convertToMatchInfoBundle(
entry.getSnippetMatches(j), entry.getPropertyName());
Bundle matchInfoBundle =
convertToMatchInfoBundle(
entry.getSnippetMatches(j), entry.getPropertyName());
matchList.add(matchInfoBundle);
}
}

View File

@@ -17,8 +17,8 @@
package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull;
import android.app.appsearch.SearchSpec;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.ResultSpecProto;
@@ -28,9 +28,9 @@ import com.google.android.icing.proto.TermMatchType;
/**
* Translates a {@link SearchSpec} into icing search protos.
*
* @hide
*/
public final class SearchSpecToProtoConverter {
private SearchSpecToProtoConverter() {}
@@ -38,9 +38,10 @@ public final class SearchSpecToProtoConverter {
@NonNull
public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
Preconditions.checkNotNull(spec);
SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder()
.addAllSchemaTypeFilters(spec.getSchemaTypes())
.addAllNamespaceFilters(spec.getNamespaces());
SearchSpecProto.Builder protoBuilder =
SearchSpecProto.newBuilder()
.addAllSchemaTypeFilters(spec.getSchemaTypes())
.addAllNamespaceFilters(spec.getNamespaces());
@SearchSpec.TermMatch int termMatchCode = spec.getTermMatch();
TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode);
@@ -84,8 +85,8 @@ public final class SearchSpecToProtoConverter {
ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategyCode);
if (rankingStrategyCodeProto == null) {
throw new IllegalArgumentException("Invalid result ranking strategy: "
+ rankingStrategyCode);
throw new IllegalArgumentException(
"Invalid result ranking strategy: " + rankingStrategyCode);
}
protoBuilder.setRankBy(rankingStrategyCodeProto);

View File

@@ -24,16 +24,18 @@ public class AppSearchEmailTest {
@Test
public void testBuildEmailAndGetValue() {
AppSearchEmail email = new AppSearchEmail.Builder("uri")
.setFrom("FakeFromAddress")
.setCc("CC1", "CC2")
// Score and Property are mixed into the middle to make sure DocumentBuilder's
// methods can be interleaved with EmailBuilder's methods.
.setScore(1)
.setPropertyString("propertyKey", "propertyValue1", "propertyValue2")
.setSubject("subject")
.setBody("EmailBody")
.build();
AppSearchEmail email =
new AppSearchEmail.Builder("uri")
.setFrom("FakeFromAddress")
.setCc("CC1", "CC2")
// Score and Property are mixed into the middle to make sure
// DocumentBuilder's
// methods can be interleaved with EmailBuilder's methods.
.setScore(1)
.setPropertyString("propertyKey", "propertyValue1", "propertyValue2")
.setSubject("subject")
.setBody("EmailBody")
.build();
assertThat(email.getUri()).isEqualTo("uri");
assertThat(email.getFrom()).isEqualTo("FakeFromAddress");
@@ -42,8 +44,9 @@ public class AppSearchEmailTest {
assertThat(email.getBcc()).isNull();
assertThat(email.getScore()).isEqualTo(1);
assertThat(email.getPropertyString("propertyKey")).isEqualTo("propertyValue1");
assertThat(email.getPropertyStringArray("propertyKey")).asList().containsExactly(
"propertyValue1", "propertyValue2");
assertThat(email.getPropertyStringArray("propertyKey"))
.asList()
.containsExactly("propertyValue1", "propertyValue2");
assertThat(email.getSubject()).isEqualTo("subject");
assertThat(email.getBody()).isEqualTo("EmailBody");
}

View File

@@ -25,25 +25,26 @@ import org.junit.Test;
public class SearchSpecTest {
@Test
public void testGetBundle() {
SearchSpec searchSpec = new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addNamespace("namespace1", "namespace2")
.addSchemaType("schemaTypes1", "schemaTypes2")
.setSnippetCount(5)
.setSnippetCountPerProperty(10)
.setMaxSnippetSize(15)
.setResultCountPerPage(42)
.setOrder(SearchSpec.ORDER_ASCENDING)
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
.build();
SearchSpec searchSpec =
new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addNamespace("namespace1", "namespace2")
.addSchemaType("schemaTypes1", "schemaTypes2")
.setSnippetCount(5)
.setSnippetCountPerProperty(10)
.setMaxSnippetSize(15)
.setResultCountPerPage(42)
.setOrder(SearchSpec.ORDER_ASCENDING)
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
.build();
Bundle bundle = searchSpec.getBundle();
assertThat(bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD))
.isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD)).containsExactly(
"namespace1", "namespace2");
assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD)).containsExactly(
"schemaTypes1", "schemaTypes2");
assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD))
.containsExactly("namespace1", "namespace2");
assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD))
.containsExactly("schemaTypes1", "schemaTypes2");
assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)).isEqualTo(5);
assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)).isEqualTo(10);
assertThat(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)).isEqualTo(15);

View File

@@ -33,11 +33,11 @@ public class AppSearchResultCtsTest {
assertThat(result1.hashCode()).isEqualTo(result2.hashCode());
AppSearchResult<String> result3 =
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
"errorMessage");
AppSearchResult.newFailedResult(
AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage");
AppSearchResult<String> result4 =
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
"errorMessage");
AppSearchResult.newFailedResult(
AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage");
assertThat(result3).isEqualTo(result4);
assertThat(result3.hashCode()).isEqualTo(result4.hashCode());
@@ -47,7 +47,7 @@ public class AppSearchResultCtsTest {
public void testResultEquals_failure() {
AppSearchResult<String> result1 = AppSearchResult.newSuccessfulResult("String");
AppSearchResult<String> result2 = AppSearchResult.newSuccessfulResult("Wrong");
AppSearchResult<String> resultNull = AppSearchResult.newSuccessfulResult(/*value=*/null);
AppSearchResult<String> resultNull = AppSearchResult.newSuccessfulResult(/*value=*/ null);
assertThat(result1).isNotEqualTo(result2);
assertThat(result1.hashCode()).isNotEqualTo(result2.hashCode());
@@ -55,26 +55,23 @@ public class AppSearchResultCtsTest {
assertThat(result1.hashCode()).isNotEqualTo(resultNull.hashCode());
AppSearchResult<String> result3 =
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
"errorMessage");
AppSearchResult.newFailedResult(
AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage");
AppSearchResult<String> result4 =
AppSearchResult.newFailedResult(AppSearchResult.RESULT_IO_ERROR,
"errorMessage");
AppSearchResult.newFailedResult(AppSearchResult.RESULT_IO_ERROR, "errorMessage");
assertThat(result3).isNotEqualTo(result4);
assertThat(result3.hashCode()).isNotEqualTo(result4.hashCode());
AppSearchResult<String> result5 =
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
"Wrong");
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR, "Wrong");
assertThat(result3).isNotEqualTo(result5);
assertThat(result3.hashCode()).isNotEqualTo(result5.hashCode());
AppSearchResult<String> result6 =
AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR,
/*errorMessage=*/null);
AppSearchResult.newFailedResult(
AppSearchResult.RESULT_INTERNAL_ERROR, /*errorMessage=*/ null);
assertThat(result3).isNotEqualTo(result6);
assertThat(result3.hashCode()).isNotEqualTo(result6.hashCode());

View File

@@ -16,7 +16,6 @@
package android.app.appsearch.cts;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.expectThrows;
@@ -55,21 +54,28 @@ public class AppSearchSchemaCtsTest {
@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()));
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");
}
}

View File

@@ -25,114 +25,131 @@ import android.app.appsearch.GenericDocument;
import org.junit.Test;
public class GenericDocumentCtsTest {
private static final byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
private static final GenericDocument sDocumentProperties1 = new GenericDocument
.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
.setCreationTimestampMillis(12345L)
.build();
private static final GenericDocument sDocumentProperties2 = new GenericDocument
.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.setCreationTimestampMillis(6789L)
.build();
private static final byte[] sByteArray1 = new byte[] {(byte) 1, (byte) 2, (byte) 3};
private static final byte[] sByteArray2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7};
private static final GenericDocument sDocumentProperties1 =
new GenericDocument.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
.setCreationTimestampMillis(12345L)
.build();
private static final GenericDocument sDocumentProperties2 =
new GenericDocument.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.setCreationTimestampMillis(6789L)
.build();
@Test
public void testDocumentEquals_identical() {
GenericDocument document1 = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
GenericDocument document2 = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
GenericDocument document1 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
GenericDocument document2 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
assertThat(document1).isEqualTo(document2);
assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
}
@Test
public void testDocumentEquals_differentOrder() {
GenericDocument document1 = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
.setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
.build();
GenericDocument document1 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.build();
// Create second document with same parameter but different order.
GenericDocument document2 = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
.setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.build();
GenericDocument document2 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.build();
assertThat(document1).isEqualTo(document2);
assertThat(document1.hashCode()).isEqualTo(document2.hashCode());
}
@Test
public void testDocumentEquals_failure() {
GenericDocument document1 = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.build();
GenericDocument document1 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.build();
// Create second document with same order but different value.
GenericDocument document2 = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 4L) // Different
.build();
GenericDocument document2 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 4L) // Different
.build();
assertThat(document1).isNotEqualTo(document2);
assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode());
}
@Test
public void testDocumentEquals_repeatedFieldOrder_failure() {
GenericDocument document1 = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyBoolean("booleanKey1", true, false, true)
.build();
GenericDocument document1 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyBoolean("booleanKey1", true, false, true)
.build();
// Create second document with same order but different value.
GenericDocument document2 = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyBoolean("booleanKey1", true, true, false) // Different
.build();
GenericDocument document2 =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyBoolean("booleanKey1", true, true, false) // Different
.build();
assertThat(document1).isNotEqualTo(document2);
assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode());
}
@Test
public void testDocumentGetSingleValue() {
GenericDocument document = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setScore(1)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L)
.setPropertyDouble("doubleKey1", 1.0)
.setPropertyBoolean("booleanKey1", true)
.setPropertyString("stringKey1", "test-value1")
.setPropertyBytes("byteKey1", sByteArray1)
.setPropertyDocument("documentKey1", sDocumentProperties1)
.build();
GenericDocument document =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setScore(1)
.setTtlMillis(1L)
.setPropertyLong("longKey1", 1L)
.setPropertyDouble("doubleKey1", 1.0)
.setPropertyBoolean("booleanKey1", true)
.setPropertyString("stringKey1", "test-value1")
.setPropertyBytes("byteKey1", sByteArray1)
.setPropertyDocument("documentKey1", sDocumentProperties1)
.build();
assertThat(document.getUri()).isEqualTo("uri1");
assertThat(document.getTtlMillis()).isEqualTo(1L);
assertThat(document.getSchemaType()).isEqualTo("schemaType1");
@@ -143,88 +160,102 @@ public class GenericDocumentCtsTest {
assertThat(document.getPropertyBoolean("booleanKey1")).isTrue();
assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
assertThat(document.getPropertyBytes("byteKey1"))
.asList().containsExactly((byte) 1, (byte) 2, (byte) 3);
.asList()
.containsExactly((byte) 1, (byte) 2, (byte) 3);
assertThat(document.getPropertyDocument("documentKey1")).isEqualTo(sDocumentProperties1);
}
@Test
public void testDocumentGetArrayValues() {
GenericDocument document = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
GenericDocument document =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
assertThat(document.getUri()).isEqualTo("uri1");
assertThat(document.getSchemaType()).isEqualTo("schemaType1");
assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L, 2L, 3L);
assertThat(document.getPropertyDoubleArray("doubleKey1")).usingExactEquality()
assertThat(document.getPropertyDoubleArray("doubleKey1"))
.usingExactEquality()
.containsExactly(1.0, 2.0, 3.0);
assertThat(document.getPropertyBooleanArray("booleanKey1")).asList()
assertThat(document.getPropertyBooleanArray("booleanKey1"))
.asList()
.containsExactly(true, false, true);
assertThat(document.getPropertyStringArray("stringKey1")).asList()
assertThat(document.getPropertyStringArray("stringKey1"))
.asList()
.containsExactly("test-value1", "test-value2", "test-value3");
assertThat(document.getPropertyBytesArray("byteKey1")).asList()
assertThat(document.getPropertyBytesArray("byteKey1"))
.asList()
.containsExactly(sByteArray1, sByteArray2);
assertThat(document.getPropertyDocumentArray("documentKey1")).asList()
assertThat(document.getPropertyDocumentArray("documentKey1"))
.asList()
.containsExactly(sDocumentProperties1, sDocumentProperties2);
}
@Test
public void testDocument_toString() {
GenericDocument document = new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString("stringKey1", "String1", "String2", "String3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
String exceptedString = "{ key: 'creationTimestampMillis' value: 5 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: "
+ "{ key: 'booleanKey1' value: [ 'true' 'false' 'true' ] } "
+ "{ key: 'byteKey1' value: "
+ "{ key: 'byteArray' value: [ '1' '2' '3' ] } "
+ "{ key: 'byteArray' value: [ '4' '5' '6' '7' ] } } "
+ "{ key: 'documentKey1' value: [ '"
+ "{ key: 'creationTimestampMillis' value: 12345 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: } "
+ "{ key: 'schemaType' value: sDocumentPropertiesSchemaType1 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: sDocumentProperties1 } ' '"
+ "{ key: 'creationTimestampMillis' value: 6789 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: } "
+ "{ key: 'schemaType' value: sDocumentPropertiesSchemaType2 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: sDocumentProperties2 } ' ] } "
+ "{ key: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } "
+ "{ key: 'longKey1' value: [ '1' '2' '3' ] } "
+ "{ key: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] } } "
+ "{ key: 'schemaType' value: schemaType1 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: uri1 } ";
GenericDocument document =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setCreationTimestampMillis(5L)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString("stringKey1", "String1", "String2", "String3")
.setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
.setPropertyDocument(
"documentKey1", sDocumentProperties1, sDocumentProperties2)
.build();
String exceptedString =
"{ key: 'creationTimestampMillis' value: 5 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: "
+ "{ key: 'booleanKey1' value: [ 'true' 'false' 'true' ] } "
+ "{ key: 'byteKey1' value: "
+ "{ key: 'byteArray' value: [ '1' '2' '3' ] } "
+ "{ key: 'byteArray' value: [ '4' '5' '6' '7' ] } } "
+ "{ key: 'documentKey1' value: [ '"
+ "{ key: 'creationTimestampMillis' value: 12345 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: } "
+ "{ key: 'schemaType' value: sDocumentPropertiesSchemaType1 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: sDocumentProperties1 } ' '"
+ "{ key: 'creationTimestampMillis' value: 6789 } "
+ "{ key: 'namespace' value: } "
+ "{ key: 'properties' value: } "
+ "{ key: 'schemaType' value: sDocumentPropertiesSchemaType2 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: sDocumentProperties2 } ' ] } "
+ "{ key: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } "
+ "{ key: 'longKey1' value: [ '1' '2' '3' ] } "
+ "{ key: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] } } "
+ "{ key: 'schemaType' value: schemaType1 } "
+ "{ key: 'score' value: 0 } "
+ "{ key: 'ttlMillis' value: 0 } "
+ "{ key: 'uri' value: uri1 } ";
assertThat(document.toString()).isEqualTo(exceptedString);
}
@Test
public void testDocumentGetValues_differentTypes() {
GenericDocument document = new GenericDocument.Builder<>("uri1", "schemaType1")
.setScore(1)
.setPropertyLong("longKey1", 1L)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
.build();
GenericDocument document =
new GenericDocument.Builder<>("uri1", "schemaType1")
.setScore(1)
.setPropertyLong("longKey1", 1L)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.build();
// Get a value for a key that doesn't exist
assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(0.0);
@@ -236,7 +267,8 @@ public class GenericDocumentCtsTest {
// Get a value with multiple elements as an array and as a single value
assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1");
assertThat(document.getPropertyStringArray("stringKey1")).asList()
assertThat(document.getPropertyStringArray("stringKey1"))
.asList()
.containsExactly("test-value1", "test-value2", "test-value3");
// Get a value of the wrong type
@@ -249,6 +281,6 @@ public class GenericDocumentCtsTest {
GenericDocument.Builder<?> builder = new GenericDocument.Builder<>("uri1", "schemaType1");
expectThrows(
IllegalArgumentException.class,
() -> builder.setPropertyBoolean("test", new boolean[]{}));
() -> builder.setPropertyBoolean("test", new boolean[] {}));
}
}

View File

@@ -27,31 +27,35 @@ import org.junit.Test;
public class SearchSpecCtsTest {
@Test
public void buildSearchSpecWithoutTermMatchType() {
RuntimeException e = expectThrows(RuntimeException.class, () -> new SearchSpec.Builder()
.addSchemaType("testSchemaType")
.build());
RuntimeException e =
expectThrows(
RuntimeException.class,
() -> new SearchSpec.Builder().addSchemaType("testSchemaType").build());
assertThat(e).hasMessageThat().contains("Missing termMatchType field");
}
@Test
public void testBuildSearchSpec() {
SearchSpec searchSpec = new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addNamespace("namespace1", "namespace2")
.addSchemaType("schemaTypes1", "schemaTypes2")
.setSnippetCount(5)
.setSnippetCountPerProperty(10)
.setMaxSnippetSize(15)
.setResultCountPerPage(42)
.setOrder(SearchSpec.ORDER_ASCENDING)
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
.build();
SearchSpec searchSpec =
new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addNamespace("namespace1", "namespace2")
.addSchemaType("schemaTypes1", "schemaTypes2")
.setSnippetCount(5)
.setSnippetCountPerProperty(10)
.setMaxSnippetSize(15)
.setResultCountPerPage(42)
.setOrder(SearchSpec.ORDER_ASCENDING)
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
.build();
assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
assertThat(searchSpec.getNamespaces())
.containsExactly("namespace1", "namespace2").inOrder();
.containsExactly("namespace1", "namespace2")
.inOrder();
assertThat(searchSpec.getSchemaTypes())
.containsExactly("schemaTypes1", "schemaTypes2").inOrder();
.containsExactly("schemaTypes1", "schemaTypes2")
.inOrder();
assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);

View File

@@ -32,50 +32,58 @@ import org.junit.Test;
*/
public class CustomerDocumentTest {
private static final byte[] BYTE_ARRAY1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
private static final byte[] BYTE_ARRAY2 = new byte[]{(byte) 4, (byte) 5, (byte) 6};
private static final GenericDocument DOCUMENT_PROPERTIES1 = new GenericDocument
.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
.build();
private static final GenericDocument DOCUMENT_PROPERTIES2 = new GenericDocument
.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.build();
private static final byte[] BYTE_ARRAY1 = new byte[] {(byte) 1, (byte) 2, (byte) 3};
private static final byte[] BYTE_ARRAY2 = new byte[] {(byte) 4, (byte) 5, (byte) 6};
private static final GenericDocument DOCUMENT_PROPERTIES1 =
new GenericDocument.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1")
.build();
private static final GenericDocument DOCUMENT_PROPERTIES2 =
new GenericDocument.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.build();
@Test
public void testBuildCustomerDocument() {
CustomerDocument customerDocument = new CustomerDocument.Builder("uri1")
.setScore(1)
.setCreationTimestampMillis(0)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", BYTE_ARRAY1, BYTE_ARRAY2)
.setPropertyDocument("documentKey1", DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2)
.build();
CustomerDocument customerDocument =
new CustomerDocument.Builder("uri1")
.setScore(1)
.setCreationTimestampMillis(0)
.setPropertyLong("longKey1", 1L, 2L, 3L)
.setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
.setPropertyBoolean("booleanKey1", true, false, true)
.setPropertyString(
"stringKey1", "test-value1", "test-value2", "test-value3")
.setPropertyBytes("byteKey1", BYTE_ARRAY1, BYTE_ARRAY2)
.setPropertyDocument(
"documentKey1", DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2)
.build();
assertThat(customerDocument.getUri()).isEqualTo("uri1");
assertThat(customerDocument.getSchemaType()).isEqualTo("customerDocument");
assertThat(customerDocument.getScore()).isEqualTo(1);
assertThat(customerDocument.getCreationTimestampMillis()).isEqualTo(0L);
assertThat(customerDocument.getPropertyLongArray("longKey1")).asList()
assertThat(customerDocument.getPropertyLongArray("longKey1"))
.asList()
.containsExactly(1L, 2L, 3L);
assertThat(customerDocument.getPropertyDoubleArray("doubleKey1")).usingExactEquality()
assertThat(customerDocument.getPropertyDoubleArray("doubleKey1"))
.usingExactEquality()
.containsExactly(1.0, 2.0, 3.0);
assertThat(customerDocument.getPropertyBooleanArray("booleanKey1")).asList()
assertThat(customerDocument.getPropertyBooleanArray("booleanKey1"))
.asList()
.containsExactly(true, false, true);
assertThat(customerDocument.getPropertyStringArray("stringKey1")).asList()
assertThat(customerDocument.getPropertyStringArray("stringKey1"))
.asList()
.containsExactly("test-value1", "test-value2", "test-value3");
assertThat(customerDocument.getPropertyBytesArray("byteKey1")).asList()
assertThat(customerDocument.getPropertyBytesArray("byteKey1"))
.asList()
.containsExactly(BYTE_ARRAY1, BYTE_ARRAY2);
assertThat(customerDocument.getPropertyDocumentArray("documentKey1")).asList()
assertThat(customerDocument.getPropertyDocumentArray("documentKey1"))
.asList()
.containsExactly(DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2);
}
/**
* An example document type for test purposes, defined outside of
* {@link GenericDocument} (the way an external developer would define
* it).
* An example document type for test purposes, defined outside of {@link GenericDocument} (the
* way an external developer would define it).
*/
private static class CustomerDocument extends GenericDocument {
private CustomerDocument(GenericDocument document) {

View File

@@ -25,8 +25,8 @@ import android.app.appsearch.GenericDocument;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.exceptions.AppSearchException;
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
import com.android.server.appsearch.proto.DocumentProto;
import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
import com.android.server.appsearch.proto.PropertyConfigProto;
@@ -36,6 +36,7 @@ import com.android.server.appsearch.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.proto.SearchSpecProto;
import com.android.server.appsearch.proto.StringIndexingConfig;
import com.android.server.appsearch.proto.TermMatchType;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
@@ -50,8 +51,7 @@ import java.util.List;
import java.util.Set;
public class AppSearchImplTest {
@Rule
public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private AppSearchImpl mAppSearchImpl;
private SchemaTypeConfigProto mVisibilitySchemaProto;
@@ -61,13 +61,17 @@ public class AppSearchImplTest {
AppSearchSchema visibilityAppSearchSchema =
new AppSearchSchema.Builder(
VisibilityStore.DATABASE_NAME + AppSearchImpl.DATABASE_DELIMITER
+ VisibilityStore.SCHEMA_TYPE)
.addProperty(new AppSearchSchema.PropertyConfig.Builder(
VisibilityStore.PLATFORM_HIDDEN_PROPERTY)
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.build())
VisibilityStore.DATABASE_NAME
+ AppSearchImpl.DATABASE_DELIMITER
+ VisibilityStore.SCHEMA_TYPE)
.addProperty(
new AppSearchSchema.PropertyConfig.Builder(
VisibilityStore.PLATFORM_HIDDEN_PROPERTY)
.setDataType(
AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.build())
.build();
mVisibilitySchemaProto = SchemaToProtoConverter.convert(visibilityAppSearchSchema);
}
@@ -79,92 +83,132 @@ public class AppSearchImplTest {
*/
@Test
public void testRewriteSchema_addType() throws Exception {
SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("existingDatabase/Foo").build());
SchemaProto.Builder existingSchemaBuilder =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("existingDatabase/Foo")
.build());
// Create a copy so we can modify it.
List<SchemaTypeConfigProto> existingTypes =
new ArrayList<>(existingSchemaBuilder.getTypesList());
SchemaProto newSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("Foo").build())
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("TestType")
.addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setStringIndexingConfig(StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
.build()
).build()
).addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("link")
.setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setSchemaType("RefType")
.build()
).build()
).build();
SchemaProto newSchema =
SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build())
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("TestType")
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(
PropertyConfigProto.DataType.Code
.STRING)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig
.TokenizerType
.Code.PLAIN)
.setTermMatchType(
TermMatchType.Code
.PREFIX)
.build())
.build())
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("link")
.setDataType(
PropertyConfigProto.DataType.Code
.DOCUMENT)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setSchemaType("RefType")
.build())
.build())
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
"newDatabase", existingSchemaBuilder,
newSchema);
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema("newDatabase", existingSchemaBuilder, newSchema);
// We rewrote all the new types that were added. And nothing was removed.
assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
.containsExactly("newDatabase/Foo", "newDatabase/TestType");
assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
SchemaProto expectedSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("newDatabase/Foo").build())
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("newDatabase/TestType")
.addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setStringIndexingConfig(StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
.build()
).build()
).addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("link")
.setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setSchemaType("newDatabase/RefType")
.build()
).build())
.build();
SchemaProto expectedSchema =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("newDatabase/Foo")
.build())
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("newDatabase/TestType")
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(
PropertyConfigProto.DataType.Code
.STRING)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig
.TokenizerType
.Code.PLAIN)
.setTermMatchType(
TermMatchType.Code
.PREFIX)
.build())
.build())
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("link")
.setDataType(
PropertyConfigProto.DataType.Code
.DOCUMENT)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setSchemaType("newDatabase/RefType")
.build())
.build())
.build();
existingTypes.addAll(expectedSchema.getTypesList());
assertThat(existingSchemaBuilder.getTypesList()).containsExactlyElementsIn(existingTypes);
}
/**
* Ensure that we track all types that were rewritten in the input schema. Even if they were
* not technically "added" to the existing schema.
* Ensure that we track all types that were rewritten in the input schema. Even if they were not
* technically "added" to the existing schema.
*/
@Test
public void testRewriteSchema_rewriteType() throws Exception {
SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("existingDatabase/Foo").build());
SchemaProto.Builder existingSchemaBuilder =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("existingDatabase/Foo")
.build());
SchemaProto newSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("Foo").build())
.build();
SchemaProto newSchema =
SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build())
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
"existingDatabase", existingSchemaBuilder, newSchema);
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema("existingDatabase", existingSchemaBuilder, newSchema);
// Nothing was removed, but the method did rewrite the type name.
assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
@@ -183,17 +227,20 @@ public class AppSearchImplTest {
*/
@Test
public void testRewriteSchema_deleteType() throws Exception {
SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("existingDatabase/Foo").build());
SchemaProto.Builder existingSchemaBuilder =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("existingDatabase/Foo")
.build());
SchemaProto newSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("Bar").build())
.build();
SchemaProto newSchema =
SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Bar").build())
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
"existingDatabase", existingSchemaBuilder, newSchema);
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema("existingDatabase", existingSchemaBuilder, newSchema);
// Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
// new schema.
@@ -203,10 +250,13 @@ public class AppSearchImplTest {
.containsExactly("existingDatabase/Foo");
// Same schema since nothing was added.
SchemaProto expectedSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
.setSchemaType("existingDatabase/Bar").build())
.build();
SchemaProto expectedSchema =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("existingDatabase/Bar")
.build())
.build();
assertThat(existingSchemaBuilder.getTypesList())
.containsExactlyElementsIn(expectedSchema.getTypesList());
@@ -214,29 +264,35 @@ public class AppSearchImplTest {
@Test
public void testAddDocumentTypePrefix() {
DocumentProto insideDocument = DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("type")
.setNamespace("namespace")
.build();
DocumentProto documentProto = DocumentProto.newBuilder()
.setUri("uri")
.setSchema("type")
.setNamespace("namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
.build();
DocumentProto insideDocument =
DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("type")
.setNamespace("namespace")
.build();
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri("uri")
.setSchema("type")
.setNamespace("namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
.build();
DocumentProto expectedInsideDocument = DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("databaseName/type")
.setNamespace("databaseName/namespace")
.build();
DocumentProto expectedDocumentProto = DocumentProto.newBuilder()
.setUri("uri")
.setSchema("databaseName/type")
.setNamespace("databaseName/namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(expectedInsideDocument))
.build();
DocumentProto expectedInsideDocument =
DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("databaseName/type")
.setNamespace("databaseName/namespace")
.build();
DocumentProto expectedDocumentProto =
DocumentProto.newBuilder()
.setUri("uri")
.setSchema("databaseName/type")
.setNamespace("databaseName/namespace")
.addProperties(
PropertyProto.newBuilder()
.addDocumentValues(expectedInsideDocument))
.build();
DocumentProto.Builder actualDocument = documentProto.toBuilder();
mAppSearchImpl.addPrefixToDocument(actualDocument, "databaseName/");
@@ -245,30 +301,36 @@ public class AppSearchImplTest {
@Test
public void testRemoveDocumentTypePrefixes() throws Exception {
DocumentProto insideDocument = DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("databaseName1/type")
.setNamespace("databaseName2/namespace")
.build();
DocumentProto documentProto = DocumentProto.newBuilder()
.setUri("uri")
.setSchema("databaseName2/type")
.setNamespace("databaseName3/namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
.build();
DocumentProto insideDocument =
DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("databaseName1/type")
.setNamespace("databaseName2/namespace")
.build();
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri("uri")
.setSchema("databaseName2/type")
.setNamespace("databaseName3/namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
.build();
DocumentProto expectedInsideDocument = DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("type")
.setNamespace("namespace")
.build();
DocumentProto expectedInsideDocument =
DocumentProto.newBuilder()
.setUri("inside-uri")
.setSchema("type")
.setNamespace("namespace")
.build();
// Since we don't pass in "databaseName3/" as a prefix to remove, it stays on the Document.
DocumentProto expectedDocumentProto = DocumentProto.newBuilder()
.setUri("uri")
.setSchema("type")
.setNamespace("namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(expectedInsideDocument))
.build();
DocumentProto expectedDocumentProto =
DocumentProto.newBuilder()
.setUri("uri")
.setSchema("type")
.setNamespace("namespace")
.addProperties(
PropertyProto.newBuilder()
.addDocumentValues(expectedInsideDocument))
.build();
DocumentProto.Builder actualDocument = documentProto.toBuilder();
mAppSearchImpl.removeDatabasesFromDocument(actualDocument);
@@ -280,14 +342,18 @@ public class AppSearchImplTest {
// Insert schema
Set<AppSearchSchema> schemas =
Collections.singleton(new AppSearchSchema.Builder("type").build());
mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/false);
mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false);
// Insert enough documents.
for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) {
for (int i = 0;
i
< AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
i++) {
GenericDocument document =
new GenericDocument.Builder("uri" + i, "type").setNamespace(
"namespace").build();
new GenericDocument.Builder("uri" + i, "type")
.setNamespace("namespace")
.build();
mAppSearchImpl.putDocument("database", document);
}
@@ -308,8 +374,10 @@ public class AppSearchImplTest {
// Keep delete docs, will reach the interval this time and trigger optimize().
for (int i = AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT;
i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) {
i
< AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
i++) {
mAppSearchImpl.remove("database", "namespace", "uri" + i);
}
@@ -321,64 +389,61 @@ public class AppSearchImplTest {
@Test
public void testRewriteSearchSpec_oneInstance() throws Exception {
SearchSpecProto.Builder searchSpecProto =
SearchSpecProto.newBuilder().setQuery("");
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
// Insert schema
Set<AppSearchSchema> schemas =
Collections.singleton(new AppSearchSchema.Builder("type").build());
mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/false);
mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false);
// Insert document
GenericDocument document = new GenericDocument.Builder("uri", "type").setNamespace(
"namespace").build();
GenericDocument document =
new GenericDocument.Builder("uri", "type").setNamespace("namespace").build();
mAppSearchImpl.putDocument("database", document);
// Rewrite SearchSpec
mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(searchSpecProto,
Collections.singleton(
"database"));
mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(
searchSpecProto, Collections.singleton("database"));
assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly("database/type");
assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly("database/namespace");
}
@Test
public void testRewriteSearchSpec_twoInstances() throws Exception {
SearchSpecProto.Builder searchSpecProto =
SearchSpecProto.newBuilder().setQuery("");
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
// Insert schema
Set<AppSearchSchema> schemas = Set.of(
new AppSearchSchema.Builder("typeA").build(),
new AppSearchSchema.Builder("typeB").build());
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/false);
Set<AppSearchSchema> schemas =
Set.of(
new AppSearchSchema.Builder("typeA").build(),
new AppSearchSchema.Builder("typeB").build());
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false);
// Insert documents
GenericDocument document1 = new GenericDocument.Builder("uri", "typeA").setNamespace(
"namespace").build();
GenericDocument document1 =
new GenericDocument.Builder("uri", "typeA").setNamespace("namespace").build();
mAppSearchImpl.putDocument("database1", document1);
GenericDocument document2 = new GenericDocument.Builder("uri", "typeB").setNamespace(
"namespace").build();
GenericDocument document2 =
new GenericDocument.Builder("uri", "typeB").setNamespace("namespace").build();
mAppSearchImpl.putDocument("database2", document2);
// Rewrite SearchSpec
mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(searchSpecProto,
ImmutableSet.of("database1", "database2"));
assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
"database1/typeA", "database1/typeB", "database2/typeA", "database2/typeB");
assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly(
"database1/namespace", "database2/namespace");
mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(
searchSpecProto, ImmutableSet.of("database1", "database2"));
assertThat(searchSpecProto.getSchemaTypeFiltersList())
.containsExactly(
"database1/typeA", "database1/typeB", "database2/typeA", "database2/typeB");
assertThat(searchSpecProto.getNamespaceFiltersList())
.containsExactly("database1/namespace", "database2/namespace");
}
@Test
public void testQueryEmptyDatabase() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage = mAppSearchImpl.query(
"EmptyDatabase",
"", searchSpec);
SearchResultPage searchResultPage = mAppSearchImpl.query("EmptyDatabase", "", searchSpec);
assertThat(searchResultPage.getResults()).isEmpty();
}
@@ -386,25 +451,25 @@ public class AppSearchImplTest {
public void testGlobalQueryEmptyDatabase() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage = mAppSearchImpl.query(
"EmptyDatabase",
"", searchSpec);
SearchResultPage searchResultPage = mAppSearchImpl.query("EmptyDatabase", "", searchSpec);
assertThat(searchResultPage.getResults()).isEmpty();
}
@Test
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder().addSchemaType("FakeType").setTermMatch(
TermMatchType.Code.PREFIX_VALUE).build();
mAppSearchImpl.removeByQuery("EmptyDatabase",
"", searchSpec);
new SearchSpec.Builder()
.addSchemaType("FakeType")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec);
searchSpec =
new SearchSpec.Builder().addNamespace("FakeNamespace").setTermMatch(
TermMatchType.Code.PREFIX_VALUE).build();
mAppSearchImpl.removeByQuery("EmptyDatabase",
"", searchSpec);
new SearchSpec.Builder()
.addNamespace("FakeNamespace")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec);
searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec);
@@ -415,12 +480,14 @@ public class AppSearchImplTest {
Set<AppSearchSchema> schemas =
Collections.singleton(new AppSearchSchema.Builder("Email").build());
// Set schema Email to AppSearch database1
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.build();
SchemaProto expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.build();
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
expectedTypes.add(mVisibilitySchemaProto);
@@ -431,23 +498,28 @@ public class AppSearchImplTest {
@Test
public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
"schema1").build()), /*forceOverride=*/false);
mAppSearchImpl.setSchema(
"database",
Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
/*forceOverride=*/ false);
mAppSearchImpl.setVisibility("database", Set.of("schema1"));
// "schema1" is platform hidden now
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas(
"database")).containsExactly("database/schema1");
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
.containsExactly("database/schema1");
// Add a new schema, and include the already-existing "schema1"
mAppSearchImpl.setSchema("database", Set.of(new AppSearchSchema.Builder(
"schema1").build(), new AppSearchSchema.Builder(
"schema2").build()), /*forceOverride=*/false);
mAppSearchImpl.setSchema(
"database",
Set.of(
new AppSearchSchema.Builder("schema1").build(),
new AppSearchSchema.Builder("schema2").build()),
/*forceOverride=*/ false);
// Check that "schema1" is still platform hidden, but "schema2" is the default platform
// visible.
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas(
"database")).containsExactly("database/schema1");
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
.containsExactly("database/schema1");
}
@Test
@@ -456,13 +528,17 @@ public class AppSearchImplTest {
schemas.add(new AppSearchSchema.Builder("Email").build());
schemas.add(new AppSearchSchema.Builder("Document").build());
// Set schema Email and Document to AppSearch database1
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
.build();
SchemaProto expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("database1/Document"))
.build();
// Check both schema Email and Document saved correctly.
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
@@ -471,21 +547,27 @@ public class AppSearchImplTest {
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
final Set<AppSearchSchema> finalSchemas = Collections.singleton(new AppSearchSchema.Builder(
"Email").build());
final Set<AppSearchSchema> finalSchemas =
Collections.singleton(new AppSearchSchema.Builder("Email").build());
// Check the incompatible error has been thrown.
AppSearchException e = expectThrows(AppSearchException.class, () ->
mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/false));
AppSearchException e =
expectThrows(
AppSearchException.class,
() ->
mAppSearchImpl.setSchema(
"database1", finalSchemas, /*forceOverride=*/ false));
assertThat(e).hasMessageThat().contains("Schema is incompatible");
assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]");
// ForceOverride to delete.
mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/true);
mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/ true);
// Check Document schema is removed.
expectedProto = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.build();
expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.build();
expectedTypes = new ArrayList<>();
expectedTypes.add(mVisibilitySchemaProto);
@@ -502,16 +584,23 @@ public class AppSearchImplTest {
schemas.add(new AppSearchSchema.Builder("Document").build());
// Set schema Email and Document to AppSearch database1 and 2
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false);
mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/false);
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
.build();
SchemaProto expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("database1/Document"))
.addTypes(
SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("database2/Document"))
.build();
// Check Email and Document is saved in database 1 and 2 correctly.
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
@@ -522,15 +611,20 @@ public class AppSearchImplTest {
// Save only Email to database1 this time.
schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build());
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/true);
mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ true);
// Create expected schemaType list, database 1 should only contain Email but database 2
// remains in same.
expectedProto = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
.build();
expectedProto =
SchemaProto.newBuilder()
.addTypes(
SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("database2/Document"))
.build();
// Check nothing changed in database2.
expectedTypes = new ArrayList<>();
@@ -540,61 +634,69 @@ public class AppSearchImplTest {
.containsExactlyElementsIn(expectedTypes);
}
@Test
public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
"schema1").build()), /*forceOverride=*/false);
mAppSearchImpl.setSchema(
"database",
Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
/*forceOverride=*/ false);
mAppSearchImpl.setVisibility("database", Set.of("schema1"));
// "schema1" is platform hidden now
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas(
"database")).containsExactly("database/schema1");
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
.containsExactly("database/schema1");
// Remove "schema1" by force overriding
mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/true);
mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/ true);
// Check that "schema1" is no longer considered platform hidden
assertThat(
mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas(
"database")).isEmpty();
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
.isEmpty();
// Add "schema1" back, it gets default visibility settings which means it's not platform
// hidden.
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
"schema1").build()), /*forceOverride=*/false);
assertThat(
mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas(
"database")).isEmpty();
mAppSearchImpl.setSchema(
"database",
Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
.isEmpty();
}
@Test
public void testSetVisibility_defaultPlatformVisible() throws Exception {
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
"Schema").build()), /*forceOverride=*/false);
assertThat(
mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas(
"database")).isEmpty();
mAppSearchImpl.setSchema(
"database",
Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
.isEmpty();
}
@Test
public void testSetVisibility_platformHidden() throws Exception {
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
"Schema").build()), /*forceOverride=*/false);
mAppSearchImpl.setSchema(
"database",
Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
/*forceOverride=*/ false);
mAppSearchImpl.setVisibility("database", Set.of("Schema"));
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas(
"database")).containsExactly("database/Schema");
assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
.containsExactly("database/Schema");
}
@Test
public void testSetVisibility_unknownSchema() throws Exception {
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
"Schema").build()), /*forceOverride=*/false);
mAppSearchImpl.setSchema(
"database",
Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
/*forceOverride=*/ false);
// We'll throw an exception if a client tries to set visibility on a schema we don't know
// about.
AppSearchException e = expectThrows(AppSearchException.class,
() -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema")));
AppSearchException e =
expectThrows(
AppSearchException.class,
() -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema")));
assertThat(e).hasMessageThat().contains("Unknown schema(s)");
}
@@ -603,8 +705,10 @@ public class AppSearchImplTest {
// Nothing exists yet
assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isFalse();
mAppSearchImpl.setSchema("database", Collections.singleton(new AppSearchSchema.Builder(
"Schema").build()), /*forceOverride=*/false);
mAppSearchImpl.setSchema(
"database",
Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isTrue();
assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "UnknownSchema")).isFalse();
@@ -613,19 +717,23 @@ public class AppSearchImplTest {
@Test
public void testGetDatabases() throws Exception {
// No client databases exist yet, but the VisibilityStore's does
assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
VisibilityStore.DATABASE_NAME);
assertThat(mAppSearchImpl.getDatabasesLocked())
.containsExactly(VisibilityStore.DATABASE_NAME);
// Has database1
mAppSearchImpl.setSchema("database1", Collections.singleton(new AppSearchSchema.Builder(
"schema").build()), /*forceOverride=*/false);
assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
VisibilityStore.DATABASE_NAME, "database1");
mAppSearchImpl.setSchema(
"database1",
Collections.singleton(new AppSearchSchema.Builder("schema").build()),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getDatabasesLocked())
.containsExactly(VisibilityStore.DATABASE_NAME, "database1");
// Has both databases
mAppSearchImpl.setSchema("database2", Collections.singleton(new AppSearchSchema.Builder(
"schema").build()), /*forceOverride=*/false);
assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
VisibilityStore.DATABASE_NAME, "database1", "database2");
mAppSearchImpl.setSchema(
"database2",
Collections.singleton(new AppSearchSchema.Builder("schema").build()),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getDatabasesLocked())
.containsExactly(VisibilityStore.DATABASE_NAME, "database1", "database2");
}
}

View File

@@ -28,8 +28,7 @@ import java.util.Set;
public class VisibilityStoreTest {
@Rule
public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private AppSearchImpl mAppSearchImpl;
private VisibilityStore mVisibilityStore;
@@ -72,5 +71,4 @@ public class VisibilityStoreTest {
mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema2"));
assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
}
}

View File

@@ -32,18 +32,18 @@ import java.util.HashMap;
import java.util.List;
public class GenericDocumentToProtoConverterTest {
private static final byte[] BYTE_ARRAY_1 = new byte[]{(byte) 1, (byte) 2, (byte) 3};
private static final byte[] BYTE_ARRAY_2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7};
private static final byte[] BYTE_ARRAY_1 = new byte[] {(byte) 1, (byte) 2, (byte) 3};
private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7};
private static final GenericDocument DOCUMENT_PROPERTIES_1 =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
"sDocumentProperties1", "sDocumentPropertiesSchemaType1")
.setCreationTimestampMillis(12345L)
.build();
"sDocumentProperties1", "sDocumentPropertiesSchemaType1")
.setCreationTimestampMillis(12345L)
.build();
private static final GenericDocument DOCUMENT_PROPERTIES_2 =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
"sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.setCreationTimestampMillis(6789L)
.build();
"sDocumentProperties2", "sDocumentPropertiesSchemaType2")
.setCreationTimestampMillis(6789L)
.build();
@Test
public void testDocumentProtoConvert() {
@@ -63,32 +63,42 @@ public class GenericDocumentToProtoConverterTest {
.build();
// Create the Document proto. Need to sort the property order by key.
DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder()
.setUri("uri1")
.setSchema("schemaType1")
.setCreationTimestampMs(5L)
.setScore(1)
.setTtlMs(1L)
.setNamespace("namespace");
DocumentProto.Builder documentProtoBuilder =
DocumentProto.newBuilder()
.setUri("uri1")
.setSchema("schemaType1")
.setCreationTimestampMs(5L)
.setScore(1)
.setTtlMs(1L)
.setNamespace("namespace");
HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>();
propertyProtoMap.put("longKey1",
PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
propertyProtoMap.put("doubleKey1",
propertyProtoMap.put(
"longKey1", PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L));
propertyProtoMap.put(
"doubleKey1",
PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0));
propertyProtoMap.put("booleanKey1",
propertyProtoMap.put(
"booleanKey1",
PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true));
propertyProtoMap.put("stringKey1",
propertyProtoMap.put(
"stringKey1",
PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1"));
propertyProtoMap.put("byteKey1",
PropertyProto.newBuilder().setName("byteKey1")
propertyProtoMap.put(
"byteKey1",
PropertyProto.newBuilder()
.setName("byteKey1")
.addBytesValues(ByteString.copyFrom(BYTE_ARRAY_1))
.addBytesValues(ByteString.copyFrom(BYTE_ARRAY_2)));
propertyProtoMap.put("documentKey1",
PropertyProto.newBuilder().setName("documentKey1")
propertyProtoMap.put(
"documentKey1",
PropertyProto.newBuilder()
.setName("documentKey1")
.addDocumentValues(
GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1)));
propertyProtoMap.put("documentKey2",
PropertyProto.newBuilder().setName("documentKey2")
propertyProtoMap.put(
"documentKey2",
PropertyProto.newBuilder()
.setName("documentKey2")
.addDocumentValues(
GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2)));
List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
@@ -97,8 +107,7 @@ public class GenericDocumentToProtoConverterTest {
documentProtoBuilder.addProperties(propertyProtoMap.get(key));
}
DocumentProto documentProto = documentProtoBuilder.build();
assertThat(GenericDocumentToProtoConverter.convert(document))
.isEqualTo(documentProto);
assertThat(GenericDocumentToProtoConverter.convert(document)).isEqualTo(documentProto);
assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto));
}
}

View File

@@ -30,88 +30,126 @@ 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();
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)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
).addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("body")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
).build();
SchemaTypeConfigProto expectedEmailProto =
SchemaTypeConfigProto.newBuilder()
.setSchemaType("Email")
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(
PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType
.Code.PLAIN)
.setTermMatchType(
TermMatchType.Code.PREFIX)))
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("body")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(
PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.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();
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)
.setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType.Code.PLAIN)
.setTermMatchType(TermMatchType.Code.PREFIX)
)
).addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("pubDate")
.setDataType(PropertyConfigProto.DataType.Code.INT64)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType.Code.NONE)
.setTermMatchType(TermMatchType.Code.UNKNOWN)
)
).build();
SchemaTypeConfigProto expectedMusicRecordingProto =
SchemaTypeConfigProto.newBuilder()
.setSchemaType("MusicRecording")
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("artist")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setCardinality(
PropertyConfigProto.Cardinality.Code.REPEATED)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType
.Code.PLAIN)
.setTermMatchType(
TermMatchType.Code.PREFIX)))
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("pubDate")
.setDataType(PropertyConfigProto.DataType.Code.INT64)
.setCardinality(
PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setStringIndexingConfig(
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig.TokenizerType
.Code.NONE)
.setTermMatchType(
TermMatchType.Code.UNKNOWN)))
.build();
assertThat(SchemaToProtoConverter.convert(musicRecordingSchema))
.isEqualTo(expectedMusicRecordingProto);

View File

@@ -36,9 +36,10 @@ public class SnippetTest {
public void testSingleStringSnippet() {
final String propertyKeyString = "content";
final String propertyValueString = "A commonly used fake word is foo.\n"
+ " Another nonsense word that’s used a lot\n"
+ " is bar.\n";
final String propertyValueString =
"A commonly used fake word is foo.\n"
+ " Another nonsense word that’s used a lot\n"
+ " is bar.\n";
final String uri = "uri1";
final String schemaType = "schema1";
final String searchWord = "foo";
@@ -46,34 +47,39 @@ public class SnippetTest {
final String window = "is foo";
// Building the SearchResult received from query.
PropertyProto property = PropertyProto.newBuilder()
.setName(propertyKeyString)
.addStringValues(propertyValueString)
.build();
DocumentProto documentProto = DocumentProto.newBuilder()
.setUri(uri)
.setSchema(schemaType)
.addProperties(property)
.build();
SnippetProto snippetProto = SnippetProto.newBuilder()
.addEntries(SnippetProto.EntryProto.newBuilder()
.setPropertyName(propertyKeyString)
.addSnippetMatches(SnippetMatchProto.newBuilder()
.setValuesIndex(0)
.setExactMatchPosition(29)
.setExactMatchBytes(3)
.setWindowPosition(26)
.setWindowBytes(6)
.build())
.build())
.build();
SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
.setDocument(documentProto)
.setSnippet(snippetProto)
.build();
SearchResultProto searchResultProto = SearchResultProto.newBuilder()
.addResults(resultProto)
.build();
PropertyProto property =
PropertyProto.newBuilder()
.setName(propertyKeyString)
.addStringValues(propertyValueString)
.build();
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri(uri)
.setSchema(schemaType)
.addProperties(property)
.build();
SnippetProto snippetProto =
SnippetProto.newBuilder()
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName(propertyKeyString)
.addSnippetMatches(
SnippetMatchProto.newBuilder()
.setValuesIndex(0)
.setExactMatchPosition(29)
.setExactMatchBytes(3)
.setWindowPosition(26)
.setWindowBytes(6)
.build())
.build())
.build();
SearchResultProto.ResultProto resultProto =
SearchResultProto.ResultProto.newBuilder()
.setDocument(documentProto)
.setSnippet(snippetProto)
.build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder().addResults(resultProto).build();
// Making ResultReader and getting Snippet values.
SearchResultPage searchResultPage =
@@ -83,11 +89,11 @@ public class SnippetTest {
assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
assertThat(match.getFullText()).isEqualTo(propertyValueString);
assertThat(match.getExactMatch()).isEqualTo(exactMatch);
assertThat(match.getExactMatchPosition()).isEqualTo(
new SearchResult.MatchRange(/*lower=*/29, /*upper=*/32));
assertThat(match.getExactMatchPosition())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32));
assertThat(match.getFullText()).isEqualTo(propertyValueString);
assertThat(match.getSnippetPosition()).isEqualTo(
new SearchResult.MatchRange(/*lower=*/26, /*upper=*/32));
assertThat(match.getSnippetPosition())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32));
assertThat(match.getSnippet()).isEqualTo(window);
}
}
@@ -97,9 +103,10 @@ public class SnippetTest {
public void testNoSnippets() throws Exception {
final String propertyKeyString = "content";
final String propertyValueString = "A commonly used fake word is foo.\n"
+ " Another nonsense word that’s used a lot\n"
+ " is bar.\n";
final String propertyValueString =
"A commonly used fake word is foo.\n"
+ " Another nonsense word that’s used a lot\n"
+ " is bar.\n";
final String uri = "uri1";
final String schemaType = "schema1";
final String searchWord = "foo";
@@ -107,21 +114,21 @@ public class SnippetTest {
final String window = "is foo";
// Building the SearchResult received from query.
PropertyProto property = PropertyProto.newBuilder()
.setName(propertyKeyString)
.addStringValues(propertyValueString)
.build();
DocumentProto documentProto = DocumentProto.newBuilder()
.setUri(uri)
.setSchema(schemaType)
.addProperties(property)
.build();
SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
.setDocument(documentProto)
.build();
SearchResultProto searchResultProto = SearchResultProto.newBuilder()
.addResults(resultProto)
.build();
PropertyProto property =
PropertyProto.newBuilder()
.setName(propertyKeyString)
.addStringValues(propertyValueString)
.build();
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri(uri)
.setSchema(schemaType)
.addProperties(property)
.build();
SearchResultProto.ResultProto resultProto =
SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder().addResults(resultProto).build();
SearchResultPage searchResultPage =
SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto);
@@ -135,54 +142,57 @@ public class SnippetTest {
final String searchWord = "Test";
// Building the SearchResult received from query.
PropertyProto property1 = PropertyProto.newBuilder()
.setName("sender.name")
.addStringValues("Test Name Jr.")
.build();
PropertyProto property2 = PropertyProto.newBuilder()
.setName("sender.email")
.addStringValues("TestNameJr@gmail.com")
.build();
DocumentProto documentProto = DocumentProto.newBuilder()
.setUri("uri1")
.setSchema("schema1")
.addProperties(property1)
.addProperties(property2)
.build();
SnippetProto snippetProto = SnippetProto.newBuilder()
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName("sender.name")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
.setValuesIndex(0)
.setExactMatchPosition(0)
.setExactMatchBytes(4)
.setWindowPosition(0)
.setWindowBytes(9)
.build())
.build())
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName("sender.email")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
.setValuesIndex(0)
.setExactMatchPosition(0)
.setExactMatchBytes(20)
.setWindowPosition(0)
.setWindowBytes(20)
.build())
.build()
)
.build();
SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
.setDocument(documentProto)
.setSnippet(snippetProto)
.build();
SearchResultProto searchResultProto = SearchResultProto.newBuilder()
.addResults(resultProto)
.build();
PropertyProto property1 =
PropertyProto.newBuilder()
.setName("sender.name")
.addStringValues("Test Name Jr.")
.build();
PropertyProto property2 =
PropertyProto.newBuilder()
.setName("sender.email")
.addStringValues("TestNameJr@gmail.com")
.build();
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri("uri1")
.setSchema("schema1")
.addProperties(property1)
.addProperties(property2)
.build();
SnippetProto snippetProto =
SnippetProto.newBuilder()
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName("sender.name")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
.setValuesIndex(0)
.setExactMatchPosition(0)
.setExactMatchBytes(4)
.setWindowPosition(0)
.setWindowBytes(9)
.build())
.build())
.addEntries(
SnippetProto.EntryProto.newBuilder()
.setPropertyName("sender.email")
.addSnippetMatches(
SnippetMatchProto.newBuilder()
.setValuesIndex(0)
.setExactMatchPosition(0)
.setExactMatchBytes(20)
.setWindowPosition(0)
.setWindowBytes(20)
.build())
.build())
.build();
SearchResultProto.ResultProto resultProto =
SearchResultProto.ResultProto.newBuilder()
.setDocument(documentProto)
.setSnippet(snippetProto)
.build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder().addResults(resultProto).build();
// Making ResultReader and getting Snippet values.
SearchResultPage searchResultPage =
@@ -192,21 +202,21 @@ public class SnippetTest {
SearchResult.MatchInfo match1 = result.getMatches().get(0);
assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
assertThat(match1.getExactMatchPosition()).isEqualTo(
new SearchResult.MatchRange(/*lower=*/0, /*upper=*/4));
assertThat(match1.getExactMatchPosition())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4));
assertThat(match1.getExactMatch()).isEqualTo("Test");
assertThat(match1.getSnippetPosition()).isEqualTo(
new SearchResult.MatchRange(/*lower=*/0, /*upper=*/9));
assertThat(match1.getSnippetPosition())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9));
assertThat(match1.getSnippet()).isEqualTo("Test Name");
SearchResult.MatchInfo match2 = result.getMatches().get(1);
assertThat(match2.getPropertyPath()).isEqualTo("sender.email");
assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com");
assertThat(match2.getExactMatchPosition()).isEqualTo(
new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20));
assertThat(match2.getExactMatchPosition())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com");
assertThat(match2.getSnippetPosition()).isEqualTo(
new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20));
assertThat(match2.getSnippetPosition())
.isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20));
assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com");
}
}