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:
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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[] {}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user