AppSearch, yerel olarak depolanan, yapılandırılmış verileri yönetmek için kullanılan yüksek performanslı bir cihaz üzerinde arama çözümüdür. Verileri dizine eklemeye ve tam metin araması kullanarak veri almaya yönelik API'ler içerir. Uygulamalar, kullanıcıların çevrimdışıyken bile içerik aramasına olanak tanıyarak özel uygulama içi arama özellikleri sunmak için AppSearch'ü kullanabilir.
![AppSearch'te dizine ekleme ve arama yapmayı gösteren şema](https://developer.android.com/static/images/guide/topics/search/appsearch.png?authuser=6&hl=tr)
AppSearch aşağıdaki özellikleri sağlar:
- Düşük G/Ç kullanımı ile hızlı, mobil öncelikli depolama uygulaması
- Büyük veri kümelerinde son derece verimli dizine ekleme ve sorgulama
- İngilizce ve İspanyolca gibi çoklu dil desteği
- Alaka düzeyi sıralaması ve kullanım puanı
Daha düşük G/Ç kullanımı nedeniyle AppSearch, dizine ekleme ve büyük veri kümelerinde arama için SQLite'a kıyasla daha düşük gecikme sunar. AppSearch, tek sorguları destekleyerek türler arası sorguları basitleştirirken SQLite, birden fazla tablodaki sonuçları birleştirir.
AppSearch'ün özelliklerini göstermek için kullanıcıların favori şarkılarını yöneten ve bu şarkıları kolayca arayabilmelerini sağlayan bir müzik uygulaması örneğini ele alalım. Kullanıcılar farklı dillerdeki şarkı başlıklarıyla dünyanın dört bir yanından müziklerin keyfini çıkarır. AppSearch yerel olarak dizine eklemeyi ve sorgulamayı destekler. Kullanıcı bir şarkıyı başlığa veya sanatçı adına göre aradığında uygulama, eşleşen şarkıları hızlı ve etkili bir şekilde almak için isteği AppSearch'e iletir. Uygulama sonuçları göstererek kullanıcılarının en sevdikleri şarkıları hızlıca çalmaya başlamalarını sağlar.
Kurulum
AppSearch'ü uygulamanızda kullanmak için uygulamanızın build.gradle
dosyasına aşağıdaki bağımlılıkları ekleyin:
Modern
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") }
AppSearch kavramları
Aşağıdaki şemada AppSearch kavramları ve etkileşimleri gösterilmektedir.
Şekil 1. AppSearch kavramlarının şeması: AppSearch veritabanı, şema, şema türleri, belgeler, oturum ve arama.
Veritabanı ve oturum
AppSearch veritabanı, veritabanı şemasına uygun olan belge koleksiyonudur. İstemci uygulamaları, uygulama bağlamını ve veritabanı adını sağlayarak veritabanı oluşturur. Veritabanları yalnızca onları oluşturan uygulama tarafından açılabilir. Bir veritabanı açıldığında, veritabanıyla etkileşimde bulunmak için bir oturum döndürülür. Oturum, AppSearch API'lerini çağırmak için kullanılan 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ındaki verilerin kuruluş 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 kardinaliteyi içeren mülklerden oluşur. Veritabanı şemasına bir şema türü eklendikten sonra, bu şema türünde belgeler oluşturulabilir ve veritabanına eklenebilir.
Dokümanlar
AppSearch'te, bir veri birimi doküman olarak temsil edilir. AppSearch veritabanındaki her belge, ad alanı ve kimliği ile benzersiz şekilde tanımlanır. Ad alanları, kullanıcı hesapları gibi yalnızca tek bir kaynağın sorgulanması gerektiğinde farklı kaynaklardan verileri ayırmak için kullanılır.
Belgelerde oluşturma zaman damgası, geçerlilik süresi (TTL) ve alma sırasında sıralamada kullanılabilecek bir puan bulunur. Ayrıca belgeye, dokümanın sahip olması gereken ek veri özelliklerini açıklayan bir şema türü de atanır.
Doküman sınıfı, dokümanın soyutlanmasıdır. Bir belgenin içeriğini temsil eden ek açıklamalı alanlar içerir. Varsayılan olarak, şema türünün adı belge sınıfının adı tarafından belirlenir.
Arama
Dokümanlar dizine eklenir ve sorgu sağlanarak aranabilir. Bir doküman, sorgudaki terimleri içeriyorsa veya başka bir arama spesifikasyonuyla eşleşirse 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ıralı olarak alabileceğiniz sayfalarla gösterilir.
AppSearch, arama için filtreler, sayfa boyutu yapılandırması ve snippet oluşturma gibi özelleştirmeler sunar.
Platform Depolama Alanı - Yerel Depolama Alanı
AppSearch iki depolama çözümü sunar: LocalStorage ve PlatformStorage. LocalStorage sayesinde uygulamanız, uygulama veri dizininizde bulunan uygulamaya özel bir dizini yönetir. PlatformStorage sayesinde uygulamanız sistem genelinde bir merkezi dizine katkıda bulunur. Merkezi dizindeki veri erişimi, uygulamanızın katkıda bulunduğu ve başka bir uygulamanın sizinle açıkça paylaştığı verilerle sınırlıdır. Hem LocalStorage hem de PlatformStorage aynı API'yi paylaşır ve cihazın sürümüne bağlı olarak birbirinin yerine kullanılabilir:
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())); }
Uygulamanız, PlatformStorage kullanarak verileri diğer uygulamalarla güvenli bir şekilde paylaşarak uygulamaların verilerinde de arama yapmalarına olanak tanıyabilir. Salt okunur uygulama veri paylaşımı, diğer uygulamanın verileri okuma iznine sahip olduğundan emin olmak için sertifika el sıkışması ile verilir. setSchemaTypeVisibilityForPackage() belgelerinde bu API hakkında daha fazla bilgi edinebilirsiniz.
Ayrıca, dizine eklenen veriler sistem arayüzü yüzeylerinde görüntülenebilir. Uygulamalar, verilerinin bir kısmının veya tamamının Sistem kullanıcı arayüzü yüzeylerinde görüntülenmesini devre dışı bırakabilir. setSchemaTypeDisplayedBySystem() belgelerinde bu API hakkında daha fazla bilgi edinebilirsiniz.
Özellikler | 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 |
LocalStorage ve PlatformStorage arasında seçim yaparken göz önünde bulundurulması gereken ek dengelemeler vardır. PlatformStorage, Jetpack API'lerini AppSearch sistem hizmeti üzerinden sarmaladığından LocalStorage kullanımına kıyasla APK boyutu etkisi minimum düzeydedir. Ancak bu aynı zamanda AppSearch işlemlerinin, AppSearch sistem hizmeti çağrılırken ek bağlayıcı gecikmesine neden olacağı anlamına da gelir. PlatformStorage sayesinde AppSearch, verimli bir merkezi dizin sağlamak için bir uygulamanın dizine ekleyebileceği doküman sayısını ve doküman boyutunu sınırlandırır.
AppSearch'ü kullanmaya başlayın
Bu bölümdeki örnekte, varsayıma dayalı not tutma uygulaması ile entegrasyon sağlamak için AppSearch API'lerinin nasıl kullanılacağı gösterilmektedir.
Belge sınıfı yazın
AppSearch ile entegrasyon için ilk adım, veritabanına eklenecek verileri tanımlamak üzere bir belge sınıfı yazmaktır. @Document
notasyonunu kullanarak bir sınıfı belge sınıfı olarak işaretleyin.Veritabanına belge eklemek ve almak için belge sınıfının örneklerini kullanabilirsiniz.
Aşağıdaki kod, bir Note nesnesinin metnini dizine eklemek için @Document.StringProperty
ek açıklamalı alanı içeren bir Note dokümanı 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; } }
Bir veritabanı açın
Dokümanlarla çalışmaya başlamadan önce bir veritabanı oluşturmanız gerekir. Aşağıdaki kod notes_app
adında yeni bir veritabanı oluşturur ve AppSearchSession
için bir ListenableFuture
alır. Bu da veritabanı ile olan bağlantıyı temsil eder ve veritabanı işlemleri için API'ler sağlar.
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
Dokümanları veritabanına yerleştirip almak için öncelikle bir şema ayarlamanız gerekir. Veritabanı şeması, "şema türleri" olarak adlandırılan farklı yapılandırılmış veri türlerinden oluşur. Aşağıdaki kod, belge sınıfını bir ş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 bir belge ekleyin
Bir şema türü eklendikten sonra, bu türdeki dokümanları veritabanına ekleyebilirsiniz.
Aşağıdaki kod, Note
doküman sınıfı oluşturucuyu kullanarak Note
şema türünde bir belge oluşturur. user1
belge ad alanını, bu örneğin rastgele bir kullanıcısını temsil edecek şekilde ayarlar. Belge, daha sonra veritabanına eklenir ve koyma işleminin sonucunu işlemek için bir işleyici iliştirilir.
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);
Arama
Bu bölümde ele alınan arama işlemlerini kullanarak dizine eklenen dokümanları arayabilirsiniz. Aşağıdaki kod, user1
ad alanına ait belgeler için veritabanı üzerinde "meyve" terimi için 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);
Arama Sonuçları ile yineleyin
Aramalar, SearchResult
nesnelerinin sayfalarına erişim sağlayan bir SearchResults
örneği döndürür. Her SearchResult
, eşleşen GenericDocument
öğesini (tüm dokümanların dönüştürüldüğü genel biçim) barındırır. Aşağıdaki kod, arama sonuçlarının ilk sayfasını alır ve sonucu tekrar bir Note
dokümanına 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
dokümanını veritabanından siler. Bu, notun artık sorgularda gösterilmemesini sağlar. Aşağıdaki kod, Note
belgesini kimliğe göre veritabanından kaldırmak için açık bir istekte bulunur.
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 devam et
Bir veritabanında yapılan güncellemeler, requestFlush()
çağrısı yapılarak düzenli aralıklarla diskte sürdürülmelidir. Aşağıdaki kod, çağrının başarılı olup olmadığını belirlemek için bir işleyici ile requestFlush()
ç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);
Oturum kapatma
Bir uygulama artık veritabanı işlemi çağırmayacaksa AppSearchSession
kapatılmalıdır. Aşağıdaki kod, daha önce açılmış olan AppSearch oturumunu kapatır ve diskte yapılan tüm güncellemeleri devam ettirir.
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 bakın:
Sana Özel
- Android AppSearch Örneği (Kotlin), bir kullanıcının notlarını dizine eklemek için AppSearch'ü kullanan ve kullanıcıların notlarında arama yapmasına olanak tanıyan bir not alma uygulamasıdır.
Geri bildirim gönderme
Aşağıdaki kaynakları kullanarak geri bildirimlerinizi ve fikirlerinizi bizimle paylaşabilirsiniz:
Düzeltebilmemiz için hataları bildirin.