AppSearch

AppSearch یک راه حل جستجوی روی دستگاه با کارایی بالا برای مدیریت داده های ساختار یافته و ذخیره شده محلی است. این شامل APIهایی برای نمایه سازی داده ها و بازیابی داده ها با استفاده از جستجوی متن کامل است. برنامه‌ها می‌توانند از AppSearch برای ارائه قابلیت‌های جستجوی درون‌برنامه سفارشی استفاده کنند، که به کاربران امکان می‌دهد حتی در حالت آفلاین، محتوا را جستجو کنند.

نمودار نمایه سازی و جستجو در AppSearch را نشان می دهد

AppSearch ویژگی های زیر را ارائه می دهد:

  • یک پیاده سازی ذخیره سازی سریع و اول موبایل با استفاده کم ورودی/خروجی
  • نمایه سازی و پرس و جو بسیار کارآمد در مجموعه داده های بزرگ
  • پشتیبانی از چند زبان، مانند انگلیسی و اسپانیایی
  • رتبه بندی مرتبط و امتیاز استفاده

به دلیل استفاده کمتر از ورودی/خروجی، AppSearch در مقایسه با SQLite تأخیر کمتری را برای نمایه سازی و جستجو در مجموعه داده های بزرگ ارائه می دهد. AppSearch با پشتیبانی از پرس‌و‌جوهای منفرد، پرس‌و‌جوهای متقاطع را ساده می‌کند، در حالی که SQLite نتایج چندین جدول را ادغام می‌کند.

برای نشان دادن ویژگی های AppSearch، اجازه دهید یک برنامه موسیقی را مثال بزنیم که آهنگ های مورد علاقه کاربران را مدیریت می کند و به کاربران امکان می دهد به راحتی آنها را جستجو کنند. کاربران از موسیقی سراسر جهان با عناوین آهنگ به زبان‌های مختلف لذت می‌برند، که AppSearch به‌طور بومی از فهرست‌بندی و جستجو برای آن پشتیبانی می‌کند. هنگامی که کاربر آهنگی را بر اساس عنوان یا نام هنرمند جستجو می کند، برنامه به سادگی درخواست را به AppSearch ارسال می کند تا آهنگ های منطبق را به سرعت و کارآمد بازیابی کند. این برنامه نتایج را نشان می دهد و به کاربران خود اجازه می دهد تا به سرعت شروع به پخش آهنگ های مورد علاقه خود کنند.

راه اندازی

برای استفاده از AppSearch در برنامه خود، وابستگی های زیر را به فایل build.gradle برنامه خود اضافه کنید:

شیار

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

کاتلین

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

نمودار زیر مفاهیم AppSearch و تعاملات آنها را نشان می دهد.

طرح کلی یک برنامه مشتری و تعاملات آن با مفاهیم AppSearch زیر: پایگاه داده AppSearch، طرحواره، انواع طرحواره، اسناد، جلسه و جستجو. شکل 1. نمودار مفاهیم AppSearch: پایگاه داده AppSearch، طرحواره، انواع طرحواره، اسناد، جلسه و جستجو.

پایگاه داده و جلسه

پایگاه داده AppSearch مجموعه ای از اسناد است که با طرح پایگاه داده مطابقت دارد. برنامه های کاربردی سرویس گیرنده با ارائه زمینه برنامه خود و نام پایگاه داده یک پایگاه داده ایجاد می کنند. پایگاه داده ها را تنها می توان توسط برنامه ای که آنها را ایجاد کرده است باز کرد. هنگامی که یک پایگاه داده باز می شود، یک جلسه برای تعامل با پایگاه داده باز می گردد. جلسه نقطه ورود برای فراخوانی API های AppSearch است و تا زمانی که توسط برنامه مشتری بسته نشود باز می ماند.

طرحواره و انواع طرحواره

طرحواره ساختار سازمانی داده ها را در پایگاه داده AppSearch نشان می دهد.

طرحواره از انواع طرحواره تشکیل شده است که انواع منحصر به فرد داده را نشان می دهد. انواع طرحواره شامل ویژگی هایی است که شامل نام، نوع داده و کاردینالیته است. هنگامی که یک نوع طرحواره به طرح پایگاه داده اضافه شد، اسنادی از آن نوع طرحواره را می توان ایجاد کرد و به پایگاه داده اضافه کرد.

اسناد

در AppSearch، یک واحد داده به عنوان یک سند نشان داده می شود. هر سند در پایگاه داده AppSearch به طور منحصر به فرد با فضای نام و شناسه خود شناسایی می شود. فضاهای نام برای جدا کردن داده ها از منابع مختلف زمانی استفاده می شود که فقط یک منبع نیاز به پرس و جو دارد، مانند حساب های کاربری.

اسناد حاوی یک مهر زمانی ایجاد، یک زمان برای زندگی (TTL) و یک امتیاز است که می تواند برای رتبه بندی در طول بازیابی استفاده شود. همچنین به یک سند یک نوع طرح واره اختصاص داده می‌شود که ویژگی‌های داده اضافی را که سند باید داشته باشد، توصیف می‌کند.

کلاس سند انتزاعی از یک سند است. این شامل فیلدهای حاشیه نویسی است که محتوای یک سند را نشان می دهد. به طور پیش فرض، نام کلاس سند، نام نوع طرحواره را تعیین می کند.

اسناد نمایه می شوند و با ارائه پرس و جو می توان آنها را جستجو کرد. سندی مطابقت داده می‌شود و در نتایج جستجو گنجانده می‌شود که حاوی عبارات موجود در جستار باشد یا با مشخصات جستجوی دیگری مطابقت داشته باشد. نتایج بر اساس امتیاز و استراتژی رتبه بندی آنها مرتب می شوند. نتایج جستجو با صفحاتی نمایش داده می شوند که می توانید به صورت متوالی بازیابی کنید.

AppSearch سفارشی‌سازی‌هایی را برای جستجو ارائه می‌کند، مانند فیلترها، پیکربندی اندازه صفحه، و قطعه‌بندی.

فضای ذخیره‌سازی پلتفرم در مقابل فضای ذخیره‌سازی محلی

AppSearch دو راه حل ذخیره سازی ارائه می دهد: LocalStorage و PlatformStorage. با LocalStorage، برنامه شما یک فهرست ویژه برنامه را مدیریت می کند که در فهرست اطلاعات برنامه شما زندگی می کند. با PlatformStorage، برنامه شما به یک نمایه مرکزی در سراسر سیستم کمک می کند. دسترسی به داده ها در فهرست مرکزی محدود به داده هایی است که برنامه شما ارائه کرده است و داده هایی که به صراحت توسط برنامه دیگری با شما به اشتراک گذاشته شده است. هر دو LocalStorage و PlatformStorage API یکسانی دارند و بر اساس نسخه دستگاه قابل تعویض هستند:

کاتلین

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

جاوا

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

با استفاده از PlatformStorage، برنامه شما می‌تواند به‌طور ایمن داده‌ها را با سایر برنامه‌ها به اشتراک بگذارد تا به آن‌ها اجازه دهد روی داده‌های برنامه شما نیز جستجو کنند. به اشتراک گذاری داده های برنامه فقط خواندنی از طریق یک دست دادن گواهی اعطا می شود تا اطمینان حاصل شود که برنامه دیگر مجوز خواندن داده ها را دارد. درباره این API در مستندات setSchemaTypeVisibilityForPackage() بیشتر بخوانید.

علاوه بر این، داده‌هایی که ایندکس می‌شوند را می‌توان بر روی سطوح رابط کاربری سیستم نمایش داد. برنامه‌ها می‌توانند از نمایش برخی یا همه داده‌هایشان در سطوح رابط کاربری سیستم انصراف دهند. درباره این API در مستندات setSchemaTypeDisplayedBySystem() بیشتر بخوانید.

ویژگی ها 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 و PlatformStorage، باید مبادلات دیگری را در نظر بگیرید. از آنجایی که PlatformStorage API های Jetpack را روی سرویس سیستم AppSearch قرار می دهد، تأثیر اندازه APK در مقایسه با استفاده از LocalStorage بسیار کم است. با این حال، این همچنین به این معنی است که عملیات AppSearch در هنگام فراخوانی سرویس سیستم AppSearch دارای تأخیر بایندر اضافی است. با PlatformStorage، AppSearch تعداد اسناد و اندازه اسنادی را که یک برنامه می‌تواند فهرست‌بندی کند، محدود می‌کند تا از یک فهرست مرکزی کارآمد اطمینان حاصل شود.

با AppSearch شروع کنید

مثال در این بخش نحوه استفاده از API های AppSearch برای ادغام با یک برنامه فرضی یادداشت برداری را نشان می دهد.

یک کلاس سند بنویسید

اولین گام برای ادغام با AppSearch، نوشتن یک کلاس سند برای توصیف داده‌ها برای درج در پایگاه داده است. با استفاده از @Document annotation یک کلاس را به عنوان کلاس سند علامت گذاری کنید. می توانید از نمونه هایی از کلاس document برای قرار دادن اسناد و بازیابی اسناد از پایگاه داده استفاده کنید.

کد زیر یک کلاس سند Note را با یک فیلد حاشیه‌نویسی @Document.StringProperty برای نمایه‌سازی متن یک شی Note تعریف می‌کند.

کاتلین

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

جاوا

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

یک پایگاه داده باز کنید

قبل از کار با اسناد باید یک پایگاه داده ایجاد کنید. کد زیر یک پایگاه داده جدید با نام notes_app ایجاد می کند و یک ListenableFuture برای AppSearchSession دریافت می کند که نشان دهنده اتصال به پایگاه داده است و API ها را برای عملیات پایگاه داده ارائه می دهد.

کاتلین

val context: Context = getApplicationContext()
val sessionFuture = LocalStorage.createSearchSession(
    LocalStorage.SearchContext.Builder(context, /*databaseName=*/"notes_app")
    .build()
)

جاوا

Context context = getApplicationContext();
ListenableFuture<AppSearchSession> sessionFuture = LocalStorage.createSearchSession(
       new LocalStorage.SearchContext.Builder(context, /*databaseName=*/ "notes_app")
               .build()
);

یک طرح واره تنظیم کنید

قبل از قرار دادن اسناد و بازیابی اسناد از پایگاه داده، باید یک طرحواره تنظیم کنید. طرحواره پایگاه داده شامل انواع مختلفی از داده های ساختاریافته است که به آنها "انواع طرحواره" گفته می شود. کد زیر با ارائه کلاس سند به عنوان یک نوع طرحواره، طرحواره را تنظیم می کند.

کاتلین

val setSchemaRequest = SetSchemaRequest.Builder().addDocumentClasses(Note::class.java)
    .build()
val setSchemaFuture = Futures.transformAsync(
    sessionFuture,
    { session ->
        session?.setSchema(setSchemaRequest)
    }, mExecutor
)

جاوا

SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder().addDocumentClasses(Note.class)
       .build();
ListenableFuture<SetSchemaResponse> setSchemaFuture =
       Futures.transformAsync(sessionFuture, session -> session.setSchema(setSchemaRequest), mExecutor);

یک سند را در پایگاه داده قرار دهید

هنگامی که یک نوع طرحواره اضافه شد، می توانید اسنادی از آن نوع را به پایگاه داده اضافه کنید. کد زیر با استفاده از سازنده کلاس Note document یک سند از نوع طرحواره Note می سازد. فضای نام سند user1 را برای نمایش یک کاربر دلخواه از این نمونه تنظیم می کند. سپس سند در پایگاه داده وارد می شود و یک شنونده برای پردازش نتیجه عملیات put ضمیمه می شود.

کاتلین

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
)

جاوا

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

شما می توانید اسنادی را که نمایه شده اند با استفاده از عملیات جستجوی پوشش داده شده در این بخش جستجو کنید. کد زیر پرس و جوهایی را برای عبارت "fruit" در پایگاه داده برای اسنادی که به فضای نام user1 تعلق دارند انجام می دهد.

کاتلین

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
)

جاوا

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

از طریق Search Results تکرار کنید

جستجوها یک نمونه SearchResults را برمی‌گردانند که به صفحات اشیاء SearchResult دسترسی می‌دهد. هر SearchResult دارای GenericDocument منطبق است، شکل کلی سندی که همه اسناد به آن تبدیل می‌شوند. کد زیر صفحه اول نتایج جستجو را دریافت می کند و نتیجه را به یک سند Note تبدیل می کند.

کاتلین

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
)

جاوا

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

یک سند را حذف کنید

هنگامی که کاربر یک یادداشت را حذف می کند، برنامه سند Note مربوطه را از پایگاه داده حذف می کند. این تضمین می کند که یادداشت دیگر در پرس و جو ظاهر نمی شود. کد زیر یک درخواست صریح برای حذف سند Note از پایگاه داده توسط Id می دهد.

کاتلین

val removeRequest = RemoveByDocumentIdRequest.Builder("user1")
    .addIds("noteId")
    .build()

val removeFuture = Futures.transformAsync(
    sessionFuture, { session ->
        session?.remove(removeRequest)
    },
    mExecutor
)

جاوا

RemoveByDocumentIdRequest removeRequest = new RemoveByDocumentIdRequest.Builder("user1")
       .addIds("noteId")
       .build();

ListenableFuture<AppSearchBatchResult<String, Void>> removeFuture =
       Futures.transformAsync(sessionFuture, session -> session.remove(removeRequest), mExecutor);

ماندگاری روی دیسک

به روز رسانی های یک پایگاه داده باید به صورت دوره ای با فراخوانی requestFlush() روی دیسک ادامه یابد. کد زیر requestFlush() را با یک شنونده فراخوانی می کند تا مشخص کند که آیا تماس موفق بوده است یا خیر.

کاتلین

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)

جاوا

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

یک جلسه را ببندید

یک AppSearchSession باید زمانی بسته شود که یک برنامه دیگر هیچ عملیات پایگاه داده را فراخوانی نکند. کد زیر جلسه AppSearch را که قبلاً باز شده بود می بندد و همه به روز رسانی های دیسک را ادامه می دهد.

کاتلین

val closeFuture = Futures.transform<AppSearchSession, Unit>(sessionFuture,
    { session ->
        session?.close()
        Unit
    }, mExecutor
)

جاوا

ListenableFuture<Void> closeFuture = Futures.transform(sessionFuture, session -> {
   session.close();
   return null;
}, mExecutor);

منابع اضافی

برای کسب اطلاعات بیشتر در مورد AppSearch، به منابع اضافی زیر مراجعه کنید:

نمونه ها

  • Android AppSearch Sample (Kotlin) ، یک برنامه یادداشت برداری که از AppSearch برای فهرست کردن یادداشت های کاربر استفاده می کند و به کاربران اجازه می دهد در یادداشت های خود جستجو کنند.

بازخورد ارائه دهید

نظرات و ایده های خود را از طریق این منابع با ما در میان بگذارید:

ردیاب مشکل

اشکالات را گزارش کنید تا بتوانیم آنها را برطرف کنیم.