0

Elasticsearch Bài học 20: Tự build Custom Analyzer để phục vụ bài toán nghiệp vụ đặc thù

Ở Bài 19, chúng ta đã hiểu rõ cơ chế 3 bước của một Analyzer: Character Filters -> Tokenizer -> Token Filters. Bộ standard analyzer mặc định hoạt động khá ổn cho văn bản tiếng Anh thông thường, nhưng khi áp dụng vào E-commerce (nơi đầy rẫy mã SKU, tên thương hiệu viết tắt, và từ lóng), nó sẽ tạo ra những kết quả tìm kiếm cực kỳ thảm họa.

Khi đó, bạn bắt buộc phải tự tay "lắp ráp" một Custom Analyzer (Bộ phân tích tùy chỉnh) của riêng mình.

1. Khai báo Custom Analyzer ở đâu?

Khác với Mapping (định nghĩa cấu trúc dữ liệu), Analyzer thuộc về phần Settings (Cấu hình luồng hoạt động) của Index.

Bạn phải định nghĩa Custom Analyzer ngay lúc khởi tạo Index bằng lệnh PUT. Cấu trúc khai báo luôn tuân theo nguyên tắc: Định nghĩa các thành phần lẻ (char_filter, filter) trước, rồi mới "lắp ráp" chúng lại trong block analyzer.

2. Kịch bản E-commerce: Bài toán "H&M" và "KCN"

Giả sử hệ thống Hasaki của bạn gặp 2 bài toán tìm kiếm từ khách hàng:

  1. Lỗi ký tự đặc biệt: Khách tìm chữ "H&M". Analyzer mặc định xóa dấu & và tách thành 2 chữ "h" và "m", khiến kết quả search ra toàn các từ chứa chữ H hoặc M (rất rác). Chúng ta muốn giữ nguyên hoặc biến nó thành một từ liền mạch.

  2. Từ đồng nghĩa & Viết tắt: Khách hàng lười gõ, họ tìm "kcn", nhưng tên sản phẩm trên database lại ghi là "Kem chống nắng". Hoặc tìm "srm" thay cho "Sữa rửa mặt".

Chúng ta sẽ giải quyết cả 2 bài toán này trong cùng 1 Custom Analyzer.

3. Thực hành: Khởi tạo Index với Custom Analyzer

Hãy mở Kibana Dev Tools và chạy lệnh khởi tạo Index hasaki_products sau:

JSON

PUT hasaki_products
{
  "settings": {
    "analysis": {
      "char_filter": {
        "brand_mapper": {
          "type": "mapping",
          "mappings": [
            "H&M => hm_brand",
            "M.A.C => mac_brand"
          ]
        }
      },
      "filter": {
        "ecommerce_synonyms": {
          "type": "synonym",
          "synonyms": [
            "kcn, kem chống nắng",
            "srm, sữa rửa mặt",
            "laptop, máy tính xách tay, macbook"
          ]
        }
      },
      "analyzer": {
        "hasaki_custom_analyzer": {
          "type": "custom",
          "char_filter": [
            "brand_mapper"
          ],
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "ecommerce_synonyms"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "hasaki_custom_analyzer"
      }
    }
  }
}

Phân tích cấu trúc lắp ráp trên:

  1. Block char_filter: Chúng ta tự chế ra một bộ lọc ký tự tên là brand_mapper. Nếu thấy chữ H&M, nó sẽ lập tức thay thế thành chuỗi hm_brand (để bảo vệ chữ này không bị Tokenizer băm nát ở bước sau).

  2. Block filter: Chúng ta chế ra bộ lọc token tên là ecommerce_synonyms dùng kiểu synonym. Ở đây quy định rõ các cụm từ đồng nghĩa cách nhau bằng dấu phẩy.

  3. Block analyzer: Đây là nơi lắp ráp thành phẩm mang tên hasaki_custom_analyzer. Chúng ta khai báo type là custom và nạp các phụ kiện vào theo đúng thứ tự luồng chảy: Đi qua brand_mapper -> Băm bằng standard tokenizer -> Đưa tất cả về chữ thường (lowercase) -> Cuối cùng nhân bản các từ đồng nghĩa qua ecommerce_synonyms.

  4. Block mappings: Cuối cùng, chúng ta áp dụng thẳng chiếc máy tự chế này cho trường name của dữ liệu.

4. X-Quang thành quả bằng API _analyze

Để kiểm chứng xem chiếc máy của chúng ta hoạt động có đúng logic không, hãy gọi API kiểm tra:

JSON

POST hasaki_products/_analyze
{
  "analyzer": "hasaki_custom_analyzer",
  "text": "Sữa rửa mặt nam H&M"
}

Kết quả Token sinh ra trong Inverted Index sẽ là:

["sữa", "rửa", "mặt", "srm", "nam", "hm_brand"]

Hãy chú ý phép màu vừa xảy ra:

  • Cụm "H&M" không bị băm nát thành "h" và "m" nữa, nó đã được map thành token hm_brand.

  • Cụm "Sữa rửa mặt" không chỉ sinh ra các token rời rạc sữa, rửa, mặt, mà nhờ có bộ lọc synonym, nó tự động nội suy và đẻ thêm một token srm nhét vào Index (dù trong câu gốc không hề có chữ srm).

  • Giờ đây, khách hàng lên ô tìm kiếm gõ "srm nam hm" chắc chắn sẽ ra đúng sản phẩm này.

5. Quy tắc vận hành (Production Rule)

Với tư cách là Backend Engineer, bạn phải nằm lòng quy tắc sinh tử này: Bạn KHÔNG THỂ thay đổi Settings Analyzer của một Index đang mở (Open).

Nếu ngày mai Hasaki có thêm từ đồng nghĩa mới "tt" -> "tẩy trang", bạn không thể gửi lệnh PUT sửa thẳng vào Index đang chạy live. Bạn bắt buộc phải thực hiện 3 bước:

  1. Đóng Index tạm thời: POST hasaki_products/_close (Lúc này API search sẽ báo lỗi).

  2. Cập nhật Settings thêm từ đồng nghĩa: PUT hasaki_products/_settings { ... }

  3. Mở lại Index: POST hasaki_products/_open

(Cách tối ưu hơn để cập nhật từ đồng nghĩa mà không gây downtime là đưa danh sách synonym ra một file .txt độc lập trên server và dùng API Reload Search Analyzers - nhưng đó là một bài toán nâng cao dành cho vận hành cụm lớn).

Đến đây, bạn đã biết cách build một Analyzer cho riêng mình. Tuy nhiên, nếu bạn thử nhập một câu tiếng Việt có dấu phức tạp như "Chống nắng đi biển", standard tokenizer vẫn băm nó theo khoảng trắng thành ["chống", "nắng", "đi", "biển"]. Trong tiếng Việt, "chống nắng" là một từ ghép có nghĩa, nếu tách rời nó sẽ làm giảm độ chính xác của điểm Ranking (Scoring).

Để giải quyết triệt để tìm kiếm tiếng Việt, chúng ta cần một Tokenizer thông minh hơn, dựa trên từ điển NLP.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.