Merge "Merge Jetpack SearchSpec work from last two quarters."

This commit is contained in:
Alexander Dorokhine
2020-10-08 20:09:50 +00:00
committed by Android (Google) Code Review
6 changed files with 284 additions and 134 deletions

View File

@@ -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);
}

View File

@@ -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}&lt;{@link AppSearchResult}&lt;{@link byte[]}&gt;&gt;
* 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);
/**

View File

@@ -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);
}
}
}

View File

@@ -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 {

View File

@@ -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();
}
}

View File

@@ -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());
}