1 /* 2 * Copyright 2023 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; 18 19 import android.app.appsearch.SearchSpec; 20 21 import com.android.appsearch.flags.Flags; 22 23 import com.google.android.icing.proto.IcingSearchEngineOptions; 24 25 import org.jspecify.annotations.NonNull; 26 27 /** 28 * An interface exposing the optional config flags in {@link IcingSearchEngineOptions} used to 29 * instantiate {@link com.google.android.icing.IcingSearchEngine}, as well as other additional 30 * config flags for IcingSearchEngine. 31 */ 32 public interface IcingOptionsConfig { 33 // Defaults from IcingSearchEngineOptions proto 34 int DEFAULT_MAX_TOKEN_LENGTH = 30; 35 36 int DEFAULT_INDEX_MERGE_SIZE = 1048576; // 1 MiB 37 38 boolean DEFAULT_DOCUMENT_STORE_NAMESPACE_ID_FINGERPRINT = false; 39 40 float DEFAULT_OPTIMIZE_REBUILD_INDEX_THRESHOLD = 0.9f; 41 42 /** 43 * The default compression level in IcingSearchEngineOptions proto matches the 44 * previously-hardcoded document compression level in Icing (which is 3). 45 */ 46 int DEFAULT_COMPRESSION_LEVEL = 3; 47 48 boolean DEFAULT_USE_PREMAPPING_WITH_FILE_BACKED_VECTOR = false; 49 50 boolean DEFAULT_USE_PERSISTENT_HASH_MAP = false; 51 52 int DEFAULT_MAX_PAGE_BYTES_LIMIT = Integer.MAX_VALUE; 53 54 /** 55 * The default threshold for integer index bucket split. 65536 is picked based on benchmark 56 * (Icing integer-index-storage_benchmark.cc). 57 * 58 * <ul> 59 * <li>There will be only 16 buckets when indexing 1M integers, which improves the performance 60 * of numeric search range query. 61 * <li>It also increases # of hits to read for numeric search exact query, but the overall 62 * query latency is still reasonable. 63 * </ul> 64 */ 65 int DEFAULT_INTEGER_INDEX_BUCKET_SPLIT_THRESHOLD = 65536; 66 67 boolean DEFAULT_LITE_INDEX_SORT_AT_INDEXING = true; 68 69 /** 70 * The default sort threshold for the lite index when sort at indexing is enabled. 8192 is 71 * picked based on Icing microbenchmarks (icing-search-engine_benchmarks.cc). 72 */ 73 int DEFAULT_LITE_INDEX_SORT_SIZE = 8192; // 8Kib 74 75 boolean DEFAULT_USE_NEW_QUALIFIED_ID_JOIN_INDEX = false; 76 77 boolean DEFAULT_BUILD_PROPERTY_EXISTENCE_METADATA_HITS = false; 78 79 long DEFAULT_ORPHAN_BLOB_TIME_TO_LIVE_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week. 80 81 String DEFAULT_ICU_DATA_FILE_ABSOLUTE_PATH = ""; 82 83 /** 84 * The maximum allowable token length. All tokens in excess of this size will be truncated to 85 * max_token_length before being indexed. 86 * 87 * <p>Clients may use this option to prevent unnecessary indexing of long tokens. Depending on 88 * the use case, indexing all of 'Supercalifragilisticexpialidocious' may be unnecessary - a 89 * user is unlikely to type that entire query. So only indexing the first n bytes may still 90 * provide the desired behavior without wasting resources. 91 */ getMaxTokenLength()92 int getMaxTokenLength(); 93 94 /** 95 * The size (measured in bytes) at which Icing's internal indices should be merged. Icing 96 * buffers changes together before merging them into a more compact format. When the buffer 97 * exceeds index_merge_size during a Put operation, the buffer is merged into the larger, more 98 * compact index. 99 * 100 * <p>This more compact index is more efficient to search over as the index grows larger and has 101 * smaller system health impact. 102 * 103 * <p>Setting a low index_merge_size increases the frequency of merges - increasing 104 * indexing-time latency and flash wear. Setting a high index_merge_size leads to larger 105 * resource usage and higher query latency. 106 */ getIndexMergeSize()107 int getIndexMergeSize(); 108 109 /** 110 * Whether to use namespace id or namespace name to build up fingerprint for 111 * document_key_mapper_ and corpus_mapper_ in document store. 112 */ getDocumentStoreNamespaceIdFingerprint()113 boolean getDocumentStoreNamespaceIdFingerprint(); 114 115 /** 116 * The threshold of the percentage of invalid documents at which to rebuild index during 117 * optimize. 118 * 119 * <p>We rebuild index if and only if |invalid_documents| / |all_documents| >= threshold. 120 * 121 * <p>Rebuilding the index could be faster than optimizing the index if we have removed most of 122 * the documents. Based on benchmarks, 85%~95% seems to be a good threshold for most cases. 123 */ getOptimizeRebuildIndexThreshold()124 float getOptimizeRebuildIndexThreshold(); 125 126 /** 127 * The level of gzip compression for documents in the Icing document store. 128 * 129 * <p>NO_COMPRESSION = 0, BEST_SPEED = 1, BEST_COMPRESSION = 9 130 */ getCompressionLevel()131 int getCompressionLevel(); 132 133 /** 134 * Whether to allow circular references between schema types for the schema definition. 135 * 136 * <p>Even when set to true, circular references are still not allowed in the following cases: 137 * 1. All edges of a cycle have index_nested_properties=true 2. One of the types in the cycle 138 * has a joinable property, or depends on a type with a joinable property. 139 */ getAllowCircularSchemaDefinitions()140 boolean getAllowCircularSchemaDefinitions(); 141 142 /** 143 * Flag for {@link com.google.android.icing.proto.SearchSpecProto}. 144 * 145 * <p>Whether to use the read-only implementation of IcingSearchEngine::Search. 146 * 147 * <p>The read-only version enables multiple queries to be performed concurrently as it only 148 * acquires the read lock at IcingSearchEngine's level. Finer-grained locks are implemented 149 * around code paths that write changes to Icing during Search. 150 */ getUseReadOnlySearch()151 boolean getUseReadOnlySearch(); 152 153 /** 154 * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 155 * 156 * <p>Whether or not to pre-map the potential memory region used by the PersistentHashMap. This 157 * will avoid the need to re-map the mmapping used by PersistentHashMap whenever the underlying 158 * storage grows. 159 */ getUsePreMappingWithFileBackedVector()160 boolean getUsePreMappingWithFileBackedVector(); 161 162 /** 163 * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 164 * 165 * <p>Whether or not to use the PersistentHashMap in the QualifiedIdTypeJoinableIndex. If false, 166 * we will use the old IcingDynamicTrie to store key value pairs. 167 */ getUsePersistentHashMap()168 boolean getUsePersistentHashMap(); 169 170 /** 171 * Flag for {@link com.google.android.icing.proto.ResultSpecProto}. 172 * 173 * <p>The maximum byte size to allow in a single page. This limit is only loosely binding. 174 * AppSearch will add results to the page until either 1) AppSearch has retrieved {@link 175 * SearchSpec#getResultCountPerPage()} results or 2) total size of the page exceeds this value. 176 * Therefore, AppSearch will always retrieve at least a single result, even if that result 177 * exceeds this limit. 178 */ getMaxPageBytesLimit()179 int getMaxPageBytesLimit(); 180 181 /** 182 * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 183 * 184 * <p>Threshold for integer index bucket split. Integer index stores hits in several buckets, 185 * and splits if # of hits in a single bucket exceed the threshold. Splitting bucket accelerates 186 * numeric search exact query, but potentially downgrades the performance of range query. 187 * 188 * <p>This flag is for rolling out new threshold 65536. If identifying any issues, then change 189 * it back to 341 (the previous bucket split threshold, capacity of full max-sized posting 190 * list). 191 */ getIntegerIndexBucketSplitThreshold()192 int getIntegerIndexBucketSplitThreshold(); 193 194 /** 195 * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 196 * 197 * <p>Whether Icing should sort and merge its lite index HitBuffer unsorted tail at indexing 198 * time. 199 * 200 * <p>If set to true, the HitBuffer will be sorted at indexing time after exceeding the sort 201 * threshold. If false, the HifBuffer will be sorted at querying time, before the first query 202 * after inserting new elements into the HitBuffer. 203 */ getLiteIndexSortAtIndexing()204 boolean getLiteIndexSortAtIndexing(); 205 206 /** 207 * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 208 * 209 * <p>Size (in bytes) at which Icing's lite index should sort and merge the HitBuffer's unsorted 210 * tail into the sorted head for sorting at indexing time. Size specified here is unsorted tail 211 * section. 212 * 213 * <p>Setting a lower sort size reduces querying latency at the expense of indexing latency. 214 */ getLiteIndexSortSize()215 int getLiteIndexSortSize(); 216 217 /** 218 * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 219 * 220 * <p>Whether to use the new qualified Id join index. 221 */ getUseNewQualifiedIdJoinIndex()222 boolean getUseNewQualifiedIdJoinIndex(); 223 224 /** 225 * Flag for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 226 * 227 * <p>Whether to build the metadata hits used for property existence check, which is required to 228 * support the hasProperty function in advanced query. 229 */ getBuildPropertyExistenceMetadataHits()230 boolean getBuildPropertyExistenceMetadataHits(); 231 232 /** 233 * Config for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 234 * 235 * <p>The maximum time in millisecond for a orphan blob to get recycled and deleted if there is 236 * no reference document linked to it. 237 */ getOrphanBlobTimeToLiveMs()238 long getOrphanBlobTimeToLiveMs(); 239 240 /** 241 * Config for {@link com.google.android.icing.proto.IcingSearchEngineOptions}. 242 * 243 * <p>The absolute path to the ICU data file. If a valid path has been provided, it will be used 244 * to initialize ICU. The path is not available in Jetpack and Framework. This method is 245 * functionally no-op and returns an empty string. 246 */ getIcuDataFileAbsolutePath()247 @NonNull String getIcuDataFileAbsolutePath(); 248 249 /** 250 * Converts to an {@link IcingSearchEngineOptions} instance. 251 * 252 * @param baseDir base directory of the icing instance. 253 */ toIcingSearchEngineOptions(@onNull String baseDir)254 default @NonNull IcingSearchEngineOptions toIcingSearchEngineOptions(@NonNull String baseDir) { 255 return IcingSearchEngineOptions.newBuilder() 256 .setBaseDir(baseDir) 257 .setMaxTokenLength(getMaxTokenLength()) 258 .setIndexMergeSize(getIndexMergeSize()) 259 .setDocumentStoreNamespaceIdFingerprint(getDocumentStoreNamespaceIdFingerprint()) 260 .setOptimizeRebuildIndexThreshold(getOptimizeRebuildIndexThreshold()) 261 .setCompressionLevel(getCompressionLevel()) 262 .setAllowCircularSchemaDefinitions(getAllowCircularSchemaDefinitions()) 263 .setPreMappingFbv(getUsePreMappingWithFileBackedVector()) 264 .setUsePersistentHashMap(getUsePersistentHashMap()) 265 .setIntegerIndexBucketSplitThreshold(getIntegerIndexBucketSplitThreshold()) 266 .setLiteIndexSortAtIndexing(getLiteIndexSortAtIndexing()) 267 .setLiteIndexSortSize(getLiteIndexSortSize()) 268 .setUseNewQualifiedIdJoinIndex(getUseNewQualifiedIdJoinIndex()) 269 .setBuildPropertyExistenceMetadataHits(getBuildPropertyExistenceMetadataHits()) 270 .setEnableBlobStore(Flags.enableBlobStore()) 271 .setOrphanBlobTimeToLiveMs(getOrphanBlobTimeToLiveMs()) 272 .setEnableEmbeddingIndex(Flags.enableSchemaEmbeddingPropertyConfig()) 273 .setEnableEmbeddingQuantization(Flags.enableSchemaEmbeddingQuantization()) 274 .setEnableScorableProperties(Flags.enableScorableProperty()) 275 .setIcuDataFileAbsolutePath(getIcuDataFileAbsolutePath()) 276 .setManageBlobFiles(!Flags.enableAppSearchManageBlobFiles()) 277 // Join index v3 is a prerequisite for delete propagation. 278 .setEnableDeletePropagationFrom( 279 Flags.enableDeletePropagationType() && Flags.enableQualifiedIdJoinIndexV3()) 280 .setCalculateTimeSinceLastAttemptedOptimize( 281 Flags.enableCalculateTimeSinceLastAttemptedOptimize()) 282 .setEnableQualifiedIdJoinIndexV3(Flags.enableQualifiedIdJoinIndexV3()) 283 .setEnableSoftIndexRestoration(Flags.enableSoftIndexRestoration()) 284 .setEnableMarkerFileForOptimize(Flags.enableMarkerFileForOptimize()) 285 .setReleaseBackupSchemaFileIfOverlayPresent( 286 Flags.enableReleaseBackupSchemaFileIfOverlayPresent()) 287 .build(); 288 } 289 } 290