AppSearch

AppSearch는 로컬 관리를 위한 고성능 온디바이스 검색 솔루션입니다. 구조화된 데이터일 수도 있습니다 데이터 색인을 생성하고 데이터를 검색하기 위한 API를 포함합니다. 검색할 수 있습니다. 애플리케이션은 AppSearch를 사용하여 맞춤 인앱 제공 검색 기능을 통해 오프라인 상태에서도 콘텐츠를 검색할 수 있습니다.

AppSearch 내 색인 생성과 검색을 보여주는 다이어그램

AppSearch는 다음과 같은 기능을 제공합니다.

  • I/O 사용량이 적은 빠른 모바일 중심 스토리지 구현
  • 대규모 데이터 세트의 매우 효율적인 색인 생성 및 쿼리
  • 다국어 지원(예: 영어, 스페인어)
  • 관련성 순위 지정 및 사용 점수

I/O 사용량이 적기 때문에 AppSearch는 색인 생성 및 검색의 지연 시간이 짧음 더 효율적입니다. 교차 유형 쿼리를 간소화하는 AppSearch 단일 쿼리를 지원하는 반면 SQLite는 여러 테이블의 결과를 병합합니다.

AppSearch의 기능을 설명하기 위해 음악의 예를 들어보겠습니다. 사용자가 좋아하는 노래를 관리하고 음악을 쉽게 검색할 수 있게 해주는 애플리케이션 만들 수 있습니다 사용자는 다양한 형식의 노래 제목을 통해 전 세계의 음악을 즐깁니다. 사용할 수 있습니다. 이 사용자가 제목이나 아티스트 이름으로 노래를 검색하면 애플리케이션은 AppSearch에 요청을 보내 일치하는 노래를 신속하고 효율적으로 검색할 수 있습니다. 이 애플리케이션이 결과를 표시하여 사용자가 빠르게 플레이를 시작할 수 있도록 합니다. 좋아하는 노래를 들 수 있습니다.

설정

애플리케이션에서 AppSearch를 사용하려면 다음 종속 항목을 애플리케이션의 build.gradle 파일:

Groovy

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 개념

다음 다이어그램은 AppSearch 개념과 각각의 상호작용을 보여줍니다.

다이어그램
클라이언트 애플리케이션 및 애플리케이션과의 상호 작용에 대한 개요는
AppSearch 개념: AppSearch 데이터베이스, 스키마, 스키마 유형, 문서
있습니다.그림 1. AppSearch 개념 다이어그램: AppSearch 데이터베이스, 스키마 스키마 유형, 문서, 세션, 검색이 포함됩니다

데이터베이스 및 세션

AppSearch 데이터베이스는 데이터베이스와 일치하는 문서의 모음입니다. 사용할 수 있습니다 클라이언트 애플리케이션은 애플리케이션을 제공하여 데이터베이스를 생성함 데이터베이스 이름이 포함됩니다 데이터베이스는 애플리케이션에서만 열 수 있습니다. 확인할 수 있습니다 데이터베이스가 열리면 세션이 반환되어 상호작용합니다. 데이터베이스와 연결할 수 있습니다 세션은 AppSearch API를 호출하기 위한 진입점입니다. 클라이언트 애플리케이션에서 닫을 때까지 열려 있습니다

스키마 및 스키마 유형

스키마는 AppSearch 내에 있는 데이터의 조직 구조를 나타냅니다. 데이터베이스입니다.

스키마는 고유한 데이터 유형을 나타내는 스키마 유형으로 구성됩니다. 스키마 유형은 이름, 데이터 유형, 스키마가 포함된 카디널리티입니다. 스키마 유형이 데이터베이스 스키마에 추가되면 스키마 유형을 만들어 데이터베이스에 추가할 수 있습니다.

문서

AppSearch에서 데이터 단위는 문서로 표현됩니다. 하나의 AppSearch 데이터베이스는 네임스페이스와 ID로 고유하게 식별됩니다. 네임스페이스 하나의 소스에서만 필요할 때 여러 소스의 데이터를 분리하는 데 사용됨 사용자 계정과 같이 쿼리될 수 있습니다.

문서에는 생성 타임스탬프, TTL (수명), 검색 중 순위 지정에 사용할 수 있습니다. 문서에 스키마도 할당됨 문서에 포함되어야 하는 추가 데이터 속성을 설명하는 유형입니다.

문서 클래스는 문서를 추상화한 것입니다. 주석이 달린 필드가 포함되어 있습니다. 문서 내용을 나타냅니다. 기본적으로 class는 스키마 유형의 이름을 설정합니다.

문서는 색인이 생성되며 쿼리를 제공하여 검색할 수 있습니다. 문서는 검색어에 해당 단어가 포함된 경우 검색 결과에 포함됩니다. 또는 다른 검색 사양과 일치합니다. 결과는 다음을 기준으로 정렬됩니다. 순위 전략을 수립할 수 있습니다. 검색결과는 순차적으로 가져옵니다.

AppSearch는 맞춤설정을 제공합니다. 필터, 페이지 크기 구성 및 스니펫과 같은 검색에 사용할 수 있습니다.

플랫폼 스토리지와 로컬 스토리지 비교

AppSearch는 LocalStorage 및 PlatformStorage의 두 가지 저장소 솔루션을 제공합니다. 애플리케이션은 LocalStorage를 사용하여 로컬 저장소에 있는 앱별 색인을 관리합니다. 애플리케이션 데이터 디렉터리에 저장합니다 PlatformStorage를 사용하면 시스템 전체의 중앙 인덱스에 기여합니다. 중앙 색인 내의 데이터 액세스 애플리케이션이 제공한 데이터와 이전에 수집된 데이터로 명시적으로 공유될 수 있습니다. LocalStorage 및 PlatformStorage는 동일한 API를 공유하며 기기의 버전:

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

자바

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() 문서에 대한 문서를 참조하세요.

또한 색인이 생성된 데이터는 시스템 UI 노출 영역에 표시될 수 있습니다. 애플리케이션에서 데이터의 일부 또는 전부가 시스템에 표시되지 않도록 선택 해제할 수 있습니다. UI 노출 영역 이 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는 Jetpack API를 AppSearch 시스템 서비스를 사용하기 때문에 APK 크기에 미치는 영향이 미미합니다. 로컬 저장소 하지만 이는 AppSearch 작업에서 AppSearch 시스템 서비스를 호출할 때의 바인더 지연 시간 PlatformStorage를 사용하면 AppSearch는 애플리케이션의 문서 수와 문서 크기를 제한합니다. 중앙 색인을 효율적으로 생성할 수 있습니다.

AppSearch 시작하기

이 섹션의 예에서는 AppSearch API를 사용하여 가상의 메모 작성 애플리케이션을 제공할 수 있습니다

문서 클래스 작성

AppSearch와 통합하는 첫 번째 단계는 데이터베이스에 삽입할 데이터를 설명합니다. 클래스를 문서 클래스로 표시 @Document 사용 주석을 추가합니다.문서 클래스의 인스턴스를 사용하여 문서를 저장하고 데이터베이스에서 문서 검색

다음 코드는 @Document.StringProperty개 주석 처리됨 Note 객체의 텍스트 색인을 생성하기 위한 필드입니다.

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
)

자바

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

자바

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
)

자바

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

데이터베이스에 문서 저장

스키마 유형이 추가되면 해당 유형의 문서를 데이터베이스에 추가할 수 있습니다. 다음 코드는 Note를 사용하여 스키마 유형이 Note인 문서를 빌드합니다. 문서 클래스 빌더입니다. 이 클래스는 문서 네임스페이스 user1을 설정하여 이 샘플의 임의 사용자입니다. 그런 다음 문서가 데이터베이스에 삽입됩니다. 리스너가 연결되어 put 작업의 결과를 처리합니다.

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
)

자바

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
)

자바

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를 통해 반복

검색에서 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
)

자바

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를 찾습니다.

Kotlin

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()를 호출하여 호출이 이(가) 성공했습니다.

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)

자바

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 AppSearch 샘플 (Kotlin) AppSearch를 사용하여 사용자의 메모에 대한 색인을 생성하고 사용자가 메모를 작성할 수 있게 하는 메모 작성 앱 검색할 수 있습니다.

의견 보내기

다음 리소스를 통해 의견을 보내고 아이디어를 공유해 주세요.

Issue Tracker

버그를 신고하면 Google에서 수정할 수 있습니다.