• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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