AppSearch

AppSearch, yerel olarak depolanan yapılandırılmış verileri yönetmek için yüksek performanslı bir cihaz üzerinde arama çözümüdür. Verileri dizine ekleme ve tam metin aramayı kullanarak verileri alma API'lerini içerir. Uygulamalar, özel uygulama içi arama özellikleri sunmak için AppSearch'i kullanabilir. Bu sayede kullanıcılar çevrimdışıyken bile içerik arayabilir.

AppSearch'ta dizine ekleme ve arama işlemini gösteren şema

AppSearch aşağıdaki özellikleri sağlar:

  • Düşük G/Ç kullanımıyla hızlı ve mobil öncelikli bir depolama alanı uygulaması
  • Büyük veri kümelerinde yüksek verimlilikte dizine ekleme ve sorgu oluşturma
  • İngilizce ve İspanyolca gibi çok dilli destek
  • Alaka düzeyi sıralaması ve kullanım puanlaması

Daha düşük G/Ç kullanımı nedeniyle AppSearch, büyük veri kümelerinde dizine ekleme ve arama için SQLite'e kıyasla daha düşük gecikme süresi sunar. AppSearch, tek sorguları destekleyerek türler arası sorguları basitleştirirken SQLite, birden fazla tablodaki sonuçları birleştirir.

AppSearch'in özelliklerini açıklamak için kullanıcıların favori şarkılarını yöneten ve kullanıcıların bu şarkıları kolayca aramasına olanak tanıyan bir müzik uygulaması örneğini ele alalım. Kullanıcılar, dünyanın dört bir yanından farklı dillerdeki şarkı başlıklarıyla müzik dinleyebilir. AppSearch, bu şarkıların dizine eklenmesini ve sorgulanması için doğal olarak destek sunar. Kullanıcı, şarkıyı başlığa veya sanatçı adına göre aradığında uygulama, eşleşen şarkıları hızlı ve verimli bir şekilde almak için isteği AppSearch'e iletir. Uygulama, sonuçları gösterir ve kullanıcıların en sevdikleri şarkıları hızlı bir şekilde çalmaya başlamasına olanak tanır.

Kurulum

Uygulamanızda AppSearch'i kullanmak için uygulamanızın build.gradle dosyasına aşağıdaki bağımlılıkları ekleyin:

Groovy

dependencies {
    def appsearch_version = "1.1.0-beta01"

    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"

    // PlayServicesStorage is compatible with all devices that support Google Play Services on
    // all API levels. It offers the same features as PlatformStorage and is the recommended
    // solution for lower API levels on which PlatformStorage is not supported.
    implementation "androidx.appsearch:appsearch-play-services-storage:$appsearch_version"
}

Kotlin

dependencies {
    val appsearch_version = "1.1.0-beta01"

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

    // PlayServicesStorage is compatible with all devices that support Google Play Services on
    // all API levels. It offers the same features as PlatformStorage and is the recommended
    // solution for lower API levels on which PlatformStorage is not supported.
    implementation("androidx.appsearch:appsearch-play-services-storage:$appsearch_version")

}

Uygulama Araması kavramları

Aşağıdaki diyagramda AppSearch kavramları ve bunların etkileşimleri gösterilmektedir.

Bir istemci uygulamasının ve aşağıdaki AppSearch kavramlarıyla etkileşimlerinin şematik özeti: AppSearch veritabanı, şema, şema türleri, dokümanlar, oturum ve arama. Şekil 1. AppSearch kavramlarının şeması: AppSearch veritabanı, şema, şema türleri, dokümanlar, oturum ve arama.

Veritabanı ve oturum

AppSearch veritabanı, veritabanı şemasına uygun bir doküman koleksiyonudur. İstemci uygulamaları, uygulama bağlamlarını ve bir veritabanı adı sağlayarak veritabanı oluşturur. Veritabanları yalnızca onları oluşturan uygulama tarafından açılabilir. Bir veritabanı açıldığında, veritabanıyla etkileşim kurmak için bir oturum döndürülür. Oturum, AppSearch API'lerini çağırmanın giriş noktasıdır ve istemci uygulaması tarafından kapatılana kadar açık kalır.

Şema ve şema türleri

Şema, bir AppSearch veritabanı içindeki verilerin organizasyon yapısını temsil eder.

Şema, benzersiz veri türlerini temsil eden şema türlerinden oluşur. Şema türleri; ad, veri türü ve kardinalite içeren özelliklerden oluşur. Bir şema türü veritabanı şemasına eklendikten sonra, bu şema türünde dokümanlar oluşturulup veritabanına eklenebilir.

Dokümanlar

AppSearch'te veri birimleri doküman olarak temsil edilir. AppSearch veritabanındaki her doküman, ad alanı ve kimliğiyle benzersiz şekilde tanımlanır. Alan adları, yalnızca bir kaynağın (ör. kullanıcı hesapları) sorgulanması gerektiğinde farklı kaynaklardan gelen verileri ayırmak için kullanılır.

Dokümanlar, oluşturma zaman damgası, geçerlilik süresi (TTL) ve getirme sırasında sıralama için kullanılabilecek bir puan içerir. Belgelere, sahip olması gereken ek veri özelliklerini açıklayan bir şema türü de atanır.

Belge sınıfı, bir belgenin soyut bir temsilidir. Bir dokümanın içeriğini temsil eden ek açıklamalı alanlar içerir. Varsayılan olarak, şema türü adı, belge sınıfının adıyla belirlenir.

Dizine eklenen dokümanlar, sorgu sağlanarak aranabilir. Sorgudaki terimleri içeren veya başka bir arama özelliğiyle eşleşen dokümanlar eşleştirilir ve arama sonuçlarına dahil edilir. Sonuçlar, puanlarına ve sıralama stratejilerine göre sıralanır. Arama sonuçları, sırayla alabileceğiniz sayfalarla temsil edilir.

AppSearch, arama için filtreler, sayfa boyutu yapılandırması ve snippet oluşturma gibi özelleştirmeler sunar.

Platform Depolama, Yerel Depolama veya Play Hizmetleri Depolama

AppSearch üç depolama çözümü sunar: LocalStorage, PlatformStorage ve PlayServicesStorage. LocalStorage ile uygulamanız, uygulama veri dizininde bulunan uygulamaya özel bir dizini yönetir. Hem PlatformStorage hem de PlayServicesStorage ile uygulamanız, sistem genelindeki merkezi bir dizin oluşturmaya katkıda bulunur. PlatformStorage dizini sistem sunucusunda, PlayServicesStorage dizini ise Google Play Hizmetleri'nin depolama alanında barındırılır. Bu merkezi dizinlerdeki veri erişimi, uygulamanızın katkıda bulunduğu veriler ve başka bir uygulama tarafından sizinle açıkça paylaşılan verilerle sınırlıdır. Bu depolama alanı seçeneklerinin tümü aynı API'yi kullanır ve cihazın sürümüne göre değiştirilebilir:

Kotlin

if (BuildCompat.isAtLeastS()) {
    appSearchSessionFuture.setFuture(
        PlatformStorage.createSearchSession(
            PlatformStorage.SearchContext.Builder(mContext, DATABASE_NAME)
               .build()
        )
    )
} else {
    if (usePlayServicesStorageBelowS) {
        appSearchSessionFuture.setFuture(
            PlayServicesStorage.createSearchSession(
                PlayServicesStorage.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 {
    if (usePlayServicesStorageBelowS) {
        mAppSearchSessionFuture.setFuture(PlayServicesStorage.createSearchSession(
                new PlayServicesStorage.SearchContext.Builder(mContext, DATABASE_NAME)
                        .build()));
    } else {
        mAppSearchSessionFuture.setFuture(LocalStorage.createSearchSession(
                new LocalStorage.SearchContext.Builder(mContext, DATABASE_NAME)
                        .build()));
    }
}

PlatformStorage ve PlayServicesStorage'ü kullanarak uygulamanız, diğer uygulamaların da uygulamanızın verilerinde arama yapmasına izin vermek için verileri güvenli bir şekilde paylaşabilir. Salt okunur uygulama verisi paylaşımı, diğer uygulamanın verileri okuma izni olduğundan emin olmak için sertifika el sıkışması kullanılarak verilir. Bu API hakkında daha fazla bilgiyi setSchemaTypeVisibilityForPackage() dokümanlarında bulabilirsiniz.

Ayrıca PlatformStorage ile dizine eklenen veriler sistem kullanıcı arayüzü platformlarında görüntülenebilir. Uygulamalar, verilerinin bir kısmının veya tamamının sistem kullanıcı arayüzü yüzeylerinde gösterilmesini devre dışı bırakabilir. Bu API hakkında daha fazla bilgiyi setSchemaTypeDisplayedBySystem() dokümanlarında bulabilirsiniz.

Özellikler LocalStorage (Android 5.0 ve sonraki sürümlerle uyumlu) PlatformStorage (Android 12 ve sonraki sürümlerle uyumlu) PlayServicesStorage (Android 5.0 ve sonraki sürümlerle uyumlu)
Etkili tam metin arama
Çok dilli destek
Azaltılmış ikili dosya boyutu
Uygulamalar arası veri paylaşımı
Sistem kullanıcı arayüzü yüzeylerinde veri görüntüleme yeteneği
Sınırsız sayıda ve boyuttaki doküman dizine eklenebilir
Ek bağlayıcı gecikmesi olmadan daha hızlı işlemler

LocalStorage ile PlatformStorage arasında seçim yaparken dikkate almanız gereken başka değişkenler de vardır. PlatformStorage, Jetpack API'lerini AppSearch sistem hizmetinin üzerine sarmaladığından APK boyutu üzerindeki etkisi, LocalStorage'ı kullanmaya kıyasla çok azdır. Ancak bu, AppSearch sistem hizmetini çağırırken AppSearch işlemlerinin ek bağlayıcı gecikmesi yaşaması anlamına da gelir. AppSearch, PlatformStorage ile verimli bir merkezi dizin sağlamak için bir uygulamanın dizine ekleyebildiği doküman sayısını ve doküman boyutunu sınırlandırır. PlayServicesStorage, PlatformStorage ile aynı sınırlamalara sahiptir ve yalnızca Google Play Hizmetleri'nin yüklü olduğu cihazlarda desteklenir.

AppSearch'i kullanmaya başlama

Bu bölümdeki örnekte, varsayımsal bir not tutma uygulamasıyla entegrasyon için AppSearch API'lerinin nasıl kullanılacağı gösterilmektedir.

Doküman sınıfı yazma

AppSearch ile entegrasyona ilk adım, veritabanına eklenecek verileri tanımlayacak bir doküman sınıfı yazmaktır. @Document ek açıklamasını kullanarak bir sınıfı doküman sınıfı olarak işaretleyin.Doküman sınıfının örneklerini kullanarak veritabanına doküman ekleyebilir ve veritabanından doküman alabilirsiniz.

Aşağıdaki kod, bir Not nesnesinin metnini dizine eklemek için @Document.StringProperty ek açıklamalı bir alan içeren bir Not belgesi sınıfı tanımlar.

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

Veritabanı açma

Dokümanlarla çalışmaya başlamadan önce bir veritabanı oluşturmanız gerekir. Aşağıdaki kod, notes_app adlı yeni bir veritabanı oluşturur ve veritabanı bağlantısını temsil eden ve veritabanı işlemleri için API'leri sağlayan bir AppSearchSession için ListenableFuture alır.

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

Şema ayarlama

Veri tabanına belge eklemeden ve veritabanından belge almadan önce bir şema ayarlamanız gerekir. Veritabanı şeması, "şema türleri" olarak adlandırılan farklı türde yapılandırılmış verilerden oluşur. Aşağıdaki kod, belge sınıfını şema türü olarak sağlayarak şemayı ayarlar.

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

Veritabanına doküman ekleme

Eklenen bir şema türüne ait dokümanları veritabanına ekleyebilirsiniz. Aşağıdaki kod, Note doküman sınıfı oluşturucusunu kullanarak Note şeması türüne sahip bir doküman oluşturur. Bu örnekteki rastgele bir kullanıcıyı temsil edecek şekilde doküman ad alanını user1 ayarlar. Ardından, belge veritabanına eklenir ve ekleme işleminin sonucunu işlemek için bir dinleyici eklenir.

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

Bu bölümde açıklanan arama işlemlerini kullanarak dizine eklenen dokümanları arayabilirsiniz. Aşağıdaki kod, user1 ad alanına ait belgeler için veritabanında "fruit" terimiyle ilgili sorgular gerçekleştirir.

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

SearchResults öğesini iterasyonla gezme

Aramalar, SearchResult nesnelerinin sayfalarına erişim sağlayan bir SearchResults örneği döndürür. Her SearchResult, eşleşen GenericDocument'yi (tüm dokümanların dönüştürüldüğü belgenin genel biçimi) içerir. Aşağıdaki kod, arama sonuçlarının ilk sayfasını alır ve sonucu tekrar Note belgesine dönüştürür.

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

Doküman kaldırma

Kullanıcı bir notu sildiğinde uygulama, ilgili Note belgeyi veritabanından siler. Bu işlem, notun artık sorgularda gösterilmemesini sağlar. Aşağıdaki kod, Note belgesinin kimliğe göre veritabanından kaldırılması için açık bir istek gönderir.

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

Diske kalıcı olarak kaydetme

Bir veritabanındaki güncellemeler, requestFlush() çağrılarak düzenli olarak diske kaydedilmelidir. Aşağıdaki kod, aramanın başarılı olup olmadığını belirlemek için requestFlush()'ü bir dinleyiciyle çağırır.

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

Oturumları kapatma

Bir uygulama artık veritabanı işlemi çağırmayacaksa AppSearchSession kapatılmalıdır. Aşağıdaki kod, daha önce açılan AppSearch oturumunu kapatır ve tüm güncellemeleri diske kaydeder.

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

Ek kaynaklar

AppSearch hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara göz atın:

Örnekler

  • Android AppSearch Örneği (Kotlin): Kullanıcı notlarını dizine eklemek için AppSearch'i kullanan ve kullanıcıların notlarında arama yapmasına olanak tanıyan bir not alma uygulaması.

Geri bildirim gönder

Geri bildirimlerinizi ve fikirlerinizi aşağıdaki kaynaklar üzerinden bizimle paylaşın:

Sorun takipçisi

Düzeltmemiz için hataları bildirin.