بحث التطبيقات

AppSearch هو حلّ عالي الأداء للبحث على الجهاز فقط لإدارة عمليات البحث على الجهاز والمخزنة والمهيكلة. يحتوي على واجهات برمجة تطبيقات لفهرسة البيانات واسترداد البيانات باستخدام البحث في النص الكامل. يمكن للتطبيقات استخدام AppSearch لتقديم تجربة مخصَّصة داخل التطبيق إمكانات البحث، ما يسمح للمستخدمين بالبحث عن المحتوى حتى في وضع عدم الاتصال.

مخطّط بياني يوضّح الفهرسة والبحث ضمن AppSearch

ويوفّر AppSearch الميزات التالية:

  • تنفيذ سريع للتخزين أولاً على الأجهزة الجوّالة مع انخفاض في استخدام وحدات الإدخال والإخراج
  • فهرسة وإجراء طلبات بحث بكفاءة عالية على مجموعات كبيرة من البيانات
  • التوافق مع لغات متعددة، مثل الإنجليزية والإسبانية
  • تصنيف مدى الصلة بموضوع البحث ونتيجة الاستخدام

بسبب انخفاض استخدام وحدات الإدخال والإخراج، يوفّر AppSearch وقت استجابة أقل للفهرسة والبحث على مجموعات البيانات الكبيرة مقارنة بـ SQLite. خدمة AppSearch تبسّط طلبات البحث المتعددة الأنواع من خلال دعم الاستعلامات المفردة بينما تدمج SQLite النتائج من جداول متعددة.

لتوضيح ميزات AppSearch، لنلقِ نظرة على محتوى موسيقي تطبيق يدير الأغاني المفضلة للمستخدمين ويتيح للمستخدمين البحث بسهولة لهم. يستمتع المستخدمون بموسيقى من جميع أنحاء العالم مع عناوين أغاني مختلفة التي تتيح خدمة AppSearch في الأصل الفهرسة وطلب البحث. عندما يبحث المستخدم عن أغنية حسب العنوان أو اسم الفنان، يجتاز التطبيق طلب البحث إلى AppSearch لاسترداد الأغاني المطابقة بسرعة وفعالية. تشير رسالة الأشكال البيانية يعرض التطبيق النتائج، ما يتيح للمستخدمين بدء اللعب بسرعة والأغاني المفضلة لديهم.

ضبط إعدادات الجهاز

لاستخدام AppSearch في تطبيقك، أضِف التبعيات التالية إلى ملف build.gradle للتطبيق:

Groovy

dependencies {
    def appsearch_version = "1.1.0-alpha06"

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

    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 هي مجموعة من المستندات التي تتوافق مع قاعدة البيانات Google. تنشئ تطبيقات العميل قاعدة بيانات من خلال تقديم تطبيقاتها والسياق واسم قاعدة البيانات. لا يمكن فتح قواعد البيانات إلا بواسطة التطبيق التي أنشأتها. عند فتح قاعدة بيانات، يتم إرجاع جلسة للتفاعل مع قاعدة البيانات. الجلسة هي نقطة الدخول لاستدعاء واجهات برمجة تطبيقات AppSearch ويظل مفتوحًا إلى أن يغلقه تطبيق العميل.

أنواع المخططات والمخططات

يمثِّل المخطط البنية التنظيمية للبيانات ضمن عملية بحث التطبيقات. قاعدة البيانات.

يتألف المخطط من أنواع مخططات تمثل أنواعًا فريدة من البيانات. تتكون أنواع المخططات من الخصائص التي تحتوي على اسم ونوع بيانات عدد القيم الفريدة للسمة. وبمجرد إضافة نوع المخطط إلى مخطط قاعدة البيانات، فإن مستندات نوع المخطط هذا وإضافته إلى قاعدة البيانات.

المستندات

في AppSearch، يتم تمثيل وحدة من البيانات كمستند. كل مستند في يتم تحديد قاعدة بيانات AppSearch بشكلٍ فريد من خلال مساحة الاسم ورقم التعريف. مساحات الاسم تُستخدم لفصل البيانات من مصادر مختلفة عندما يحتاج مصدر واحد فقط المطلوب البحث عنها، مثل حسابات المستخدمين.

تشتمل المستندات على طابع زمني للإنشاء ومدة البقاء (TTL) ونقاط ويمكن استخدامها للترتيب أثناء الاسترجاع. يتم أيضًا تعيين مخطط للمستند يصف خصائص البيانات الإضافية التي يجب أن يحتوي عليها المستند.

فئة المستند هي تجريد لمستند. تحتوي النتيجة على حقول تتضمن تعليقات توضيحية. التي تمثّل محتوى المستند. بشكل افتراضي، يكون اسم المستند تحدد الفئة اسم نوع المخطط.

تتم فهرسة المستندات ويمكن البحث عنها من خلال تقديم طلب بحث. الوثيقة عبارة عن بمطابقتها وتضمينها في نتائج البحث إذا كانت تحتوي على العبارات في طلب البحث أو تتطابق مع مواصفات بحث أخرى يتم ترتيب النتائج استنادًا إلى استراتيجية النتائج والترتيب. يتم تمثيل نتائج البحث بالصفحات التي يمكنك استردادها بشكل تسلسلي.

يوفّر AppSearch تخصيصات لشبكة البحث، مثل الفلاتر وإعدادات حجم الصفحة والمقتطفات

مقارنة بين مساحة تخزين النظام الأساسي والتخزين المحلي

تقدّم AppSearch حلَي تخزين هما LocalStorage وPlatformStorage. باستخدام LocalStorage، يمكن للتطبيق إدارة فهرس خاص بالتطبيق موجود في دليل بيانات التطبيق. باستخدام PlatformStorage، يمكن لتطبيقك في فهرس مركزي على مستوى النظام. الوصول إلى البيانات في الفهرس المركزي فقط على البيانات التي ساهم بها تطبيقك والبيانات التي تمت مشاركتها معك صراحةً بواسطة تطبيق آخر. كل من LocalStorage و تشترك PlatformStorage في واجهة برمجة التطبيقات نفسها ويمكن تبادلها استنادًا إلى إعدادات :الإصدار:

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

باستخدام PlatformStorage، يمكن لتطبيقك مشاركة البيانات بشكل آمن مع التطبيقات للسماح لها بالبحث في بيانات تطبيقك أيضًا. للقراءة فقط يتم منح مشاركة بيانات التطبيق عن طريق تأكيد اتصال الشهادة لضمان فإن التطبيق الآخر لديه إذن لقراءة البيانات. مزيد من المعلومات حول واجهة برمجة التطبيقات هذه في مستندات setSchemaType visibilityForPackage().

بالإضافة إلى ذلك، يمكن عرض البيانات المفهرَسة على مساحات عرض واجهة مستخدِم النظام. يمكن للتطبيقات إيقاف عرض بعض أو كل بياناتها على النظام مساحات عرض واجهة المستخدم يمكنك الاطّلاع على مزيد من المعلومات عن واجهة برمجة التطبيقات هذه في مستندات 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 تضع واجهات برمجة تطبيقات Jetpack على خدمة نظام AppSearch، يكون تأثير حجم حزمة APK ضئيلاً مقارنةً باستخدام LocalStorage. ومع ذلك، يعني هذا أيضًا أنّ عمليات AppSearch ستترتّب عليك وقت استجابة أداة الربط عند طلب خدمة نظام AppSearch. باستخدام PlatformStorage يحد AppSearch من عدد المستندات وحجم المستندات التي يمكن تطبيقها في التطبيق يمكننا فهرستها لضمان وجود فهرس مركزي فعال.

بدء استخدام AppSearch

يعرض المثال في هذا القسم كيفية استخدام واجهات AppSearch API لدمج باستخدام تطبيق افتراضي لحفظ الملاحظات.

كتابة صف مستند

تتمثل الخطوة الأولى للدمج مع AppSearch في كتابة فئة مستند وصف البيانات لإدراجها في قاعدة البيانات. وضع علامة على صف كصف في المستند من خلال استخدام @Document التعليق التوضيحي.يمكنك استخدام مثيلات فئة المستند لوضع المستندات في لاسترداد المستندات من قاعدة البيانات.

يحدد التعليمة البرمجية التالية فئة مستند الملاحظة تم إضافة @Document.StringProperty تعليقات توضيحية. لفهرسة نص عنصر الملاحظة.

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

فتح قاعدة بيانات

يجب عليك إنشاء قاعدة بيانات قبل العمل على المستندات. يُنشئ الكود التالي تنشئ قاعدة بيانات جديدة باسم notes_app وتحصل على ListenableFuture مقابل AppSearchSession، الذي يمثل الاتصال بقاعدة البيانات ويوفر واجهات برمجة التطبيقات وعمليات قاعدة البيانات.

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

إعداد مخطط

يجب تعيين مخطط قبل أن تتمكن من وضع المستندات واسترداد مستندات من قاعدة البيانات. يتكون مخطط قاعدة البيانات من أنواع مختلفة من البيانات المنظَّمة، ويُشار إليها باسم "أنواع المخططات". تُعيِّن التعليمة البرمجية التالية من خلال توفير فئة المستند كنوع مخطط.

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

وضع مستند في قاعدة البيانات

بعد إضافة نوع المخطط، يمكنك إضافة مستندات من هذا النوع إلى قاعدة البيانات. ينشئ الرمز التالي مستندًا من نوع المخطط Note باستخدام Note منصة إنشاء فئات المستندات. تضبط مساحة اسم المستند user1 لتمثيل المستخدم العشوائي لهذه العينة. ويتم بعد ذلك إدراج المستند في قاعدة البيانات ويتم ربط أداة استماع لمعالجة نتيجة عملية add.

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

يمكنك البحث في المستندات التي تمت فهرستها باستخدام عمليات البحث التي يتم تناولها في هذا القسم. تقوم التعليمة البرمجية التالية بإجراء استعلامات عن مصطلح "فاكهة" خلال قاعدة البيانات للمستندات التي تنتمي إلى مساحة الاسم 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);

التكرار من خلال نتائج البحث

تعرض عمليات البحث SearchResults الذي يمنح إمكانية الوصول إلى صفحات عناصر SearchResult. كل SearchResult على GenericDocument، وهي الشكل العام المستند الذي يتم تحويل جميع المستندات إليه. تحصل التعليمة البرمجية التالية على الأول صفحة نتائج البحث وتحويل النتيجة إلى مستند 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);

إزالة مستند

عندما يحذف المستخدم ملاحظة، يحذف التطبيق Note المقابلة. مستند من قاعدة البيانات. يضمن هذا عدم ظهور الملاحظة في طلبات البحث. يقدّم الرمز التالي طلبًا صريحًا لإزالة Note. من قاعدة البيانات بواسطة المعرف.

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

الاحتفاظ بالقرص

يجب الاحتفاظ بتحديثات قاعدة البيانات بشكل دوري على القرص عن طريق استدعاء requestFlush() تشير رسالة الأشكال البيانية يستدعي الرمز التالي requestFlush() مع أحد المستمعين لتحديد ما إذا كانت المكالمة كان ناجحًا.

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

إغلاق جلسة

إنّ AppSearchSession يجب إغلاقها عندما يتوقف أحد التطبيقات عن استدعاء أي قاعدة بيانات. العمليات التجارية. يغلق الرمز البرمجي التالي جلسة AppSearch التي تم فتحها ويستمر في جميع التحديثات على القرص.

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

مصادر إضافية

للحصول على مزيد من المعلومات حول AppSearch، يمكنك الاطلاع على الموارد الإضافية التالية:

نماذج

  • نموذج بحث Android (Kotlin)، تطبيق لتدوين الملاحظات يستخدم AppSearch لفهرسة ملاحظات المستخدم ويسمح للمستخدمين للبحث في ملاحظاتهم.

تقديم تعليقات

يمكنك مشاركة ملاحظاتك وأفكارك معنا من خلال هذه المراجع:

أداة تتبُّع المشاكل

يُرجى الإبلاغ عن الأخطاء لنتمكّن من إصلاحها.