ตัวอย่างการค้นหาข้อความแบบเต็มใน Android


89

ฉันมีปัญหาในการทำความเข้าใจวิธีใช้การค้นหาข้อความแบบเต็ม (FTS) กับ Android ฉันได้อ่านเอกสาร SQLite ส่วนขยาย และฉันรู้ว่ามันเป็นไปได้ที่จะทำบน Android อย่างไรก็ตามฉันมีช่วงเวลาที่ยากลำบากในการค้นหาตัวอย่างใด ๆ ที่ฉันสามารถเข้าใจได้

แบบจำลองฐานข้อมูลพื้นฐาน

ตารางฐานข้อมูล SQLite (ชื่อexample_table) มี 4 คอลัมน์ อย่างไรก็ตามมีเพียงคอลัมน์เดียว (ชื่อtext_column) ที่ต้องจัดทำดัชนีสำหรับการค้นหาข้อความแบบเต็ม ทุกแถวtext_columnมีข้อความที่มีความยาวแตกต่างกันตั้งแต่ 0 ถึง 1,000 คำ จำนวนแถวทั้งหมดมากกว่า 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 # สังเกต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)");

สังเกตว่าเราต้องใช้ FTS4 เพื่อทำสิ่งนี้มากกว่า FTS3 ไม่รองรับ FTS4 ใน Android ก่อน API เวอร์ชัน 11 คุณสามารถ (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ฉันแค่ใช้มันเพื่อให้อ่านง่าย)

หากคุณพยายามทำแบบสอบถาม FTS ตอนนี้fts_example_tableคุณจะไม่ได้รับผลลัพธ์ เหตุผลก็คือการเปลี่ยนตารางหนึ่งตารางไม่ได้เปลี่ยนตารางอื่นโดยอัตโนมัติ คุณต้องอัปเดตตาราง FTS ด้วยตนเอง:

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

( docidเหมือนกับrowidตารางทั่วไป) คุณต้องแน่ใจว่าได้อัปเดตตาราง FTS (เพื่อให้สามารถอัปเดตดัชนีได้) ทุกครั้งที่คุณทำการเปลี่ยนแปลง (INSERT, DELETE, UPDATE) ไปยังตารางเนื้อหาภายนอก สิ่งนี้จะยุ่งยาก หากคุณกำลังสร้างฐานข้อมูลที่เติมไว้ล่วงหน้าเท่านั้นคุณสามารถทำได้

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) ได้เท่านั้น จะเกิดอะไรขึ้นหากคุณต้องการรับข้อมูลจากคอลัมน์อื่นในตารางเนื้อหาภายนอก เนื่องจากdocidตาราง FTS ตรงกับ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:

หมายเหตุเพิ่มเติม

  • ผู้ประกอบการ Set (AND, OR, NOT) ในการสืบค้นข้อมูล SQLite FTS มีมาตรฐานแบบสอบถามไวยากรณ์และปรับปรุงแบบสอบถามไวยากรณ์ แต่น่าเสียดายที่ Android เห็นได้ชัดว่าไม่สนับสนุนการปรับปรุงแบบสอบถามไวยากรณ์ (ดูที่นี่ , ที่นี่ , ที่นี่และที่นี่ ) นั่นหมายความว่าการผสม AND และ OR กลายเป็นเรื่องยาก (ต้องใช้UNIONหรือตรวจสอบPRAGMA compile_optionsดู) โชคร้ายมาก โปรดเพิ่มความคิดเห็นหากมีการอัปเดตในพื้นที่นี้

1
ในความเป็นจริงหากคุณใช้ตาราง fts ตามวิธีที่คุณระบุไว้ (เลือกจากตารางที่ไม่ใช่ fts ซึ่งมี _id อยู่ในชุดของ docid ที่ส่งคืนโดยการจับคู่ตาราง 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.