Cách tôi thêm tính năng tìm kiếm chịu lỗi đánh máy (typo-tolerant) với OpenSearch và CJK

Các truy vấn không trả về kết quả đang làm giảm đáng kể thời gian xem của chúng tôi.

Trong một năm, TopVideoHub đã sử dụng SQLite FTS5 để tìm kiếm. Nó hoạt động tốt với các kết quả khớp chính xác, nhưng lại thất bại khi người dùng gõ sai chính tả.

Mọi người tìm kiếm "demon slyer" thay vì "demon slayer". Họ thêm các khoảng trắng thừa vào tiêu đề tiếng Nhật hoặc tiếng Hàn. Vì FTS5 khớp các token chính xác, chỉ một lỗi đánh máy nhỏ cũng dẫn đến kết quả bằng không. Người dùng sẽ không tìm kiếm lại; họ chỉ đơn giản là rời đi.

Tôi đã chuyển việc tìm kiếm tiêu đề sang OpenSearch. Dưới đây là cách tôi giải quyết vấn đề này cho đối tượng người dùng đa ngôn ngữ.

Vấn đề với Fuzziness tiêu chuẩn

Hầu hết các hướng dẫn đều khuyên bạn nên sử dụng "fuzziness" để khắc phục lỗi đánh máy. Điều này hiệu quả với tiếng Anh, nhưng lại thất bại với văn bản tiếng Trung, tiếng Nhật và tiếng Hàn (CJK).

  • Khoảng cách chỉnh sửa (edit distance) không hiệu quả với CJK. Một lỗi đánh máy trong CJK thường có nghĩa là sử dụng hoàn toàn sai ký tự.
  • Fuzziness tiêu chuẩn tạo ra các kết quả rác về mặt ngữ nghĩa. Nó có thể khớp "fire" với "water" chỉ vì chúng cách nhau một bước chỉnh sửa.
  • Việc tách từ (tokenization) rất khó khăn. Các ngôn ngữ CJK không sử dụng khoảng trắng giữa các từ.

Giải pháp: Tiếp cận đa trường (Multi-Field)

Tôi đã ngừng xử lý tất cả văn bản theo cùng một cách. Tôi tạo ra một trường tiêu đề logic duy nhất để lập chỉ mục theo ba cách khác nhau:

  • Latin và Romaji: Tôi sử dụng tokenization tiêu chuẩn với fuzziness được bật. Tôi đặt độ dài tiền tố (prefix length) là 1. Điều này đảm bảo "demon" khớp với "demn" nhưng "lemon" sẽ không khớp với "demon".
  • Văn bản CJK: Tôi sử dụng một bộ phân tích CJK bigram và một bộ chuẩn hóa ICU. Tôi tắt fuzziness. Thay vào đó, tôi sử dụng ngưỡng khớp tối thiểu (minimum match threshold) là 70%.
  • Autocomplete: Tôi sử dụng một trường edge-ngram để cung cấp kết quả tìm kiếm khi đang gõ (search-as-you-type).

Kiến trúc và An toàn dữ liệu

Tôi giữ SQLite làm nguồn dữ liệu gốc duy nhất (single source of truth). OpenSearch đóng vai trò là một chỉ mục nhanh và có thể xây dựng lại được.

  • Tôi sử dụng PHP để đẩy các bản cập nhật vào OpenSearch theo từng đợt lớn (bulk batches).
  • Tôi không bao giờ chạy lập chỉ mục (indexing) trong quá trình xử lý yêu cầu của người dùng.
  • Tôi chạy một script Python để kiểm tra sự "sai lệch" (drift). Điều này đảm bảo số lượng bản ghi trong OpenSearch khớp với số lượng trong SQLite.

Kết quả

Sự thay đổi là rất lớn:

  • Các truy vấn không có kết quả giảm từ 14% xuống dưới 3%.
  • Các phiên tìm kiếm trở nên dài hơn vì người dùng tìm thấy thứ họ muốn ngay lập tức.
  • Độ trễ vẫn duy trì ở mức thấp, khoảng 40ms.

Nếu bạn phục vụ đối tượng người dùng đa ngôn ngữ, hãy nhớ rằng: khả năng chịu lỗi đánh máy và việc khớp CJK là hai vấn đề khác nhau. Bạn cần hai giải pháp khác nhau.

Nguồn: https://dev.to/ahmet_gedik778845/how-i-added-typo-tolerant-video-title-search-with-opensearch-and-cjk-3e5d