Cuộc chiến giữa Lọc và Tìm kiếm: Bắt mạch Terms & Match Phrase trong Elasticsearch
Chào anh em Viblo, lại là mình đây!
Trong hành trình "đánh vật" với hệ thống tìm kiếm và xử lý dữ liệu lớn bằng Elasticsearch, chúng ta đã đi qua khá nhiều ngóc ngách từ Script Query phức tạp đến sự linh hoạt của should. Hôm nay, mình sẽ đưa anh em về lại những kiến thức nền tảng nhất, nhưng cũng là nơi dễ "đạp mìn" nhất nếu không nắm rõ bản chất: Sự khác biệt giữa Lọc chính xác (Terms) và Tìm kiếm cụm từ (Match Phrase).
Nhiều anh em mới tiếp cận Elasticsearch thường hay bối rối: "Ủa, khi nào thì dùng term, khi nào thì dùng match? Tại sao mình search chính xác từ khóa đó mà nó không ra?". Để mình kể cho anh em nghe câu chuyện về hai công cụ này nhé.
1. Terms Query: Kẻ máu lạnh, đòi hỏi sự tuyệt đối (Exact Match)
Trong Elasticsearch, term và terms (dùng cho mảng nhiều giá trị) là những query sinh ra để lọc (filter) chứ không phải để tìm kiếm văn bản. Tính cách của nó rất rõ ràng: Có hoặc Không, Đen hoặc Trắng, không có chuyện "gần đúng".
Bối cảnh thực tế: Khi làm việc với các hệ thống yêu cầu độ chính xác cao về mặt thống kê, chẳng hạn như truy xuất log giao dịch từ hệ thống vé tự động AFC, bạn cần lọc ra những giao dịch bị lỗi với mã lỗi cụ thể (ví dụ: ERR_001, ERR_005), hoặc lọc ra ID của một trạm cụ thể.
Lúc này, terms là nhà vô địch về tốc độ vì nó bỏ qua khâu tính điểm (Scoring) và đi thẳng vào Index để nhặt dữ liệu ra:
{
"query": {
"terms": {
"error_code": ["ERR_001", "ERR_005"]
}
}
}
Cú lừa kinh điển: Anh em thường mắc lỗi dùng term để tìm kiếm trên một trường kiểu text. Chẳng hạn, bạn lưu tên một sản phẩm là "Bàn phím cơ", sau đó dùng term search đúng chữ "Bàn phím cơ" và kết quả là... trắng bốc.
Tại sao? Vì trường text đã bị Elasticsearch chạy qua bộ phân tích (Analyzer), băm nhỏ thành các token ["bàn", "phím", "cơ"] và lưu vào Inverted Index. Dưới database không hề tồn tại nguyên khối chuỗi "Bàn phím cơ" để mà term có thể so khớp.
Kinh nghiệm rút ra: Chỉ sử dụng term / terms cho các trường dữ liệu mang tính phân loại, ID, trạng thái (thường được map với type là keyword, integer, boolean). Đừng bao giờ dùng nó cho các đoạn văn bản dài.
2. Match Phrase: Sự mềm mỏng trong khuôn khổ
Khi bạn cần tìm kiếm trên một trường văn bản dài (kiểu text), bạn phải dùng đến họ nhà match. Ở bài trước, mình đã nói về match thông thường (nó băm nhỏ từ khóa của bạn ra và tìm kiếm độc lập). Nhưng nếu bạn muốn các từ khóa đó phải đứng cạnh nhau, theo đúng thứ tự thì sao?
Đó là lúc match_phrase xuất hiện.
Bối cảnh thực tế: Giả sử trên hệ thống e-commerce, khách hàng gõ từ khóa: "sữa rửa mặt". Nếu bạn dùng match thường, Elasticsearch có thể trả về một sản phẩm có tên là: "Mặt nạ dưỡng da (tặng kèm sữa rửa tay)". Các từ "sữa", "rửa", "mặt" đều xuất hiện, nhưng nó nằm rải rác và sai hoàn toàn ngữ cảnh.
Với match_phrase, Elasticsearch sẽ yêu cầu:
- Sản phẩm phải chứa đủ 3 từ "sữa", "rửa", "mặt".
- Chúng phải đứng sát nhau và đúng theo thứ tự đó.
{
"query": {
"match_phrase": {
"product_name": "sữa rửa mặt"
}
}
}
Lúc này, khách hàng sẽ nhận được đúng những tuýp sữa rửa mặt chứ không phải là mặt nạ hay sữa tắm nữa.
3. Slop – Nghệ thuật "lách luật" cho Match Phrase
Đôi khi, match_phrase lại hơi khắt khe quá. Ngôn ngữ của người dùng thì đa dạng, họ có thể chèn thêm một vài từ chen ngang vào giữa cụm từ.
Ví dụ, khách hàng search: "sữa rửa mặt trị mụn", nhưng tên sản phẩm trong database của bạn lại là: "Sữa rửa mặt nam trị mụn". Chỉ vì thừa ra chữ "nam" xen giữa mà match_phrase bình thường sẽ đánh trượt sản phẩm này.
Đây là lúc tham số slop tỏa sáng. Slop định nghĩa số lượng khoảng trống (số từ) tối đa được phép chèn vào giữa cụm từ gốc để vẫn được coi là hợp lệ.
{
"query": {
"match_phrase": {
"product_name": {
"query": "sữa rửa mặt trị mụn",
"slop": 1
}
}
}
}
Với slop: 1, Elasticsearch hiểu rằng: "Được phép có 1 từ bất kỳ nhảy vào giữa cụm từ này". Nhờ vậy, chuỗi "Sữa rửa mặt nam trị mụn" vẫn lọt vào danh sách kết quả, cứu cho bạn một bàn thua trông thấy.
Lời kết
Để tối ưu hóa một công cụ tìm kiếm, ranh giới giữa Lọc (Filtering) và Tìm kiếm (Searching) cần được phân định rạch ròi:
- Dùng Terms khi bạn biết chính xác 100% thứ mình muốn tìm (ID, trạng thái, category) – thao tác trên trường keyword.
- Dùng Match Phrase khi bạn muốn tìm một cụm văn bản có ý nghĩa nguyên vẹn trong một trường text dài, và đừng quên sử dụng thêm slop để tạo độ đàn hồi cho trải nghiệm người dùng.
Chỉ cần nằm lòng quy tắc này, anh em đã có thể tránh được vô số những pha debug "chảy nước mắt" khi làm việc với Elasticsearch rồi. Anh em thường hay vấp phải lỗi cấu hình mapping nào nhất với hai loại query này? Hãy để lại comment bên dưới nhé! Happy coding!
All rights reserved