Android'de tam metin arama örneği


89

Android ile tam metin aramayı (FTS) nasıl kullanacağımı anlamakta zorlanıyorum. FTS3 ve FTS4 uzantılarıyla ilgili SQLite belgelerini okudum . Ve Android'de yapmanın mümkün olduğunu biliyorum . Ancak, anlayabileceğim herhangi bir örnek bulmakta zorlanıyorum.

Temel veritabanı modeli

Bir SQLite veritabanı tablosunda (adlandırılmış example_table) 4 sütun vardır. Ancak, text_columntam metin araması için dizine alınması gereken yalnızca bir sütun (adlandırılmış ) vardır. Her satırtext_column uzunluğu 0 ile 1000 kelime arasında değişen metinler içerir. Toplam satır sayısı 10.000'den fazla.

  • Masayı ve / veya FTS sanal masasını nasıl kurarsınız?
  • Bir FTS sorgusunu nasıl gerçekleştirirsiniz text_column?

Ek Notlar:

  • Yalnızca bir sütunun dizine alınması gerektiğinden, yalnızca bir FTS tablosunun kullanılması (ve bırakılması example_table) FTS dışı sorgular için verimsiz olacaktır. .
  • Böylesine büyük bir tablo için, text_columnFTS tablosunda mükerrer girişlerin depolanması arzu edilmeyecektir. Bu gönderi , harici bir içerik tablosu kullanmayı önerir .
  • Harici içerik tabloları FTS4'ü kullanır, ancak FTS4, Android API 11'den önce desteklenmez . Bir cevap bir API> = 11 varsayabilir, ancak daha düşük sürümleri desteklemek için seçenekler hakkında yorum yapmak faydalı olacaktır.
  • Orijinal tablodaki verilerin değiştirilmesi, FTS tablosunu otomatik olarak güncellemez (ve tersi). Dahil tetikleyiciler Cevabınız bu temel örneğin gerekli değildir, ama yine de yararlı olacaktır.

3
İyi belgelenmiş soru, burada aldığınız keyfi olumsuz oylara karşı çıkıyorum.
Mekap

Yanıtlar:


118

En Temel Cevap

Her şeyin olabildiğince açık ve okunaklı olması için aşağıdaki düz sql kullanıyorum. Projenizde Android kolaylık yöntemlerini kullanabilirsiniz. dbAşağıda kullanılan nesne bir örneğidir SQLiteDatabase .

FTS Tablosu Oluştur

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

Bu onCreate(), genişletilmiş SQLiteOpenHelpersınıfınızın yöntemine girebilir .

FTS Tablosunu Doldur

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

Kullanımı daha iyi olurdu SQLiteDatabase # insert veya hazır deyimleri daha execSQL.

Sorgu FTS Tablosu

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

Ayrıca SQLiteDatabase # sorgu yöntemini de kullanabilirsiniz . Not MATCHanahtar kelime.

Fuller Cevap

Yukarıdaki sanal FTS tablosunda bununla ilgili bir sorun var. Her sütun dizine eklenir, ancak bazı sütunların dizine eklenmesi gerekmiyorsa bu alan ve kaynak israfıdır. FTS indeksine ihtiyaç duyan tek sütun muhtemelen text_column.

Bu sorunu çözmek için normal bir tablo ile sanal bir FTS tablosunun bir kombinasyonunu kullanacağız. FTS tablosu dizini içerecek, ancak normal tablodaki gerçek verilerin hiçbirini içermeyecektir. Bunun yerine, normal tablonun içeriğine bir bağlantı olacaktır. Buna harici içerik tablosu denir .

görüntü açıklamasını buraya girin

Tabloları Oluşturun

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

Bunu yapmak için FTS3 yerine FTS4 kullanmamız gerektiğine dikkat edin. FTS4, API sürüm 11'den önce Android'de desteklenmez. (1) yalnızca API> = 11 için arama işlevi sağlayabilir veya (2) bir FTS3 tablosu kullanabilirsiniz (ancak bu, tam metin sütunu olduğundan veri tabanının daha büyük olacağı anlamına gelir her iki veritabanında).

Tabloları Doldurun

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

(Yine, uçlarda yapmaktan daha iyi yollar var execSQL. Okunabilirliği için kullanıyorum.)

Şimdi bir FTS sorgusu yapmaya çalıştıysanız, fts_example_tablesonuç alamazsınız. Bunun nedeni, bir tabloyu değiştirmenin diğer tabloyu otomatik olarak değiştirmemesidir. FTS tablosunu manuel olarak güncellemeniz gerekir:

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

(Bu docid, rowidnormal bir tablo gibidir.) Harici içerik tablosunda her değişiklik yaptığınızda (INSERT, DELETE, UPDATE) FTS tablosunu (dizini güncelleyebilmesi için) güncellediğinizden emin olmalısınız. Bu külfetli olabilir. Yalnızca önceden doldurulmuş bir veritabanı oluşturuyorsanız, şunları yapabilirsiniz:

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

tüm tabloyu yeniden inşa edecek. Bu yavaş olabilir, bu yüzden her küçük değişiklikten sonra yapmak isteyeceğiniz bir şey değildir. Harici içerik tablosundaki tüm ekleri tamamladıktan sonra yaparsınız. Veritabanlarını otomatik olarak senkronize tutmanız gerekiyorsa, tetikleyicileri kullanabilirsiniz . Yön bulmak için buraya gidin ve biraz aşağı kaydırın.

Veritabanlarını Sorgula

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

Bu, öncekiyle aynıdır, ancak bu sefer yalnızca text_column(ve docid) öğelerine erişiminiz vardır . Harici içerik tablosundaki diğer sütunlardan veri almanız gerekirse ne olur? Yana docidFTS tablonun maçları rowid(ve bu durumda _iddış İçerik tablosunun), bir katılmak kullanabilirsiniz. ( Bununla ilgili yardım için bu cevap sayesinde .)

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

Daha fazla okuma

FTS sanal tablolarını kullanmanın diğer yollarını görmek için bu belgeleri dikkatlice gözden geçirin:

ek Notlar


1
Aslında, fts tablosunu belirttiğiniz şekilde kullanıyorsanız (_id, fts tablo eşleşmesi tarafından döndürülen docid kümesinde bulunan fts olmayan tablodan seçim yaparak), content = "" kullanarak yerden tasarruf edebilirsiniz. . Bu, içeriği kopyalamadan tam metin dizini oluşturacaktır. İçeriksiz FTS4 Tablolarına
astyanaxas

FTS4 içerik seçeneği SQLite 3.7.9'dan ( sqlite.org/releaselog/3_7_11.html ) daha önce eklendi , bu da Android API 16'dan önce kullanılamadığı anlamına gelir. SQLiteDatabase kullanım girişiminde bulunacaktır.
Knuckles

Bu sorgu aracılığıyla nasıl yarım kelime eşleşmesi elde ederim?
Hitesh Danidhariya

@HiteshDanidhariya, bu kısmi kelime eşleme yapmıyor mu? Üzgünüm, bunun üzerinde çalışmayalı epey oldu ama zaten yaptığını sanıyordum.
Suragch

@suragch çözümü aldım. searchString'den sonra "*" eklemek zorunda kaldı ve Teşekkürler. Cevabınız bana çok yardımcı oldu. :)
Hitesh Danidhariya

3

Fts tablosunu yeniden oluşturmak için kaynağından içerik kullandığınızı unutmayın.

Bunu güncelleme, ekleme, silme sırasında bir tetikleyici ile yapıyorum


INSERT INTO foo_fts VALUES("rebuild")
James Kipling
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.