Android의 전체 텍스트 검색 예


89

Android에서 전체 텍스트 검색 (FTS)을 사용하는 방법을 이해하는 데 어려움을 겪고 있습니다. FTS3 및 FTS4 확장에 대한 SQLite 문서를 읽었습니다 . Android에서 할 수 있다는 것을 알고 있습니다 . 그러나 이해할 수있는 예를 찾는 데 어려움을 겪고 있습니다.

기본 데이터베이스 모델

SQLite 데이터베이스 테이블 (이름 example_table)에는 4 개의 열이 있습니다. 그러나 text_column전체 텍스트 검색을 위해 인덱싱해야하는 열 (이름 )은 하나뿐입니다 . 의 모든 행 text_column에는 0에서 1000 단어까지 길이가 다양한 텍스트 가 포함됩니다. 총 행 수가 10,000보다 큽니다.

  • 테이블 및 / 또는 FTS 가상 테이블을 어떻게 설정 하시겠습니까?
  • FTS 쿼리를 text_column어떻게 수행 하시겠습니까?

추가 참고 사항 :

  • 하나의 열만 인덱싱해야하므로 FTS 테이블 만 사용하고 삭제하는 example_table것은 비 FTS 쿼리에 비효율적입니다 .
  • 이러한 큰 테이블의 text_column경우 FTS 테이블에의 중복 항목을 저장하는 것은 바람직하지 않습니다. 이 게시물외부 콘텐츠 테이블 사용을 제안 합니다 .
  • 외부 콘텐츠 테이블은 FTS4를 사용하지만 FTS4는 Android API 11 이전에 지원되지 않습니다. . 대답은 API가 11 이상이라고 가정 할 수 있지만 더 낮은 버전을 지원하기위한 옵션에 대한 의견은 도움이 될 것입니다.
  • 원래 테이블의 데이터를 변경해도 FTS 테이블이 자동으로 업데이트되지 않으며 그 반대의 경우도 마찬가지입니다. 이 기본 예에서는 답변에 트리거 를 포함 할 필요가 없지만 그럼에도 불구하고 도움이 될 것입니다.

3
잘 문서화 된 질문입니다. 여기서받은 임의의 반대표에 반대합니다.
Mekap

답변:


118

가장 기본적인 답변

모든 것이 가능한 한 명확하고 읽기 쉽게 아래의 일반 SQL을 사용하고 있습니다. 프로젝트에서 Android 편의 방법을 사용할 수 있습니다. db아래에 사용 된 객체는 SQLiteDatabase 의 인스턴스입니다 .

FTS 테이블 생성

db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 ( col_1, col_2, text_column )");

이것은 onCreate()확장 SQLiteOpenHelper클래스 의 방법으로 갈 수 있습니다.

FTS 테이블 채우기

db.execSQL("INSERT INTO fts_table VALUES ('3', 'apple', 'Hello. How are you?')");
db.execSQL("INSERT INTO fts_table VALUES ('24', 'car', 'Fine. Thank you.')");
db.execSQL("INSERT INTO fts_table VALUES ('13', 'book', 'This is an example.')");

사용하는 것이 좋습니다 것 의 SQLiteDatabase # 삽입 또는 준비된 명령문 이상을 execSQL.

FTS 테이블 쿼리

String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_table WHERE fts_table MATCH ?", selectionArgs);

SQLiteDatabase # query 메서드를 사용할 수도 있습니다 . MATCH키워드에 유의하십시오 .

풀러 답변

위의 가상 FTS 테이블에 문제가 있습니다. 모든 열이 인덱싱되지만 일부 열을 인덱싱 할 필요가 없으면 공간과 리소스가 낭비됩니다. FTS 인덱스가 필요한 유일한 열은 아마도 text_column.

이 문제를 해결하기 위해 일반 테이블과 가상 FTS 테이블의 조합을 사용합니다. FTS 테이블에는 인덱스가 포함되지만 일반 테이블의 실제 데이터는 포함되지 않습니다. 대신 일반 테이블의 내용에 대한 링크가 있습니다. 이를 외부 콘텐츠 테이블 이라고 합니다 .

여기에 이미지 설명 입력

테이블 만들기

db.execSQL("CREATE TABLE example_table (_id INTEGER PRIMARY KEY, col_1 INTEGER, col_2 TEXT, text_column TEXT)");
db.execSQL("CREATE VIRTUAL TABLE fts_example_table USING fts4 (content='example_table', text_column)");

이를 위해 FTS3 대신 FTS4를 사용해야합니다. FTS4는 API 버전 11 이전의 Android에서 지원되지 않습니다. (1) API> = 11에 대한 검색 기능 만 제공하거나 (2) FTS3 테이블을 사용할 수 있습니다 (그러나 이는 전체 텍스트 열이 존재하기 때문에 데이터베이스가 더 커진다는 것을 의미합니다). 두 데이터베이스 모두).

테이블 채우기

db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('3', 'apple', 'Hello. How are you?')");
db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('24', 'car', 'Fine. Thank you.')");
db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('13', 'book', 'This is an example.')");

(다시 말하지만,를 execSQL사용하는 것보다 do inserts에 더 나은 방법이 있습니다 . 저는 가독성을 위해 사용하고 있습니다.)

지금 FTS 쿼리를 시도하면 fts_example_table결과가 나타나지 않습니다. 그 이유는 한 테이블을 변경해도 다른 테이블이 자동으로 변경되지 않기 때문입니다. FTS 테이블을 수동으로 업데이트해야합니다.

db.execSQL("INSERT INTO fts_example_table (docid, text_column) SELECT _id, text_column FROM example_table");

( 일반 테이블 의 docid경우와 같습니다 rowid.) 외부 콘텐츠 테이블을 변경할 때 (INSERT, DELETE, UPDATE) 할 때마다 FTS 테이블을 업데이트 (인덱스를 업데이트 할 수 있도록)해야합니다. 이것은 번거로울 수 있습니다. 미리 채워진 데이터베이스 만 만드는 경우 다음을 수행 할 수 있습니다.

db.execSQL("INSERT INTO fts_example_table(fts_example_table) VALUES('rebuild')");

전체 테이블을 다시 빌드합니다. 그러나 이것은 느릴 수 있으므로 약간의 변화가있을 때마다하고 싶은 일이 아닙니다. 외부 컨텐츠 테이블에 대한 모든 삽입을 완료 한 후에 수행합니다. 데이터베이스를 자동으로 동기화해야하는 경우 트리거 를 사용할 수 있습니다 . 여기 로 이동하여 약간 아래로 스크롤하여 방향을 찾으십시오.

데이터베이스 쿼리

String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_example_table WHERE fts_example_table MATCH ?", selectionArgs);

이번에는 text_column(및 docid) 에만 액세스 할 수 있다는 점을 제외하면 이전과 동일합니다 . 외부 콘텐츠 테이블의 다른 열에서 데이터를 가져와야하는 경우 어떻게해야합니까? 때문에 docidFTS의 테이블이 일치합니다 rowid(이 경우 _id외부 콘텐츠 테이블의)을, 당신은 가입 할 수 있습니다. ( 이 답변 에 감사드립니다 .)

String sql = "SELECT * FROM example_table WHERE _id IN " +
        "(SELECT docid FROM fts_example_table WHERE fts_example_table MATCH ?)";
String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery(sql, selectionArgs);

추가 읽기

FTS 가상 테이블을 사용하는 다른 방법을 보려면 다음 문서를주의 깊게 살펴보십시오.

추가 참고 사항


1
실제로 지정한 방식으로 fts 테이블을 사용하는 경우 (fts 테이블 일치에 의해 반환 된 docid 집합에 _id가 포함 된 비 fts 테이블에서 선택) content = ""를 사용하여 공간을 절약 할 수 있습니다. . 콘텐츠를 복제하지 않고 전체 텍스트 인덱스를 만듭니다. 컨텐츠없는 FTS4 테이블
astyanaxas

FTS4 콘텐츠 옵션은 SQLite 3.7.9 ( sqlite.org/releaselog/3_7_11.html ) 이전에 추가되었습니다 . 이는 Android API 16 이전에는 사용할 수 없음을 의미합니다. SQLiteDatabase는 사용 시도시 발생합니다.
Knuckles

이 쿼리를 통해 어떻게 반 단어 일치를 얻습니까?
Hitesh Danidhariya

@HiteshDanidhariya, 이것은 부분 단어 일치를하지 않습니까? 죄송합니다. 작업 한 지 오래되었지만 이미 해본 것 같습니다.
Suragch

@suragch 해결책을 얻었습니다. searchString 뒤에 "*"를 추가해야했고 감사합니다. 귀하의 답변은 저를 많이 도왔습니다. :)
Hitesh Danidhariya

3

컨텐츠를 사용하여 fts 테이블을 재 구축 할 때 잊지 마십시오.

업데이트, 삽입, 삭제시 트리거로이 작업을 수행합니다.


INSERT INTO foo_fts VALUES("rebuild")
James Kipling 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.