AppSearch es una solución de búsqueda integrada en el dispositivo de alto rendimiento para administrar de forma local datos estructurados almacenados. Contiene APIs para indexar y recuperar datos. con la búsqueda en el texto completo. Las aplicaciones pueden usar AppSearch para ofrecer anuncios de búsqueda, que permiten a los usuarios buscar contenido incluso sin conexión.
AppSearch proporciona las siguientes funciones:
- Una implementación de almacenamiento rápida que prioriza los dispositivos móviles con poco uso de E/S.
- Indexación y consultas eficientes de grandes conjuntos de datos
- Compatibilidad con diferentes idiomas, como inglés y español
- Clasificación de relevancia y puntuación de uso
Debido a un menor uso de E/S, AppSearch ofrece menor latencia para la indexación y la búsqueda. sobre conjuntos de datos grandes en comparación con SQLite. AppSearch simplifica las consultas entre tipos al admitir consultas individuales, mientras que SQLite fusiona los resultados de varias tablas.
Para ilustrar las funciones de AppSearch, tomemos el ejemplo de un modelo de que administra las canciones favoritas de los usuarios y les permite buscar fácilmente para ellos. Los usuarios disfrutan de música de todo el mundo con títulos de canciones de diferentes lenguajes de programación, que AppSearch admite de forma nativa la indexación y consulta. Cuando usuario busca una canción por título o nombre del artista, la aplicación simplemente pasa la solicitud a AppSearch para recuperar de forma rápida y eficiente las canciones coincidentes. El de muestra los resultados, lo que permite a los usuarios comenzar a jugar sus canciones favoritas.
Configuración
Para usar AppSearch en tu aplicación, agrega las siguientes dependencias a tu
archivo build.gradle
de la aplicación:
Groovy
dependencies { def appsearch_version = "1.1.0-alpha05" implementation "androidx.appsearch:appsearch:$appsearch_version" // Use kapt instead of annotationProcessor if writing Kotlin classes annotationProcessor "androidx.appsearch:appsearch-compiler:$appsearch_version" implementation "androidx.appsearch:appsearch-local-storage:$appsearch_version" // PlatformStorage is compatible with Android 12+ devices, and offers additional features // to LocalStorage. implementation "androidx.appsearch:appsearch-platform-storage:$appsearch_version" }
Kotlin
dependencies { val appsearch_version = "1.1.0-alpha05" implementation("androidx.appsearch:appsearch:$appsearch_version") // Use annotationProcessor instead of kapt if writing Java classes kapt("androidx.appsearch:appsearch-compiler:$appsearch_version") implementation("androidx.appsearch:appsearch-local-storage:$appsearch_version") // PlatformStorage is compatible with Android 12+ devices, and offers additional features // to LocalStorage. implementation("androidx.appsearch:appsearch-platform-storage:$appsearch_version") }
Conceptos de AppSearch
En el siguiente diagrama, se ilustran los conceptos de AppSearch y sus interacciones.
Base de datos y sesión
Una base de datos de AppSearch es una colección de documentos que se ajusta a la base de datos . Las aplicaciones cliente crean una base de datos proporcionando su contextual y un nombre de base de datos. Solo la aplicación puede abrir las bases de datos que las crearon. Cuando se abre una base de datos, se devuelve una sesión para interactuar con la base de datos. La sesión es el punto de entrada para llamar a las APIs de AppSearch y permanece abierta hasta que la aplicación cliente la cierre.
Esquema y tipos de esquema
Un esquema representa la estructura organizativa de los datos en una AppSearch en la base de datos.
El esquema se compone de tipos de esquema que representan tipos únicos de datos. Los tipos de esquema constan de propiedades que contienen un nombre, un tipo de datos y cardinalidad. Una vez que se agrega un tipo de esquema al esquema de la base de datos, los documentos de Ese tipo de esquema se puede crear y agregar a la base de datos.
Documentos
En AppSearch, una unidad de datos se representa como un documento. Cada documento de un La base de datos de AppSearch se identifica de forma única por su ID y espacio de nombres. Espacios de nombres se usan para separar datos de diferentes fuentes cuando solo una fuente necesita que se consultarán, como cuentas de usuario.
Los documentos contienen una marca de tiempo de creación, un tiempo de actividad (TTL) y una puntuación que para la clasificación durante la recuperación. A un documento también se le asigna un esquema que describa las propiedades adicionales de los datos que debe tener el documento.
Una clase de documento es una abstracción de un documento. Contiene campos con anotaciones que representan el contenido de un documento. De forma predeterminada, el nombre del documento class establece el nombre del tipo de esquema.
Buscar
Los documentos se indexan y se pueden buscar proporcionando una consulta. Un documento es coincide y se incluye en los resultados de la búsqueda si contiene los términos de la consulta o coincide con otra especificación de búsqueda. Los resultados se ordenan según su de puntuación y clasificación. Los resultados de la búsqueda se representan con páginas que puedes de forma secuencial.
AppSearch ofrece personalizaciones. para la búsqueda, como los filtros, la configuración del tamaño de la página y los fragmentos.
Almacenamiento de la plataforma frente al almacenamiento local
AppSearch ofrece dos soluciones de almacenamiento: LocalStorage y PlatformStorage. Con LocalStorage, tu aplicación administra un índice específico de la aplicación que se encuentra el directorio de datos de tu aplicación. Con PlatformStorage, tu aplicación contribuye al índice central de todo el sistema. Acceso a los datos dentro del índice central está restringido a los datos que aportó tu aplicación y a los datos que se compartida explícitamente contigo por otra aplicación. Tanto LocalStorage como PlatformStorage comparte la misma API y se puede intercambiar según la API de versión:
Kotlin
if (BuildCompat.isAtLeastS()) { appSearchSessionFuture.setFuture( PlatformStorage.createSearchSession( PlatformStorage.SearchContext.Builder(mContext, DATABASE_NAME) .build() ) ) } else { appSearchSessionFuture.setFuture( LocalStorage.createSearchSession( LocalStorage.SearchContext.Builder(mContext, DATABASE_NAME) .build() ) ) }
Java
if (BuildCompat.isAtLeastS()) { mAppSearchSessionFuture.setFuture(PlatformStorage.createSearchSession( new PlatformStorage.SearchContext.Builder(mContext, DATABASE_NAME) .build())); } else { mAppSearchSessionFuture.setFuture(LocalStorage.createSearchSession( new LocalStorage.SearchContext.Builder(mContext, DATABASE_NAME) .build())); }
Con PlatformStorage, tu aplicación puede compartir datos de forma segura con otros aplicaciones para permitirles también realizar búsquedas en los datos de tu aplicación. Solo lectura el uso compartido de datos de la aplicación se concede por medio de un protocolo de enlace de certificados la otra aplicación tiene permiso para leer los datos. Más información sobre esta API en la documentación de setSchemaTypeVisibilityForPackage().
Además, los datos que se indexan se pueden mostrar en las superficies de la IU del sistema. Las aplicaciones pueden rechazar que algunos o todos sus datos se muestren en el Sistema plataformas de IU. Obtén más información sobre esta API en la documentación de setSchemaTypeDisplayedBySystem().
Funciones | LocalStorage (compatible with Android 4.0+) |
PlatformStorage (compatible with Android 12+) |
---|---|---|
Efficient full-text search |
||
Multi-language support |
||
Reduced binary size |
||
Application-to-application data sharing |
||
Capability to display data on System UI surfaces |
||
Unlimited document size and count can be indexed |
||
Faster operations without additional binder latency |
Existen compensaciones adicionales que se deben considerar cuando se elige entre LocalStorage. y PlatformStorage. Como PlatformStorage une las APIs de Jetpack en el AppSearch, el impacto del tamaño del APK es mínimo en comparación con el uso Almacenamiento local. Sin embargo, esto también significa que las operaciones de AppSearch generan latencia de Binder cuando se llama al servicio del sistema AppSearch. Con PlatformStorage, AppSearch limita la cantidad de documentos y el tamaño de los documentos que se usan en una aplicación pueden indexarse para garantizar un índice central eficiente.
Comienza a usar AppSearch
En el ejemplo de esta sección, se muestra cómo usar las APIs de AppSearch para integrar con una aplicación hipotética para guardar notas.
Escribe una clase de documento
El primer paso para integrar AppSearch es escribir una clase de documento en
describir los datos que se insertarán en la base de datos. Marca una clase como clase de documento
mediante @Document
Annotation.Puedes usar instancias de la clase document para colocar documentos en y
recuperar documentos de la base de datos.
El siguiente código define una clase de documento de notas con una
@Document.StringProperty
anotado
para indexar el texto de un objeto Note.
Kotlin
@Document public data class Note( // Required field for a document class. All documents MUST have a namespace. @Document.Namespace val namespace: String, // Required field for a document class. All documents MUST have an Id. @Document.Id val id: String, // Optional field for a document class, used to set the score of the // document. If this is not included in a document class, the score is set // to a default of 0. @Document.Score val score: Int, // Optional field for a document class, used to index a note's text for this // document class. @Document.StringProperty(indexingType = AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) val text: String )
Java
@Document public class Note { // Required field for a document class. All documents MUST have a namespace. @Document.Namespace private final String namespace; // Required field for a document class. All documents MUST have an Id. @Document.Id private final String id; // Optional field for a document class, used to set the score of the // document. If this is not included in a document class, the score is set // to a default of 0. @Document.Score private final int score; // Optional field for a document class, used to index a note's text for this // document class. @Document.StringProperty(indexingType = StringPropertyConfig.INDEXING_TYPE_PREFIXES) private final String text; Note(@NonNull String id, @NonNull String namespace, int score, @NonNull String text) { this.id = Objects.requireNonNull(id); this.namespace = Objects.requireNonNull(namespace); this.score = score; this.text = Objects.requireNonNull(text); } @NonNull public String getNamespace() { return namespace; } @NonNull public String getId() { return id; } public int getScore() { return score; } @NonNull public String getText() { return text; } }
Cómo abrir una base de datos
Debes crear una base de datos antes de trabajar con documentos. El siguiente código
Crea una nueva base de datos con el nombre notes_app
y obtiene un ListenableFuture
.
para un elemento AppSearchSession
,
que representa la conexión a la base de datos y proporciona las APIs para
las operaciones de base de datos.
Kotlin
val context: Context = getApplicationContext() val sessionFuture = LocalStorage.createSearchSession( LocalStorage.SearchContext.Builder(context, /*databaseName=*/"notes_app") .build() )
Java
Context context = getApplicationContext(); ListenableFuture<AppSearchSession> sessionFuture = LocalStorage.createSearchSession( new LocalStorage.SearchContext.Builder(context, /*databaseName=*/ "notes_app") .build() );
Configura un esquema
Debes configurar un esquema antes de poder insertar documentos y recuperar documentos de la base de datos. El esquema de la base de datos consta de diferentes tipos de datos estructurados, denominados “tipos de esquema”. El siguiente código configura proporcionando la clase de documento como un tipo de esquema.
Kotlin
val setSchemaRequest = SetSchemaRequest.Builder().addDocumentClasses(Note::class.java) .build() val setSchemaFuture = Futures.transformAsync( sessionFuture, { session -> session?.setSchema(setSchemaRequest) }, mExecutor )
Java
SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder().addDocumentClasses(Note.class) .build(); ListenableFuture<SetSchemaResponse> setSchemaFuture = Futures.transformAsync(sessionFuture, session -> session.setSchema(setSchemaRequest), mExecutor);
Cómo colocar un documento en la base de datos
Una vez que se agrega un tipo de esquema, puedes agregar documentos de ese tipo a la base de datos.
El siguiente código compila un documento de tipo de esquema Note
con Note
de clase de documentos. Establece el espacio de nombres del documento user1
para representar un
usuario arbitrario de esta muestra. Luego, el documento se inserta en la base de datos
y se adjunta un objeto de escucha para procesar el resultado de la operación put.
Kotlin
val note = Note( namespace="user1", id="noteId", score=10, text="Buy fresh fruit" ) val putRequest = PutDocumentsRequest.Builder().addDocuments(note).build() val putFuture = Futures.transformAsync( sessionFuture, { session -> session?.put(putRequest) }, mExecutor ) Futures.addCallback( putFuture, object : FutureCallback<AppSearchBatchResult<String, Void>?> { override fun onSuccess(result: AppSearchBatchResult<String, Void>?) { // Gets map of successful results from Id to Void val successfulResults = result?.successes // Gets map of failed results from Id to AppSearchResult val failedResults = result?.failures } override fun onFailure(t: Throwable) { Log.e(TAG, "Failed to put documents.", t) } }, mExecutor )
Java
Note note = new Note(/*namespace=*/"user1", /*id=*/ "noteId", /*score=*/ 10, /*text=*/ "Buy fresh fruit!"); PutDocumentsRequest putRequest = new PutDocumentsRequest.Builder().addDocuments(note) .build(); ListenableFuture<AppSearchBatchResult<String, Void>> putFuture = Futures.transformAsync(sessionFuture, session -> session.put(putRequest), mExecutor); Futures.addCallback(putFuture, new FutureCallback<AppSearchBatchResult<String, Void>>() { @Override public void onSuccess(@Nullable AppSearchBatchResult<String, Void> result) { // Gets map of successful results from Id to Void Map<String, Void> successfulResults = result.getSuccesses(); // Gets map of failed results from Id to AppSearchResult Map<String, AppSearchResult<Void>> failedResults = result.getFailures(); } @Override public void onFailure(@NonNull Throwable t) { Log.e(TAG, "Failed to put documents.", t); } }, mExecutor);
Buscar
Puedes buscar documentos que estén indexados a través de las operaciones de búsqueda que se abordan en
esta sección. El siguiente código realiza consultas para el término "fruta" por encima de
base de datos para los documentos que pertenecen al espacio de nombres user1
.
Kotlin
val searchSpec = SearchSpec.Builder() .addFilterNamespaces("user1") .build(); val searchFuture = Futures.transform( sessionFuture, { session -> session?.search("fruit", searchSpec) }, mExecutor ) Futures.addCallback( searchFuture, object : FutureCallback<SearchResults> { override fun onSuccess(searchResults: SearchResults?) { iterateSearchResults(searchResults) } override fun onFailure(t: Throwable?) { Log.e("TAG", "Failed to search notes in AppSearch.", t) } }, mExecutor )
Java
SearchSpec searchSpec = new SearchSpec.Builder() .addFilterNamespaces("user1") .build(); ListenableFuture<SearchResults> searchFuture = Futures.transform(sessionFuture, session -> session.search("fruit", searchSpec), mExecutor); Futures.addCallback(searchFuture, new FutureCallback<SearchResults>() { @Override public void onSuccess(@Nullable SearchResults searchResults) { iterateSearchResults(searchResults); } @Override public void onFailure(@NonNull Throwable t) { Log.e(TAG, "Failed to search notes in AppSearch.", t); } }, mExecutor);
Itera a través de SearchResults.
Las búsquedas muestran un SearchResults
, que otorga acceso a las páginas de objetos SearchResult
. Cada SearchResult
contiene su GenericDocument
coincidente, la forma general de un
documento al que se convertirán todos los documentos. El siguiente código obtiene la primera
página de resultados de la búsqueda y vuelve a convertir el resultado en un documento Note
.
Kotlin
Futures.transform( searchResults?.nextPage, { page: List<SearchResult>? -> // Gets GenericDocument from SearchResult. val genericDocument: GenericDocument = page!![0].genericDocument val schemaType = genericDocument.schemaType val note: Note? = try { if (schemaType == "Note") { // Converts GenericDocument object to Note object. genericDocument.toDocumentClass(Note::class.java) } else null } catch (e: AppSearchException) { Log.e( TAG, "Failed to convert GenericDocument to Note", e ) null } note }, mExecutor )
Java
Futures.transform(searchResults.getNextPage(), page -> { // Gets GenericDocument from SearchResult. GenericDocument genericDocument = page.get(0).getGenericDocument(); String schemaType = genericDocument.getSchemaType(); Note note = null; if (schemaType.equals("Note")) { try { // Converts GenericDocument object to Note object. note = genericDocument.toDocumentClass(Note.class); } catch (AppSearchException e) { Log.e(TAG, "Failed to convert GenericDocument to Note", e); } } return note; }, mExecutor);
Cómo quitar un documento
Cuando el usuario borra una nota, la app borra el Note
correspondiente
documento de la base de datos. Esto garantiza que la nota ya no aparezca en
para tus consultas. El siguiente código hace una solicitud explícita para quitar Note
documento de la base de datos por ID.
Kotlin
val removeRequest = RemoveByDocumentIdRequest.Builder("user1") .addIds("noteId") .build() val removeFuture = Futures.transformAsync( sessionFuture, { session -> session?.remove(removeRequest) }, mExecutor )
Java
RemoveByDocumentIdRequest removeRequest = new RemoveByDocumentIdRequest.Builder("user1") .addIds("noteId") .build(); ListenableFuture<AppSearchBatchResult<String, Void>> removeFuture = Futures.transformAsync(sessionFuture, session -> session.remove(removeRequest), mExecutor);
Conservar en el disco
Las actualizaciones de una base de datos deben conservarse periódicamente en el disco llamando
requestFlush()
El
El siguiente código llama a requestFlush()
con un objeto de escucha para determinar si la llamada
tuvo éxito.
Kotlin
val requestFlushFuture = Futures.transformAsync( sessionFuture, { session -> session?.requestFlush() }, mExecutor ) Futures.addCallback(requestFlushFuture, object : FutureCallback<Void?> { override fun onSuccess(result: Void?) { // Success! Database updates have been persisted to disk. } override fun onFailure(t: Throwable) { Log.e(TAG, "Failed to flush database updates.", t) } }, mExecutor)
Java
ListenableFuture<Void> requestFlushFuture = Futures.transformAsync(sessionFuture, session -> session.requestFlush(), mExecutor); Futures.addCallback(requestFlushFuture, new FutureCallback<Void>() { @Override public void onSuccess(@Nullable Void result) { // Success! Database updates have been persisted to disk. } @Override public void onFailure(@NonNull Throwable t) { Log.e(TAG, "Failed to flush database updates.", t); } }, mExecutor);
Cómo cerrar una sesión
Un objeto AppSearchSession
Se debería cerrar cuando una aplicación ya no llame a ninguna base de datos.
las operaciones. El siguiente código cierra la sesión de AppSearch que se abrió
y conserva todas las actualizaciones en el disco.
Kotlin
val closeFuture = Futures.transform<AppSearchSession, Unit>(sessionFuture, { session -> session?.close() Unit }, mExecutor )
Java
ListenableFuture<Void> closeFuture = Futures.transform(sessionFuture, session -> { session.close(); return null; }, mExecutor);
Recursos adicionales
Para obtener más información sobre AppSearch, consulta los siguientes recursos adicionales:
Ejemplos
- Ejemplo de Android AppSearch (Kotlin), una app para tomar notas que usa AppSearch para indexar las notas de un usuario y permite que los usuarios para buscar en sus notas.
Envía comentarios
Comparte tus comentarios e ideas con nosotros por medio de estos recursos:
Herramienta de seguimiento de errores
Informa los errores para que podamos corregirlos.