Ví dụ về tìm kiếm toàn văn trong Android


87

Tôi đang gặp khó khăn khi hiểu cách sử dụng tìm kiếm toàn văn (FTS) với Android. Tôi đã đọc tài liệu SQLite về phần mở rộng FTS3 và FTS4 . Và tôi biết nó có thể làm được trên Android . Tuy nhiên, tôi đang gặp khó khăn trong việc tìm bất kỳ ví dụ nào mà tôi có thể hiểu được.

Mô hình cơ sở dữ liệu cơ bản

Một bảng cơ sở dữ liệu SQLite (được đặt tên example_table) có 4 cột. Tuy nhiên, chỉ có một cột (được đặt tên text_column) cần được lập chỉ mục để tìm kiếm toàn văn. Mỗi hàng text_columnchứa văn bản có độ dài khác nhau từ 0 đến 1000 từ. Tổng số hàng lớn hơn 10.000.

  • Bạn sẽ thiết lập bảng và / hoặc bảng ảo FTS như thế nào?
  • Bạn sẽ thực hiện truy vấn FTS text_columnnhư thế nào?

Ghi chú bổ sung:

  • Bởi vì chỉ một cột cần được lập chỉ mục, chỉ sử dụng bảng FTS (và giảm example_table) sẽ không hiệu quả đối với các truy vấn không phải FTS .
  • Đối với một bảng lớn như vậy, việc lưu trữ các mục trùng lặp text_columntrong bảng FTS sẽ là điều không mong muốn. Bài đăng này đề xuất sử dụng một bảng nội dung bên ngoài .
  • Các bảng nội dung bên ngoài sử dụng FTS4, nhưng FTS4 không được hỗ trợ trước Android API 11 . Một câu trả lời có thể giả sử API> = 11, nhưng nhận xét về các tùy chọn hỗ trợ các phiên bản thấp hơn sẽ rất hữu ích.
  • Việc thay đổi dữ liệu trong bảng gốc không tự động cập nhật bảng FTS (và ngược lại). Việc bao gồm các trình kích hoạt trong câu trả lời của bạn là không cần thiết đối với ví dụ cơ bản này, nhưng sẽ rất hữu ích.

3
Câu hỏi được ghi chép đầy đủ, tôi đang phản đối sự phản đối tùy ý mà bạn nhận được ở đây.
Mekap

Câu trả lời:


117

Câu trả lời cơ bản nhất

Tôi đang sử dụng sql đơn giản bên dưới để mọi thứ rõ ràng và dễ đọc nhất có thể. Trong dự án của mình, bạn có thể sử dụng các phương pháp tiện lợi của Android. Đối dbtượng được sử dụng bên dưới là một phiên bản của SQLiteDatabase .

Tạo bảng FTS

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

Điều này có thể đi trong onCreate()phương thức của SQLiteOpenHelperlớp mở rộng của bạn .

Điền bảng 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.')");

Sẽ tốt hơn nếu sử dụng SQLiteDatabase # insert hoặc các câu lệnh chuẩn bị hơn execSQL.

Bảng FTS truy vấn

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

Bạn cũng có thể sử dụng phương pháp truy vấn SQLiteDatabase # . Lưu ý MATCHtừ khóa.

Câu trả lời đầy đủ hơn

Bảng FTS ảo ở trên có vấn đề với nó. Mọi cột đều được lập chỉ mục, nhưng điều này sẽ lãng phí không gian và tài nguyên nếu một số cột không cần được lập chỉ mục. Cột duy nhất cần chỉ số FTS có lẽ là text_column.

Để giải quyết vấn đề này, chúng tôi sẽ sử dụng sự kết hợp của một bảng thông thường và một bảng FTS ảo. Bảng FTS sẽ chứa chỉ mục nhưng không chứa dữ liệu thực tế từ bảng thông thường. Thay vào đó nó sẽ có một liên kết đến nội dung của bảng thông thường. Đây được gọi là bảng nội dung bên ngoài .

nhập mô tả hình ảnh ở đây

Tạo bảng

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

Lưu ý rằng chúng ta phải sử dụng FTS4 để làm điều này chứ không phải FTS3. FTS4 không được hỗ trợ trong Android trước phiên bản API 11. Bạn có thể (1) chỉ cung cấp chức năng tìm kiếm cho API> = 11 hoặc (2) sử dụng bảng FTS3 (nhưng điều này có nghĩa là cơ sở dữ liệu sẽ lớn hơn vì tồn tại cột văn bản đầy đủ trong cả hai cơ sở dữ liệu).

Điền các bảng

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

(Một lần nữa, có nhiều cách tốt hơn để thực hiện chèn hơn là với execSQL. Tôi chỉ đang sử dụng nó để dễ đọc.)

Nếu bạn cố gắng thực hiện một truy vấn FTS ngay bây giờ, fts_example_tablebạn sẽ không nhận được kết quả. Lý do là thay đổi một bảng không tự động thay đổi bảng khác. Bạn phải cập nhật bảng FTS theo cách thủ công:

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

(Tương docidtự như rowidbảng thông thường.) Bạn phải đảm bảo cập nhật bảng FTS (để nó có thể cập nhật chỉ mục) mỗi khi bạn thực hiện thay đổi (CHÈN, XÓA, CẬP NHẬT) đối với bảng nội dung bên ngoài. Điều này có thể trở nên cồng kềnh. Nếu bạn chỉ đang tạo cơ sở dữ liệu được điều chỉnh trước, bạn có thể làm

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

sẽ xây dựng lại toàn bộ bảng. Tuy nhiên, điều này có thể chậm, vì vậy nó không phải là điều bạn muốn làm sau mỗi lần thay đổi nhỏ. Bạn sẽ làm điều đó sau khi hoàn thành tất cả các phần chèn trên bảng nội dung bên ngoài. Nếu bạn cần tự động đồng bộ hóa cơ sở dữ liệu, bạn có thể sử dụng trình kích hoạt . Tới đây và cuộn xuống một chút để tìm chỉ đường.

Truy vấn cơ sở dữ liệu

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

Điều này giống như trước đây, ngoại trừ lần này bạn chỉ có quyền truy cập vào text_column(và docid). Điều gì sẽ xảy ra nếu bạn cần lấy dữ liệu từ các cột khác trong bảng nội dung bên ngoài? Vì docidbảng FTS khớp với rowid(và trong trường hợp này _id) của bảng nội dung bên ngoài, bạn có thể sử dụng một phép nối. (Cảm ơn câu trả lời này đã giúp về điều đó.)

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

Đọc thêm

Xem kỹ các tài liệu này để xem các cách sử dụng bảng ảo FTS khác:

Ghi chú bổ sung


1
Trên thực tế, nếu bạn đang sử dụng bảng fts theo cách mà bạn đã chỉ định (chọn từ bảng không phải fts trong đó _id được chứa trong tập hợp docid được trả về bởi đối sánh bảng fts), bạn có thể tiết kiệm dung lượng bằng cách sử dụng content = "" . Điều này sẽ tạo chỉ mục toàn văn mà không trùng lặp nội dung. Xem Bảng FTS4 không có nội dung
astyanaxas

Tùy chọn nội dung FTS4 đã được thêm vào không sớm hơn trong SQLite 3.7.9 ( sqlite.org/releaselog/3_7_11.html ), có nghĩa là nó không khả dụng trước Android API 16. SQLiteDatabase sẽ cố gắng sử dụng.
Knuckles

Làm thế nào để tôi có được một nửa từ phù hợp, thông qua truy vấn này?
Hitesh Danidhariya

@HiteshDanidhariya, điều này không thực hiện đối sánh từng phần phải không? Xin lỗi, đã lâu tôi chưa làm việc này, nhưng tôi nghĩ nó đã làm được rồi.
Suragch

@suragch Đã có giải pháp. Vui lòng thêm "*" sau chuỗi tìm kiếm và Cảm ơn. Câu trả lời của bạn đã giúp tôi rất nhiều. :)
Hitesh Danidhariya

3

Đừng quên khi sử dụng nội dung từ để xây dựng lại bảng fts.

Tôi làm điều này với một trình kích hoạt khi cập nhật, chèn, xóa


INSERT INTO foo_fts VALUES("rebuild")
James Kipling
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.