1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.appsearch.external.localstorage.converter; 18 19 import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; 20 21 import android.annotation.NonNull; 22 import android.app.appsearch.GenericDocument; 23 import android.app.appsearch.SearchResult; 24 import android.app.appsearch.SearchResultPage; 25 import android.os.Bundle; 26 27 import com.android.internal.util.Preconditions; 28 29 import com.google.android.icing.proto.SchemaTypeConfigProto; 30 import com.google.android.icing.proto.SearchResultProto; 31 import com.google.android.icing.proto.SearchResultProtoOrBuilder; 32 import com.google.android.icing.proto.SnippetMatchProto; 33 import com.google.android.icing.proto.SnippetProto; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Map; 38 39 /** 40 * Translates a {@link SearchResultProto} into {@link SearchResult}s. 41 * 42 * @hide 43 */ 44 public class SearchResultToProtoConverter { SearchResultToProtoConverter()45 private SearchResultToProtoConverter() {} 46 47 /** 48 * Translate a {@link SearchResultProto} into {@link SearchResultPage}. 49 * 50 * @param proto The {@link SearchResultProto} containing results. 51 * @param packageNames A parallel array of package names. The package name at index 'i' of this 52 * list should be the package that indexed the document at index 'i' of proto.getResults(i). 53 * @param databaseNames A parallel array of database names. The database name at index 'i' of 54 * this list shold be the database that indexed the document at index 'i' of 55 * proto.getResults(i). 56 * @param schemaMap A map of prefixes to an inner-map of prefixed schema type to 57 * SchemaTypeConfigProtos, used for setting a default value for results with DocumentProtos 58 * that have empty values. 59 * @return {@link SearchResultPage} of results. 60 */ 61 @NonNull toSearchResultPage( @onNull SearchResultProtoOrBuilder proto, @NonNull List<String> packageNames, @NonNull List<String> databaseNames, @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap)62 public static SearchResultPage toSearchResultPage( 63 @NonNull SearchResultProtoOrBuilder proto, 64 @NonNull List<String> packageNames, 65 @NonNull List<String> databaseNames, 66 @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) { 67 Preconditions.checkArgument( 68 proto.getResultsCount() == packageNames.size(), 69 "Size of results does not match the number of package names."); 70 Bundle bundle = new Bundle(); 71 bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); 72 ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount()); 73 for (int i = 0; i < proto.getResultsCount(); i++) { 74 String prefix = createPrefix(packageNames.get(i), databaseNames.get(i)); 75 Map<String, SchemaTypeConfigProto> schemaTypeMap = schemaMap.get(prefix); 76 SearchResult result = 77 toSearchResult( 78 proto.getResults(i), 79 packageNames.get(i), 80 databaseNames.get(i), 81 schemaTypeMap); 82 resultBundles.add(result.getBundle()); 83 } 84 bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); 85 return new SearchResultPage(bundle); 86 } 87 88 /** 89 * Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. 90 * 91 * @param proto The proto to be converted. 92 * @param packageName The package name associated with the document in {@code proto}. 93 * @param databaseName The database name associated with the document in {@code proto}. 94 * @param schemaTypeToProtoMap A map of prefixed schema types to their corresponding 95 * SchemaTypeConfigProto, used for setting a default value for results with DocumentProtos 96 * that have empty values. 97 * @return A {@link SearchResult} bundle. 98 */ 99 @NonNull toSearchResult( @onNull SearchResultProto.ResultProtoOrBuilder proto, @NonNull String packageName, @NonNull String databaseName, @NonNull Map<String, SchemaTypeConfigProto> schemaTypeToProtoMap)100 private static SearchResult toSearchResult( 101 @NonNull SearchResultProto.ResultProtoOrBuilder proto, 102 @NonNull String packageName, 103 @NonNull String databaseName, 104 @NonNull Map<String, SchemaTypeConfigProto> schemaTypeToProtoMap) { 105 String prefix = createPrefix(packageName, databaseName); 106 GenericDocument document = 107 GenericDocumentToProtoConverter.toGenericDocument( 108 proto.getDocument(), prefix, schemaTypeToProtoMap); 109 SearchResult.Builder builder = 110 new SearchResult.Builder(packageName, databaseName) 111 .setGenericDocument(document) 112 .setRankingSignal(proto.getScore()); 113 if (proto.hasSnippet()) { 114 for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) { 115 SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i); 116 for (int j = 0; j < entry.getSnippetMatchesCount(); j++) { 117 SearchResult.MatchInfo matchInfo = 118 toMatchInfo(entry.getSnippetMatches(j), entry.getPropertyName()); 119 builder.addMatchInfo(matchInfo); 120 } 121 } 122 } 123 return builder.build(); 124 } 125 toMatchInfo( @onNull SnippetMatchProto snippetMatchProto, @NonNull String propertyPath)126 private static SearchResult.MatchInfo toMatchInfo( 127 @NonNull SnippetMatchProto snippetMatchProto, @NonNull String propertyPath) { 128 return new SearchResult.MatchInfo.Builder(propertyPath) 129 .setExactMatchRange( 130 new SearchResult.MatchRange( 131 snippetMatchProto.getExactMatchUtf16Position(), 132 snippetMatchProto.getExactMatchUtf16Position() 133 + snippetMatchProto.getExactMatchUtf16Length())) 134 .setSnippetRange( 135 new SearchResult.MatchRange( 136 snippetMatchProto.getWindowUtf16Position(), 137 snippetMatchProto.getWindowUtf16Position() 138 + snippetMatchProto.getWindowUtf16Length())) 139 .build(); 140 } 141 } 142