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 android.app.appsearch; 18 19 import android.annotation.NonNull; 20 import android.app.appsearch.annotation.CanIgnoreReturnValue; 21 import android.util.ArrayMap; 22 import android.util.ArraySet; 23 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.Collection; 27 import java.util.Collections; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Objects; 31 import java.util.Set; 32 33 /** 34 * Encapsulates a request to retrieve documents by namespace and IDs from the {@link 35 * AppSearchSession} database. 36 * 37 * @see AppSearchSession#getByDocumentId 38 */ 39 public final class GetByDocumentIdRequest { 40 /** 41 * Schema type to be used in {@link GetByDocumentIdRequest.Builder#addProjection} to apply 42 * property paths to all results, excepting any types that have had their own, specific property 43 * paths set. 44 */ 45 public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*"; 46 47 private final String mNamespace; 48 private final Set<String> mIds; 49 private final Map<String, List<String>> mTypePropertyPathsMap; 50 GetByDocumentIdRequest( @onNull String namespace, @NonNull Set<String> ids, @NonNull Map<String, List<String>> typePropertyPathsMap)51 GetByDocumentIdRequest( 52 @NonNull String namespace, 53 @NonNull Set<String> ids, 54 @NonNull Map<String, List<String>> typePropertyPathsMap) { 55 mNamespace = Objects.requireNonNull(namespace); 56 mIds = Objects.requireNonNull(ids); 57 mTypePropertyPathsMap = Objects.requireNonNull(typePropertyPathsMap); 58 } 59 60 /** Returns the namespace attached to the request. */ 61 @NonNull getNamespace()62 public String getNamespace() { 63 return mNamespace; 64 } 65 66 /** Returns the set of document IDs attached to the request. */ 67 @NonNull getIds()68 public Set<String> getIds() { 69 return Collections.unmodifiableSet(mIds); 70 } 71 72 /** 73 * Returns a map from schema type to property paths to be used for projection. 74 * 75 * <p>If the map is empty, then all properties will be retrieved for all results. 76 * 77 * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this 78 * function, rather than calling it multiple times. 79 */ 80 @NonNull getProjections()81 public Map<String, List<String>> getProjections() { 82 Map<String, List<String>> copy = new ArrayMap<>(); 83 for (Map.Entry<String, List<String>> entry : mTypePropertyPathsMap.entrySet()) { 84 copy.put(entry.getKey(), new ArrayList<>(entry.getValue())); 85 } 86 return copy; 87 } 88 89 /** 90 * Returns a map from schema type to property paths to be used for projection. 91 * 92 * <p>If the map is empty, then all properties will be retrieved for all results. 93 * 94 * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this 95 * function, rather than calling it multiple times. 96 */ 97 @NonNull getProjectionPaths()98 public Map<String, List<PropertyPath>> getProjectionPaths() { 99 Map<String, List<PropertyPath>> copy = new ArrayMap<>(mTypePropertyPathsMap.size()); 100 for (Map.Entry<String, List<String>> entry : mTypePropertyPathsMap.entrySet()) { 101 List<PropertyPath> propertyPathList = new ArrayList<>(entry.getValue().size()); 102 for (String p : entry.getValue()) { 103 propertyPathList.add(new PropertyPath(p)); 104 } 105 copy.put(entry.getKey(), propertyPathList); 106 } 107 return copy; 108 } 109 110 /** 111 * Returns a map from schema type to property paths to be used for projection. 112 * 113 * <p>If the map is empty, then all properties will be retrieved for all results. 114 * 115 * <p>A more efficient version of {@link #getProjections}, but it returns a modifiable map. This 116 * is not meant to be unhidden and should only be used by internal classes. 117 * 118 * @hide 119 */ 120 @NonNull getProjectionsInternal()121 public Map<String, List<String>> getProjectionsInternal() { 122 return mTypePropertyPathsMap; 123 } 124 125 /** Builder for {@link GetByDocumentIdRequest} objects. */ 126 public static final class Builder { 127 private final String mNamespace; 128 private ArraySet<String> mIds = new ArraySet<>(); 129 private ArrayMap<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>(); 130 private boolean mBuilt = false; 131 132 /** Creates a {@link GetByDocumentIdRequest.Builder} instance. */ Builder(@onNull String namespace)133 public Builder(@NonNull String namespace) { 134 mNamespace = Objects.requireNonNull(namespace); 135 } 136 137 /** Adds one or more document IDs to the request. */ 138 @CanIgnoreReturnValue 139 @NonNull addIds(@onNull String... ids)140 public Builder addIds(@NonNull String... ids) { 141 Objects.requireNonNull(ids); 142 resetIfBuilt(); 143 return addIds(Arrays.asList(ids)); 144 } 145 146 /** Adds a collection of IDs to the request. */ 147 @CanIgnoreReturnValue 148 @NonNull addIds(@onNull Collection<String> ids)149 public Builder addIds(@NonNull Collection<String> ids) { 150 Objects.requireNonNull(ids); 151 resetIfBuilt(); 152 mIds.addAll(ids); 153 return this; 154 } 155 156 /** 157 * Adds property paths for the specified type to be used for projection. If property paths 158 * are added for a type, then only the properties referred to will be retrieved for results 159 * of that type. If a property path that is specified isn't present in a result, it will be 160 * ignored for that result. Property paths cannot be null. 161 * 162 * <p>If no property paths are added for a particular type, then all properties of results 163 * of that type will be retrieved. 164 * 165 * <p>If property path is added for the {@link 166 * GetByDocumentIdRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will 167 * apply to all results, excepting any types that have their own, specific property paths 168 * set. 169 * 170 * @see SearchSpec.Builder#addProjectionPaths 171 */ 172 @CanIgnoreReturnValue 173 @NonNull addProjection( @onNull String schemaType, @NonNull Collection<String> propertyPaths)174 public Builder addProjection( 175 @NonNull String schemaType, @NonNull Collection<String> propertyPaths) { 176 Objects.requireNonNull(schemaType); 177 Objects.requireNonNull(propertyPaths); 178 resetIfBuilt(); 179 List<String> propertyPathsList = new ArrayList<>(propertyPaths.size()); 180 for (String propertyPath : propertyPaths) { 181 Objects.requireNonNull(propertyPath); 182 propertyPathsList.add(propertyPath); 183 } 184 mProjectionTypePropertyPaths.put(schemaType, propertyPathsList); 185 return this; 186 } 187 188 /** 189 * Adds property paths for the specified type to be used for projection. If property paths 190 * are added for a type, then only the properties referred to will be retrieved for results 191 * of that type. If a property path that is specified isn't present in a result, it will be 192 * ignored for that result. Property paths cannot be null. 193 * 194 * <p>If no property paths are added for a particular type, then all properties of results 195 * of that type will be retrieved. 196 * 197 * <p>If property path is added for the {@link 198 * GetByDocumentIdRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will 199 * apply to all results, excepting any types that have their own, specific property paths 200 * set. 201 * 202 * @see SearchSpec.Builder#addProjectionPaths 203 */ 204 @CanIgnoreReturnValue 205 @NonNull addProjectionPaths( @onNull String schemaType, @NonNull Collection<PropertyPath> propertyPaths)206 public Builder addProjectionPaths( 207 @NonNull String schemaType, @NonNull Collection<PropertyPath> propertyPaths) { 208 Objects.requireNonNull(schemaType); 209 Objects.requireNonNull(propertyPaths); 210 List<String> propertyPathsList = new ArrayList<>(propertyPaths.size()); 211 for (PropertyPath propertyPath : propertyPaths) { 212 propertyPathsList.add(propertyPath.toString()); 213 } 214 return addProjection(schemaType, propertyPathsList); 215 } 216 217 /** Builds a new {@link GetByDocumentIdRequest}. */ 218 @NonNull build()219 public GetByDocumentIdRequest build() { 220 mBuilt = true; 221 return new GetByDocumentIdRequest(mNamespace, mIds, mProjectionTypePropertyPaths); 222 } 223 resetIfBuilt()224 private void resetIfBuilt() { 225 if (mBuilt) { 226 mIds = new ArraySet<>(mIds); 227 // No need to clone each propertyPathsList inside mProjectionTypePropertyPaths since 228 // the builder only replaces it, never adds to it. So even if the builder is used 229 // again, the previous one will remain with the object. 230 mProjectionTypePropertyPaths = new ArrayMap<>(mProjectionTypePropertyPaths); 231 mBuilt = false; 232 } 233 } 234 } 235 } 236