Wyszukiwanie aplikacji

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

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:

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")
}

Pojęcia związane z AppSearch

Poniższy diagram przedstawia pojęcia AppSearch i ich interakcje.

Diagram
zarys aplikacji klienckiej i jej interakcji z:
Pojęcia związane z AppSearch: baza danych AppSearch, schemat, typy schematów, dokumenty,
sesji i wyszukiwania. 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.

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);

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:

Śledzenie problemów

Zgłaszaj błędy, abyśmy mogli je naprawić.