AppSearch to zaawansowane rozwiązanie do wyszukiwania działające na urządzeniu do zarządzania lokalnie. przechowywane, uporządkowane dane. Zawiera interfejsy API do indeksowania i pobierania danych za pomocą wyszukiwania pełnotekstowego. Aplikacje mogą korzystać z AppSearch, aby oferować niestandardowe elementy w aplikacji dzięki funkcji wyszukiwania, która umożliwia wyszukiwanie treści nawet offline.
![Diagram przedstawiający indeksowanie i wyszukiwanie w AppSearch](https://developer.android.google.cn/static/images/guide/topics/search/appsearch.png?authuser=19&hl=pl)
AppSearch udostępnia następujące funkcje:
- Szybka implementacja pamięci masowej na urządzenia mobilne i niewielkie wykorzystanie wejścia/wyjścia
- bardzo skuteczne indeksowanie dużych zbiorów danych i wykonywanie na nich zapytań,
- obsługa wielu języków, np. angielski i hiszpański.
- Ranking trafności i ocena wykorzystania
Ze względu na mniejszą liczbę operacji wejścia-wyjścia, AppSearch oferuje krótszy czas oczekiwania na indeksowanie i wyszukiwanie na dużych zbiorach danych w porównaniu z SQLite. AppSearch upraszcza zapytania obejmujące różne typy obsługują pojedyncze zapytania, a SQLite scala wyniki z wielu tabel.
Aby zilustrować funkcje AppSearch, przyjrzyjmy się przykładowi muzyki aplikacja do zarządzania ulubionymi utworami użytkowników i łatwe wyszukiwanie dla nich. Użytkownicy słuchają muzyki z całego świata, a tytuły piosenek mają różne , dla których AppSearch natywnie obsługuje indeksowanie i zapytania. Gdy użytkownik wyszukuje utwór według tytułu lub nazwy wykonawcy, aplikacja po prostu przechodzi żądania do AppSearch, aby szybko i sprawnie pobrać pasujące utwory. aplikacja wyświetla wyniki, pozwalając użytkownikom szybko rozpocząć grę ulubione piosenki.
Konfiguracja
Aby używać AppSearch w swojej aplikacji, dodaj te zależności do
plik build.gradle
aplikacji:
Odlotowe
dependencies { def appsearch_version = "1.1.0-alpha04" 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-alpha04" 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") }
Pojęcia związane z AppSearch
Poniższy diagram przedstawia pojęcia AppSearch i ich interakcje.
Rysunek 1. Schemat pojęć związanych z AppSearch: baza danych AppSearch, schemat
typy schematów, dokumenty, sesje i wyszukiwanie.
Baza danych i sesja
Baza danych AppSearch to zbiór dokumentów zgodnych z bazą danych schemat. Aplikacje klienckie tworzą bazę danych, dostarczając swoją aplikację kontekstu i nazwy bazy danych. Bazy danych mogą być otwierane tylko przez aplikację który ich stworzył. Po otwarciu bazy danych następuje sesja, która może rozpocząć interakcję. z bazą danych. Sesja jest punktem wejścia do wywoływania interfejsów AppSearch API i pozostanie otwarta, dopóki nie zostanie zamknięta przez aplikację kliencką.
Typy schematów i schematów
Schemat reprezentuje strukturę organizacyjną danych w AppSearch w bazie danych.
Schemat składa się z typów schematów, które reprezentują unikalne typy danych. Typy schematów składają się z właściwości, które zawierają nazwę, typ danych i moc zbioru. Po dodaniu typu schematu do schematu bazy danych dokumenty ten typ schematu można utworzyć i dodać do bazy danych.
Dokumenty
W AppSearch jednostka danych jest przedstawiana jako dokument. Każdy dokument w Baza danych AppSearch jest jednoznacznie identyfikowana przez swoją przestrzeń nazw i identyfikator. Przestrzenie nazw służą do oddzielania danych z różnych źródeł, gdy tylko jedno z nich , na przykład konta użytkowników.
Dokumenty zawierają sygnaturę czasową utworzenia, czas życia danych (TTL) i ocenę, która może służyć do określania pozycji w rankingu podczas pobierania. Dokument ma też przypisany schemat opisujący dodatkowe właściwości danych, które musi mieć dokument.
Klasa dokumentu to abstrakcja dokumentu. Zawiera pola z adnotacjami które reprezentują treść dokumentu. Domyślnie jest to nazwa dokumentu. klasa ustawia nazwę typu schematu.
Szukaj
Dokumenty są indeksowane i można je przeszukiwać, podając zapytanie. Dokument to zostanie uwzględniona w wynikach wyszukiwania, jeśli zawiera hasła użyte w zapytaniu lub pasuje do innej specyfikacji wyszukiwania. Wyniki są uporządkowane według wyniku i strategii rankingu. Wyniki wyszukiwania to strony, których można użyć pobieranie sekwencyjne.
AppSearch oferuje dostosowania na potrzeby wyszukiwania, takich jak filtry, konfiguracja rozmiaru strony i krótkie opisy.
Miejsce na dane na platformie a pamięć lokalna
AppSearch oferuje dwa rozwiązania pamięci masowej: LocalStorage i PlatformStorage. Dzięki LocalStorage Twoja aplikacja zarządza indeksem aplikacji, który znajduje się w do katalogu danych aplikacji. Dzięki pamięci Platform Storage Twoja aplikacja tworzy centralny indeks obejmujący cały system. Dostęp do danych w obrębie centralnego indeksu jest ograniczony do danych przekazanych przez aplikację oraz danych, które zostały udostępnione Ci bezpośrednio przez inną aplikację. Pamięć lokalna oraz PlatformStorage korzysta z tego samego interfejsu API i może być wymieniana w zależności od wersja:
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())); }
Dzięki usłudze PlatformStorage Twoja aplikacja może bezpiecznie udostępniać dane innym umożliwiające im również przeszukiwanie danych aplikacji. Tylko do odczytu udostępnianie danych aplikacji jest realizowane przez uzgadnianie połączenia certyfikatu, dzięki czemu inna aplikacja ma uprawnienia do odczytu tych danych. Więcej informacji o tym interfejsie API w dokumentacji funkcji setSchemaTypeVisibilityForPackage().
Dodatkowo zindeksowane dane mogą być wyświetlane na platformach interfejsu systemu. Aplikacje mogą zrezygnować z wyświetlania niektórych lub wszystkich swoich danych w systemie Platformy interfejsu. Więcej informacji o tym interfejsie API znajdziesz w dokumentacji funkcji setSchemaTypeDisplayedBySystem().
Funkcje | 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 |
Wybierając opcję LocalStorage, należy pamiętać o dodatkowych kompromisach. i PlatformStorage. PlatformStorage opakowuje interfejsy Jetpack API w ciągu AppSearch, wpływ na rozmiar plików APK jest minimalny w porównaniu z LocalStorage. Oznacza to jednak również, że operacje AppSearch wiążą się z dodatkowymi opóźnienia powiązania podczas wywoływania usługi systemowej AppSearch. Dzięki pamięci Platform Storage AppSearch ogranicza liczbę dokumentów i rozmiar dokumentów w aplikacji aby zapewnić wydajny centralny indeks.
Pierwsze kroki z AppSearch
Przykład w tej sekcji pokazuje, jak za pomocą interfejsów API AppSearch zintegrować dzięki hipotetycznej aplikacji do zapisywania notatek.
Tworzenie zajęć w dokumencie
Pierwszym krokiem do integracji z AppSearch jest napisanie klasy dokumentu, aby
opisuje dane, które mają zostać wstawione do bazy danych. Oznaczanie zajęć jako zajęć w dokumencie
przy użyciu funkcji @Document
adnotacji.Możesz użyć instancji klasy dokumentu, aby umieścić dokumenty w
pobierania dokumentów z bazy danych.
Ten kod definiuje klasę dokumentu Note ze znakiem
@Document.StringProperty
z adnotacjami
pole indeksowania tekstu obiektu notatki.
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; } }
Otwieranie bazy danych
Zanim zaczniesz pracować z dokumentami, musisz utworzyć bazę danych. Następujący kod:
tworzy nową bazę danych o nazwie notes_app
i pobiera ListenableFuture
dla AppSearchSession
,
który reprezentuje połączenie z bazą danych i udostępnia interfejsy API
operacji na bazach danych.
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() );
Ustaw schemat
Aby móc umieszczać dokumenty i je pobierać, musisz skonfigurować schemat dokumentów z bazy danych. Schemat bazy danych składa się z różnych typów uporządkowanych danych, nazywanych „typami schematów”. Ten kod ustawia schemat, podając klasę dokumentu jako typ schematu.
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);
Umieszczenie dokumentu w bazie danych
Po dodaniu typu schematu możesz dodać do bazy danych dokumenty tego typu.
Ten kod tworzy dokument typu schematu Note
za pomocą interfejsu Note
konstruktora klas dokumentu. Ustawianie przestrzeni nazw dokumentu user1
tak, by reprezentowała
dla dowolnego użytkownika przykładu. Dokument jest następnie wstawiany do bazy danych.
i dołączony jest detektor, który przetworzy wynik operacji umieszczania.
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);
Szukaj
Możesz wyszukiwać dokumenty zindeksowane przy użyciu operacji wyszukiwania omówionych w artykule
w tej sekcji. Poniższy kod wykonuje zapytania dla hasła „owoc” w ciągu
dla dokumentów należących do przestrzeni nazw 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);
Analizuj wyniki wyszukiwania
Wyszukiwania zwracają SearchResults
, która daje dostęp do stron obiektów SearchResult
. Co SearchResult
zawiera pasującą wartość GenericDocument
, ogólną formę
dokument, na który zostały przekonwertowane wszystkie dokumenty. Poniższy kod pobiera pierwszy
stronie wyników wyszukiwania i przekształca je z powrotem w dokument 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);
Usuwanie dokumentu
Gdy użytkownik usunie notatkę, aplikacja usunie odpowiadające im pliki Note
dokument z bazy danych. Dzięki temu notatka nie będzie już wyświetlana w
zapytań. Ten kod wyraźnie żąda usunięcia Note
dokumentu z bazy danych według identyfikatora.
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);
Utrwalaj na dysku
Aktualizacje bazy danych powinny być okresowo zachowywane na dysku przez wywołanie
requestFlush()
następujący kod wywołuje requestFlush()
za pomocą detektora, aby określić, czy wywołanie
udało się.
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);
Zamykanie sesji
AppSearchSession
powinna zostać zamknięta, gdy aplikacja nie będzie już wywoływać żadnej bazy danych
operacji. Ten kod zamyka otwartą sesję AppSearch
i zachowuje wszystkie aktualizacje na dysku.
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);
Dodatkowe materiały
Więcej informacji o AppSearch znajdziesz w tych dodatkowych materiałach:
Próbki
- Android AppSearch Sample (Kotlin), aplikacja do robienia notatek, która używa AppSearch do indeksowania notatek użytkownika i pozwala użytkownikom wyszukiwać ich notatki.
Prześlij opinię
Podziel się z nami swoimi opiniami i pomysłami, korzystając z tych zasobów:
Zgłaszaj błędy, abyśmy mogli je naprawić.