Merge "Merge Jetpack SearchSpec work from last two quarters."
This commit is contained in:
committed by
Android (Google) Code Review
commit
2c9639a347
@@ -25,7 +25,6 @@ import com.android.internal.infra.AndroidFuture;
|
||||
|
||||
import com.google.android.icing.proto.DocumentProto;
|
||||
import com.google.android.icing.proto.SearchResultProto;
|
||||
import com.google.android.icing.proto.SearchSpecProto;
|
||||
import com.google.android.icing.proto.StatusProto;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
@@ -294,13 +293,7 @@ public class AppSearchManager {
|
||||
// them in one big list.
|
||||
AndroidFuture<AppSearchResult> searchResultFuture = new AndroidFuture<>();
|
||||
try {
|
||||
SearchSpecProto searchSpecProto = searchSpec.getSearchSpecProto();
|
||||
searchSpecProto = searchSpecProto.toBuilder().setQuery(queryExpression).build();
|
||||
mService.query(
|
||||
searchSpecProto.toByteArray(),
|
||||
searchSpec.getResultSpecProto().toByteArray(),
|
||||
searchSpec.getScoringSpecProto().toByteArray(),
|
||||
searchResultFuture);
|
||||
mService.query(queryExpression, searchSpec.getBundle(), searchResultFuture);
|
||||
} catch (RemoteException e) {
|
||||
searchResultFuture.completeExceptionally(e);
|
||||
}
|
||||
|
||||
@@ -67,14 +67,14 @@ interface IAppSearchManager {
|
||||
/**
|
||||
* Searches a document based on a given specifications.
|
||||
*
|
||||
* @param searchSpecBytes Serialized SearchSpecProto.
|
||||
* @param resultSpecBytes Serialized SearchResultsProto.
|
||||
* @param scoringSpecBytes Serialized ScoringSpecProto.
|
||||
* @param queryExpression String to search for
|
||||
* @param searchSpecBundle SearchSpec bundle
|
||||
* @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link byte[]}>>
|
||||
* Will be completed with a serialized {@link SearchResultsProto}.
|
||||
*/
|
||||
void query(
|
||||
in byte[] searchSpecBytes, in byte[] resultSpecBytes, in byte[] scoringSpecBytes,
|
||||
in String queryExpression,
|
||||
in Bundle searchSpecBundle,
|
||||
in AndroidFuture<AppSearchResult> callback);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,19 +16,17 @@
|
||||
|
||||
package android.app.appsearch;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.appsearch.exceptions.IllegalSearchSpecException;
|
||||
|
||||
import com.google.android.icing.proto.ResultSpecProto;
|
||||
import com.google.android.icing.proto.ScoringSpecProto;
|
||||
import com.google.android.icing.proto.SearchSpecProto;
|
||||
import com.google.android.icing.proto.TermMatchType;
|
||||
import android.app.appsearch.exceptions.IllegalSearchSpecException;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
||||
/**
|
||||
* This class represents the specification logic for AppSearch. It can be used to set the type of
|
||||
* search, like prefix or exact only or apply filters to search for a specific schema type only etc.
|
||||
@@ -36,67 +34,100 @@ import java.lang.annotation.RetentionPolicy;
|
||||
*/
|
||||
// TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
|
||||
public final class SearchSpec {
|
||||
|
||||
private final SearchSpecProto mSearchSpecProto;
|
||||
private final ResultSpecProto mResultSpecProto;
|
||||
private final ScoringSpecProto mScoringSpecProto;
|
||||
|
||||
private SearchSpec(@NonNull SearchSpecProto searchSpecProto,
|
||||
@NonNull ResultSpecProto resultSpecProto, @NonNull ScoringSpecProto scoringSpecProto) {
|
||||
mSearchSpecProto = searchSpecProto;
|
||||
mResultSpecProto = resultSpecProto;
|
||||
mScoringSpecProto = scoringSpecProto;
|
||||
}
|
||||
|
||||
/** Creates a new {@link SearchSpec.Builder}. */
|
||||
@NonNull
|
||||
public static SearchSpec.Builder newBuilder() {
|
||||
return new SearchSpec.Builder();
|
||||
}
|
||||
/** @hide */
|
||||
|
||||
public static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
SearchSpecProto getSearchSpecProto() {
|
||||
return mSearchSpecProto;
|
||||
}
|
||||
|
||||
public static final String SCHEMA_TYPES_FIELD = "schemaType";
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
ResultSpecProto getResultSpecProto() {
|
||||
return mResultSpecProto;
|
||||
}
|
||||
|
||||
public static final String NAMESPACE_FIELD = "namespace";
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
ScoringSpecProto getScoringSpecProto() {
|
||||
return mScoringSpecProto;
|
||||
|
||||
public static final String NUM_PER_PAGE_FIELD = "numPerPage";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String RANKING_STRATEGY_FIELD = "rankingStrategy";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String ORDER_FIELD = "order";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String SNIPPET_COUNT_FIELD = "snippetCount";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String SNIPPET_COUNT_PER_PROPERTY_FIELD = "snippetCountPerProperty";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final String MAX_SNIPPET_FIELD = "maxSnippet";
|
||||
|
||||
/** @hide */
|
||||
|
||||
public static final int DEFAULT_NUM_PER_PAGE = 10;
|
||||
|
||||
private static final int MAX_NUM_PER_PAGE = 10_000;
|
||||
private static final int MAX_SNIPPET_COUNT = 10_000;
|
||||
private static final int MAX_SNIPPET_PER_PROPERTY_COUNT = 10_000;
|
||||
private static final int MAX_SNIPPET_SIZE_LIMIT = 10_000;
|
||||
|
||||
private final Bundle mBundle;
|
||||
|
||||
/** @hide */
|
||||
|
||||
public SearchSpec(@NonNull Bundle bundle) {
|
||||
Preconditions.checkNotNull(bundle);
|
||||
mBundle = bundle;
|
||||
}
|
||||
|
||||
/** Term Match Type for the query. */
|
||||
/**
|
||||
* Returns the {@link Bundle} populated by this builder.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Bundle getBundle() {
|
||||
return mBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(prefix = {"TERM_MATCH_TYPE_"}, value = {
|
||||
TERM_MATCH_TYPE_EXACT_ONLY,
|
||||
TERM_MATCH_TYPE_PREFIX
|
||||
@IntDef(value = {
|
||||
TERM_MATCH_EXACT_ONLY,
|
||||
TERM_MATCH_PREFIX
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface TermMatchTypeCode {}
|
||||
public @interface TermMatchCode {}
|
||||
|
||||
/**
|
||||
* 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_TYPE_EXACT_ONLY = 1;
|
||||
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_TYPE_PREFIX = 2;
|
||||
public static final int TERM_MATCH_PREFIX = 2;
|
||||
|
||||
/** Ranking Strategy for query result.*/
|
||||
/**
|
||||
* 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(prefix = {"RANKING_STRATEGY_"}, value = {
|
||||
@IntDef(value = {
|
||||
RANKING_STRATEGY_NONE,
|
||||
RANKING_STRATEGY_DOCUMENT_SCORE,
|
||||
RANKING_STRATEGY_CREATION_TIMESTAMP
|
||||
@@ -111,10 +142,13 @@ public final class SearchSpec {
|
||||
/** Ranked by document creation timestamps. */
|
||||
public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2;
|
||||
|
||||
/** Order for query result.*/
|
||||
/**
|
||||
* Order for query result.
|
||||
* @hide
|
||||
*/
|
||||
// NOTE: The integer values of these constants must match the proto enum constants in
|
||||
// {@link ScoringSpecProto.Order.Code }
|
||||
@IntDef(prefix = {"ORDER_"}, value = {
|
||||
@IntDef(value = {
|
||||
ORDER_DESCENDING,
|
||||
ORDER_ASCENDING
|
||||
})
|
||||
@@ -129,27 +163,24 @@ public final class SearchSpec {
|
||||
/** Builder for {@link SearchSpec objects}. */
|
||||
public static final class Builder {
|
||||
|
||||
private final SearchSpecProto.Builder mSearchSpecBuilder = SearchSpecProto.newBuilder();
|
||||
private final ResultSpecProto.Builder mResultSpecBuilder = ResultSpecProto.newBuilder();
|
||||
private final ScoringSpecProto.Builder mScoringSpecBuilder = ScoringSpecProto.newBuilder();
|
||||
private final ResultSpecProto.SnippetSpecProto.Builder mSnippetSpecBuilder =
|
||||
ResultSpecProto.SnippetSpecProto.newBuilder();
|
||||
private final Bundle mBundle;
|
||||
private boolean mBuilt = false;
|
||||
|
||||
private Builder() {
|
||||
/** Creates a new {@link SearchSpec.Builder}. */
|
||||
public Builder() {
|
||||
mBundle = new Bundle();
|
||||
mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates how the query terms should match {@link TermMatchTypeCode} in the index.
|
||||
* Indicates how the query terms should match {@code TermMatchCode} in the index.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setTermMatchType(@TermMatchTypeCode int termMatchTypeCode) {
|
||||
TermMatchType.Code termMatchTypeCodeProto =
|
||||
TermMatchType.Code.forNumber(termMatchTypeCode);
|
||||
if (termMatchTypeCodeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid term match type: "
|
||||
+ termMatchTypeCode);
|
||||
}
|
||||
mSearchSpecBuilder.setTermMatchType(termMatchTypeCodeProto);
|
||||
public Builder setTermMatch(@TermMatchCode int termMatchTypeCode) {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(termMatchTypeCode, TERM_MATCH_EXACT_ONLY,
|
||||
TERM_MATCH_PREFIX, "Term match type");
|
||||
mBundle.putInt(TERM_MATCH_TYPE_FIELD, termMatchTypeCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -160,31 +191,44 @@ public final class SearchSpec {
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setSchemaTypes(@NonNull String... schemaTypes) {
|
||||
for (String schemaType : schemaTypes) {
|
||||
mSearchSpecBuilder.addSchemaTypeFilters(schemaType);
|
||||
}
|
||||
Preconditions.checkNotNull(schemaTypes);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
mBundle.putStringArray(SCHEMA_TYPES_FIELD, schemaTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets the maximum number of results to retrieve from the query */
|
||||
/**
|
||||
* 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
|
||||
public SearchSpec.Builder setNumToRetrieve(int numToRetrieve) {
|
||||
// Just retrieve everything in one page.
|
||||
// TODO(b/152359656): Realign these two apis properly.
|
||||
mResultSpecBuilder.setNumPerPage(numToRetrieve);
|
||||
public Builder setNamespaces(@NonNull String... namespaces) {
|
||||
Preconditions.checkNotNull(namespaces);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
mBundle.putStringArray(NAMESPACE_FIELD, namespaces);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of results per page in the returned object.
|
||||
* <p> The default number of results per page is 10. And should be set in range [0, 10k].
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec.Builder setNumPerPage(int numPerPage) {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(numPerPage, 0, MAX_NUM_PER_PAGE, "NumPerPage");
|
||||
mBundle.putInt(NUM_PER_PAGE_FIELD, numPerPage);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Sets ranking strategy for AppSearch results.*/
|
||||
@NonNull
|
||||
public Builder setRankingStrategy(@RankingStrategyCode int rankingStrategy) {
|
||||
ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
|
||||
ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategy);
|
||||
if (rankingStrategyCodeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid result ranking strategy: "
|
||||
+ rankingStrategyCodeProto);
|
||||
}
|
||||
mScoringSpecBuilder.setRankBy(rankingStrategyCodeProto);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(rankingStrategy, RANKING_STRATEGY_NONE,
|
||||
RANKING_STRATEGY_CREATION_TIMESTAMP, "Result ranking strategy");
|
||||
mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -195,37 +239,41 @@ public final class SearchSpec {
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setOrder(@OrderCode int order) {
|
||||
ScoringSpecProto.Order.Code orderCodeProto =
|
||||
ScoringSpecProto.Order.Code.forNumber(order);
|
||||
if (orderCodeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid result ranking order: "
|
||||
+ orderCodeProto);
|
||||
}
|
||||
mScoringSpecBuilder.setOrderBy(orderCodeProto);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(order, ORDER_DESCENDING, ORDER_ASCENDING,
|
||||
"Result ranking order");
|
||||
mBundle.putInt(ORDER_FIELD, order);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only the first {@code numToSnippet} documents based on the ranking strategy
|
||||
* 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 SearchResults.Result#getMatchInfo} will return {@code null} for that result.
|
||||
* {@link SearchResults.Result#getMatches} will return {@code null} for that result.
|
||||
* <p>The value should be set in range[0, 10k].
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec.Builder setNumToSnippet(int numToSnippet) {
|
||||
mSnippetSpecBuilder.setNumToSnippet(numToSnippet);
|
||||
public SearchSpec.Builder setSnippetCount(int snippetCount) {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(snippetCount, 0, MAX_SNIPPET_COUNT, "snippetCount");
|
||||
mBundle.putInt(SNIPPET_COUNT_FIELD, snippetCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only the first {@code numMatchesPerProperty} matches for a every property of
|
||||
* {@link AppSearchDocument} will contain snippet information.
|
||||
* <p>If set to 0, snippeting is disabled and {@link SearchResults.Result#getMatchInfo}
|
||||
* Only the first {@code matchesCountPerProperty} matches for a every property of
|
||||
* {@link GenericDocument} will contain snippet information.
|
||||
* <p>If set to 0, snippeting is disabled and {@link SearchResults.Result#getMatches}
|
||||
* will return {@code null} for that result.
|
||||
* <p>The value should be set in range[0, 10k].
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec.Builder setNumMatchesPerProperty(int numMatchesPerProperty) {
|
||||
mSnippetSpecBuilder.setNumMatchesPerProperty(numMatchesPerProperty);
|
||||
public SearchSpec.Builder setSnippetCountPerProperty(int snippetCountPerProperty) {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(snippetCountPerProperty,
|
||||
0, MAX_SNIPPET_PER_PROPERTY_COUNT, "snippetCountPerProperty");
|
||||
mBundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, snippetCountPerProperty);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -238,10 +286,14 @@ public final class SearchSpec {
|
||||
* 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.
|
||||
* <p>The value should be in range[0, 10k].
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec.Builder setMaxSnippetSize(int maxSnippetSize) {
|
||||
mSnippetSpecBuilder.setMaxWindowBytes(maxSnippetSize);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkArgumentInRange(
|
||||
maxSnippetSize, 0, MAX_SNIPPET_SIZE_LIMIT, "maxSnippetSize");
|
||||
mBundle.putInt(MAX_SNIPPET_FIELD, maxSnippetSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -252,12 +304,12 @@ public final class SearchSpec {
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec build() {
|
||||
if (mSearchSpecBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
if (!mBundle.containsKey(TERM_MATCH_TYPE_FIELD)) {
|
||||
throw new IllegalSearchSpecException("Missing termMatchType field.");
|
||||
}
|
||||
mResultSpecBuilder.setSnippetSpec(mSnippetSpecBuilder);
|
||||
return new SearchSpec(mSearchSpecBuilder.build(), mResultSpecBuilder.build(),
|
||||
mScoringSpecBuilder.build());
|
||||
mBuilt = true;
|
||||
return new SearchSpec(mBundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.appsearch.AppSearchDocument;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.IAppSearchManager;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
@@ -32,15 +33,13 @@ import com.android.internal.util.Preconditions;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.appsearch.external.localbackend.AppSearchImpl;
|
||||
import com.android.server.appsearch.external.localbackend.converter.SchemaToProtoConverter;
|
||||
import com.android.server.appsearch.external.localbackend.converter.SearchSpecToProtoConverter;
|
||||
|
||||
import com.google.android.icing.proto.DocumentProto;
|
||||
import com.google.android.icing.proto.ResultSpecProto;
|
||||
import com.google.android.icing.proto.SchemaProto;
|
||||
import com.google.android.icing.proto.SchemaTypeConfigProto;
|
||||
import com.google.android.icing.proto.ScoringSpecProto;
|
||||
import com.google.android.icing.proto.SearchResultProto;
|
||||
import com.google.android.icing.proto.SearchSpecProto;
|
||||
import com.google.android.icing.proto.StatusProto;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -160,36 +159,31 @@ public class AppSearchManagerService extends SystemService {
|
||||
// TODO(sidchhabra): Do this in a threadpool.
|
||||
@Override
|
||||
public void query(
|
||||
@NonNull byte[] searchSpecBytes,
|
||||
@NonNull byte[] resultSpecBytes,
|
||||
@NonNull byte[] scoringSpecBytes,
|
||||
@NonNull String queryExpression,
|
||||
@NonNull Bundle searchSpecBundle,
|
||||
@NonNull AndroidFuture<AppSearchResult> callback) {
|
||||
Preconditions.checkNotNull(searchSpecBytes);
|
||||
Preconditions.checkNotNull(resultSpecBytes);
|
||||
Preconditions.checkNotNull(scoringSpecBytes);
|
||||
Preconditions.checkNotNull(queryExpression);
|
||||
Preconditions.checkNotNull(searchSpecBundle);
|
||||
Preconditions.checkNotNull(callback);
|
||||
int callingUid = Binder.getCallingUidOrThrow();
|
||||
int callingUserId = UserHandle.getUserId(callingUid);
|
||||
final long callingIdentity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
SearchSpecProto searchSpecProto = SearchSpecProto.parseFrom(searchSpecBytes);
|
||||
ResultSpecProto resultSpecProto = ResultSpecProto.parseFrom(resultSpecBytes);
|
||||
ScoringSpecProto scoringSpecProto = ScoringSpecProto.parseFrom(scoringSpecBytes);
|
||||
SearchSpec searchSpec = new SearchSpec(searchSpecBundle);
|
||||
SearchSpecProto searchSpecProto =
|
||||
SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
|
||||
searchSpecProto = searchSpecProto.toBuilder()
|
||||
.setQuery(queryExpression).build();
|
||||
AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
|
||||
String databaseName = makeDatabaseName(callingUid);
|
||||
// TODO(adorokhine): handle pagination
|
||||
SearchResultProto searchResultProto = impl.query(
|
||||
databaseName, searchSpecProto, resultSpecProto, scoringSpecProto);
|
||||
// TODO(sidchhabra): Translate SearchResultProto errors into error codes. This might
|
||||
// better be done in AppSearchImpl by throwing an AppSearchException.
|
||||
if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) {
|
||||
callback.complete(
|
||||
AppSearchResult.newFailedResult(
|
||||
AppSearchResult.RESULT_INTERNAL_ERROR,
|
||||
searchResultProto.getStatus().getMessage()));
|
||||
} else {
|
||||
callback.complete(
|
||||
AppSearchResult.newSuccessfulResult(searchResultProto.toByteArray()));
|
||||
}
|
||||
databaseName,
|
||||
searchSpecProto,
|
||||
SearchSpecToProtoConverter.toResultSpecProto(searchSpec),
|
||||
SearchSpecToProtoConverter.toScoringSpecProto(searchSpec));
|
||||
callback.complete(
|
||||
AppSearchResult.newSuccessfulResult(searchResultProto.toByteArray()));
|
||||
} catch (Throwable t) {
|
||||
callback.complete(throwableToFailedResult(t));
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.appsearch.external.localbackend.converter;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import com.google.android.icing.proto.ResultSpecProto;
|
||||
import com.google.android.icing.proto.ScoringSpecProto;
|
||||
import com.google.android.icing.proto.SearchSpecProto;
|
||||
import com.google.android.icing.proto.TermMatchType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Translates a {@link SearchSpec} into icing search protos.
|
||||
* @hide
|
||||
*/
|
||||
|
||||
public final class SearchSpecToProtoConverter {
|
||||
private SearchSpecToProtoConverter() {}
|
||||
|
||||
/** Extracts {@link SearchSpecProto} information from a {@link SearchSpec}. */
|
||||
@NonNull
|
||||
public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
|
||||
Preconditions.checkNotNull(spec);
|
||||
Bundle bundle = spec.getBundle();
|
||||
SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder();
|
||||
|
||||
@SearchSpec.TermMatchCode int termMatchCode =
|
||||
bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD);
|
||||
TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode);
|
||||
if (termMatchCodeProto == null || termMatchCodeProto.equals(TermMatchType.Code.UNKNOWN)) {
|
||||
throw new IllegalArgumentException("Invalid term match type: " + termMatchCode);
|
||||
}
|
||||
protoBuilder.setTermMatchType(termMatchCodeProto);
|
||||
|
||||
String[] schemaTypes = bundle.getStringArray(SearchSpec.SCHEMA_TYPES_FIELD);
|
||||
if (schemaTypes != null) {
|
||||
protoBuilder.addAllSchemaTypeFilters(Arrays.asList(schemaTypes));
|
||||
}
|
||||
String[] namespaces = bundle.getStringArray(SearchSpec.NAMESPACE_FIELD);
|
||||
if (namespaces != null) {
|
||||
protoBuilder.addAllNamespaceFilters(Arrays.asList(namespaces));
|
||||
}
|
||||
return protoBuilder.build();
|
||||
}
|
||||
|
||||
/** Extracts {@link ResultSpecProto} information from a {@link SearchSpec}. */
|
||||
@NonNull
|
||||
public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
|
||||
Preconditions.checkNotNull(spec);
|
||||
Bundle bundle = spec.getBundle();
|
||||
return ResultSpecProto.newBuilder()
|
||||
.setNumPerPage(bundle.getInt(
|
||||
SearchSpec.NUM_PER_PAGE_FIELD, SearchSpec.DEFAULT_NUM_PER_PAGE))
|
||||
.setSnippetSpec(ResultSpecProto.SnippetSpecProto.newBuilder()
|
||||
.setNumToSnippet(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD))
|
||||
.setNumMatchesPerProperty(
|
||||
bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD))
|
||||
.setMaxWindowBytes(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
/** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
|
||||
@NonNull
|
||||
public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) {
|
||||
Preconditions.checkNotNull(spec);
|
||||
Bundle bundle = spec.getBundle();
|
||||
ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder();
|
||||
|
||||
@SearchSpec.OrderCode int orderCode = bundle.getInt(SearchSpec.ORDER_FIELD);
|
||||
ScoringSpecProto.Order.Code orderCodeProto =
|
||||
ScoringSpecProto.Order.Code.forNumber(orderCode);
|
||||
if (orderCodeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid result ranking order: " + orderCode);
|
||||
}
|
||||
protoBuilder.setOrderBy(orderCodeProto);
|
||||
|
||||
@SearchSpec.RankingStrategyCode int rankingStrategyCode =
|
||||
bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD);
|
||||
ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
|
||||
ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategyCode);
|
||||
if (rankingStrategyCodeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid result ranking strategy: "
|
||||
+ rankingStrategyCode);
|
||||
}
|
||||
protoBuilder.setRankBy(rankingStrategyCodeProto);
|
||||
|
||||
return protoBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public class SearchResultsTest {
|
||||
|
||||
@Test
|
||||
public void buildSearchSpecWithoutTermMatchType() {
|
||||
assertThrows(RuntimeException.class, () -> SearchSpec.newBuilder()
|
||||
assertThrows(RuntimeException.class, () -> new SearchSpec.Builder()
|
||||
.setSchemaTypes("testSchemaType")
|
||||
.build());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user