Contoh pencarian teks lengkap di Android

89

Saya kesulitan memahami cara menggunakan penelusuran teks lengkap (FTS) dengan Android. Saya telah membaca dokumentasi SQLite tentang ekstensi FTS3 dan FTS4 . Dan saya tahu itu mungkin dilakukan di Android . Namun, saya kesulitan menemukan contoh yang dapat saya pahami.

Model database dasar

Tabel database SQLite (bernama example_table) memiliki 4 kolom. Namun, hanya ada satu kolom (bernama text_column) yang perlu diindeks untuk pencarian teks lengkap. Setiap baris text_columnberisi teks yang panjangnya bervariasi dari 0 hingga 1000 kata. Jumlah baris lebih dari 10.000.

  • Bagaimana Anda menyiapkan tabel dan / atau tabel virtual FTS?
  • Bagaimana Anda akan melakukan kueri FTS text_column?

Catatan tambahan:

  • Karena hanya satu kolom yang perlu diindeks, hanya menggunakan tabel FTS (dan menghapus example_table) tidak akan efisien untuk kueri non-FTS .
  • Untuk tabel sebesar itu, menyimpan entri duplikat text_columndalam tabel FTS tidak diinginkan. Posting ini menyarankan menggunakan tabel konten eksternal .
  • Tabel konten eksternal menggunakan FTS4, tetapi FTS4 tidak didukung sebelum Android API 11 . Sebuah jawaban dapat mengasumsikan API> = 11, tetapi mengomentari opsi untuk mendukung versi yang lebih rendah akan sangat membantu.
  • Mengubah data dalam tabel asli tidak secara otomatis memperbarui tabel FTS (dan sebaliknya). Memasukkan pemicu dalam jawaban Anda tidak perlu untuk contoh dasar ini, tetapi tetap akan membantu.
Suragch
sumber
3
Pertanyaan yang terdokumentasi dengan baik, saya membantah suara negatif yang Anda dapatkan di sini.
Mekap

Jawaban:

118

Jawaban Paling Dasar

Saya menggunakan sql biasa di bawah ini sehingga semuanya sejelas dan terbaca. Dalam proyek Anda, Anda dapat menggunakan metode kenyamanan Android. The dbobjek yang digunakan di bawah ini adalah sebuah contoh dari SQLiteDatabase .

Buat Tabel FTS

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

Ini bisa masuk ke onCreate()metode SQLiteOpenHelperkelas tambahan Anda .

Isi Tabel 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.')");

Akan lebih baik jika menggunakan SQLiteDatabase # insert atau prepared statement daripada execSQL.

Tabel FTS Kueri

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

Anda juga bisa menggunakan metode kueri SQLiteDatabase # . Catat MATCHkata kuncinya.

Jawaban Lebih Lengkap

Tabel FTS virtual di atas bermasalah. Setiap kolom diindeks, tetapi ini akan membuang-buang ruang dan sumber daya jika beberapa kolom tidak perlu diindeks. Satu-satunya kolom yang membutuhkan indeks FTS mungkin adalah text_column.

Untuk mengatasi masalah ini kita akan menggunakan kombinasi tabel biasa dan tabel FTS virtual. Tabel FTS akan berisi indeks tetapi tidak ada data aktual dari tabel biasa. Alih-alih itu akan memiliki tautan ke konten tabel biasa. Ini disebut tabel konten eksternal .

masukkan deskripsi gambar di sini

Buat Tabel

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

Perhatikan bahwa kita harus menggunakan FTS4 untuk melakukan ini daripada FTS3. FTS4 tidak didukung di Android sebelum API versi 11. Anda dapat (1) hanya menyediakan fungsionalitas pencarian untuk API> = 11, atau (2) menggunakan tabel FTS3 (tetapi ini berarti database akan lebih besar karena kolom teks lengkap ada di kedua database).

Isi Tabel

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.')");

(Sekali lagi, ada cara yang lebih baik untuk memasukkan do daripada dengan execSQL. Saya hanya menggunakannya agar mudah dibaca.)

Jika Anda mencoba melakukan query FTS sekarang, fts_example_tableAnda tidak akan mendapatkan hasil. Alasannya adalah bahwa mengubah satu tabel tidak secara otomatis mengubah tabel lainnya. Anda harus memperbarui tabel FTS secara manual:

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

(Ini docidseperti rowiduntuk tabel biasa.) Anda harus memastikan untuk memperbarui tabel FTS (sehingga dapat memperbarui indeks) setiap kali Anda membuat perubahan (INSERT, DELETE, UPDATE) ke tabel konten eksternal. Ini bisa menjadi tidak praktis. Jika Anda hanya membuat database yang sudah diisi sebelumnya, Anda bisa melakukannya

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

yang akan membangun kembali seluruh tabel. Ini bisa lambat, jadi ini bukan sesuatu yang ingin Anda lakukan setelah setiap perubahan kecil. Anda akan melakukannya setelah menyelesaikan semua sisipan di tabel konten eksternal. Jika Anda memang perlu menjaga database tetap sinkron secara otomatis, Anda dapat menggunakan pemicu . Buka di sini dan gulir ke bawah sedikit untuk menemukan petunjuk arah.

Buat kueri pada Database

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

Ini sama seperti sebelumnya, kecuali kali ini Anda hanya memiliki akses ke text_column(dan docid). Bagaimana jika Anda perlu mendapatkan data dari kolom lain di tabel konten eksternal? Karena docidtabel FTS cocok dengan rowid(dan dalam kasus ini _id) dari tabel konten eksternal, Anda dapat menggunakan gabungan. (Terima kasih atas jawaban ini untuk bantuannya.)

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

Bacaan lebih lanjut

Bacalah dokumen-dokumen ini dengan hati-hati untuk mengetahui cara lain menggunakan tabel virtual FTS:

catatan tambahan

Suragch
sumber
1
Faktanya, jika Anda menggunakan tabel fts dengan cara yang Anda tentukan (memilih dari tabel non-fts di mana _id berada dalam kumpulan dokumen yang dikembalikan oleh tabel fts cocok), Anda dapat menghemat ruang dengan menggunakan content = "" . Ini akan membuat indeks teks lengkap tanpa menduplikasi konten. Lihat Tabel FTS4 Tanpa Konten
astyanaxas
Opsi konten FTS4 ditambahkan tidak lebih awal dari pada SQLite 3.7.9 ( sqlite.org/releaselog/3_7_11.html ), yang berarti tidak tersedia sebelum Android API 16. SQLiteDatabase akan melakukan upaya penggunaan.
Knuckles
Bagaimana cara mendapatkan kecocokan setengah kata, melalui kueri ini?
Hitesh Danidhariya
@HiteshDanidhariya, bukankah ini melakukan pencocokan kata sebagian? Maaf, sudah lama sejak saya mengerjakan ini, tetapi saya pikir itu sudah melakukannya.
Suragch
@suragch Punya solusinya. Harus menambahkan "*" setelah searchString dan Thanks.Your menjawab sangat membantu saya. :)
Hitesh Danidhariya
3

Jangan lupa saat menggunakan konten from untuk membangun kembali tabel fts.

Saya melakukan ini dengan pemicu pada pembaruan, sisipkan, hapus

James Kipling
sumber
INSERT INTO foo_fts VALUES("rebuild")
James Kipling