Elasticsearch Bài học 17: Keyword vs Text - Sự khác biệt cốt lõi quyết định kết quả tìm kiếm
Trong thế giới của CSDL quan hệ (MySQL), chuỗi văn bản thường chỉ xoay quanh VARCHAR hoặc TEXT. Dù độ dài có khác nhau, bản chất lưu trữ và cách bạn truy vấn chúng (bằng LIKE hoặc =) là tương tự nhau.
Nhưng khi bước chân vào Elasticsearch, chuỗi văn bản bị chia làm hai thái cực hoàn toàn khác biệt: text và keyword. Việc chọn sai 1 trong 2 kiểu này là nguyên nhân của 90% các bug liên quan đến:
-
"Tại sao em search chính xác mã SKU này mà không ra?"
-
"Tại sao hệ thống báo lỗi khi em muốn Group By (Aggregations) theo tên sản phẩm?"
-
"Tại sao sort (sắp xếp) theo tên Category lại làm sập Elasticsearch?"
Bài học này sẽ bóc tách bản chất của hai kiểu dữ liệu này để bạn không bao giờ chọn sai khi thiết kế hệ thống.
1. Kiểu dữ liệu text (Sinh ra để Tìm kiếm)
Kiểu text được thiết kế dành riêng cho Full-text Search (Tìm kiếm toàn văn bản).
Bản chất hoạt động: Khi bạn đẩy một chuỗi như "Sữa rửa mặt Cetaphil 500ml" vào trường kiểu text, chuỗi này KHÔNG được lưu nguyên vẹn vào Inverted Index. Nó bắt buộc phải đi qua một bộ máy nghiền nát gọi là Analyzer. Analyzer sẽ:
-
Chuyển tất cả thành chữ thường.
-
Cắt bỏ dấu câu.
-
Băm nhỏ chuỗi thành các từ đơn lẻ (Token):
["sữa", "rửa", "mặt", "cetaphil", "500ml"].
Nhờ sự "băm nhỏ" này, khi khách hàng lên Hasaki gõ chữ "sữa cetaphil", Elasticsearch dễ dàng đối chiếu và tìm ra Document gốc.
Cấm kỵ của kiểu text: Chính vì chuỗi đã bị băm nhỏ, bạn không thể thực hiện các thao tác mang tính toàn vẹn trên trường này:
-
Không thể Group By (Aggregations): Nếu bạn gom nhóm theo trường
text, Elasticsearch sẽ đếm từ "sữa" là 1 nhóm, từ "mặt" là 1 nhóm. Điều này hoàn toàn vô nghĩa đối với nghiệp vụ. -
Không thể Sort (Sắp xếp): Làm sao bạn có thể sắp xếp tên sản phẩm theo Alphabet khi tên đó đã bị băm thành chục từ rời rạc? Nếu cố tình sort trên trường
text, Elasticsearch sẽ ném lỗi ngay lập tức để bảo vệ RAM của server.
2. Kiểu dữ liệu keyword (Sinh ra để Lọc và Thống kê)
Kiểu keyword lại đối lập hoàn toàn. Nó sinh ra để dành cho các thao tác Exact Match (Khớp chính xác), Filtering (Lọc), Sorting (Sắp xếp) và Aggregations (Thống kê).
Bản chất hoạt động: Khi bạn đẩy chuỗi "Sữa rửa mặt Cetaphil 500ml" vào trường kiểu keyword, nó bỏ qua hoàn toàn Analyzer. Nguyên một cụm chuỗi dài ngoằng (bao gồm cả chữ hoa, chữ thường, dấu cách) sẽ được ném thẳng vào Inverted Index như một Token duy nhất.
Sức mạnh của Doc Values: Bên cạnh Inverted Index, trường keyword còn được Elasticsearch ưu ái tạo thêm một cấu trúc dữ liệu lưu trên đĩa gọi là Doc Values (lưu trữ dạng cột - Columnar Store). Nhờ Doc Values, việc sắp xếp (Sort) hay gom nhóm (Aggregations) trên trường keyword diễn ra với tốc độ ánh sáng mà không hề ngốn RAM (Heap Memory).
Khi nào dùng keyword?
-
ID, Mã SKU, Mã vạch sản phẩm.
-
Trạng thái đơn hàng (
PENDING,SUCCESS). -
Tên thương hiệu (
La Roche-Posay), Danh mục. -
Tags, Email, Số điện thoại.
3. Trận chiến truy vấn: Khi nào lỗi xảy ra?
Sự nhầm lẫn lớn nhất thường diễn ra ở tầng Query (khi API Backend gửi request tìm kiếm).
Giả sử bạn có mã đơn hàng: "ORDER-2026-X1".
-
Nếu bạn set là kiểu
text: Nó bị cắt thành 3 tokens:["order", "2026", "x1"]. Nếu bạn dùng lệnhtermquery (tìm chính xác tuyệt đối) từ khóa"ORDER-2026-X1", hệ thống trả về 0 kết quả. (Vì trong kho token chỉ có các chữ viết thường rời rạc, không có token nào chứa nguyên cụm chữ hoa đó). -
Nếu bạn set là kiểu
keyword: Nó giữ nguyên"ORDER-2026-X1". Nếu khách hàng gõ trên thanh tìm kiếm"order 2026"và bạn dùng lệnhmatchquery, hệ thống trả về 0 kết quả. (Vì người dùng gõ chữ thường và thiếu đuôi X1, không khớp hoàn toàn với token gốc).
4. Giải pháp "Ăn cả hai": Multi-fields
Trong e-commerce, một trường dữ liệu thường phải gánh vác nhiều nhiệm vụ. Ví dụ với trường Tên sản phẩm (name), bạn vừa muốn khách hàng gõ "sữa rửa" là tìm ra (text), lại vừa muốn vẽ biểu đồ tròn đếm số lượng sản phẩm theo tên chính xác (keyword), đồng thời muốn sắp xếp tên theo bảng chữ cái.
Giải pháp chuẩn mực là định nghĩa Multi-fields (Đa trường) trong Mapping:
JSON
PUT products
{
"mappings": {
"properties": {
"name": {
"type": "text",
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
Với định nghĩa này, cùng một JSON bạn đẩy lên, Elasticsearch sẽ lưu nó ở 2 nơi dưới 2 hình thức:
-
name: Bị đưa qua Analyzer, phục vụ tìm kiếm Full-text (matchquery). -
name.raw: Bỏ qua Analyzer, phục vụ lọc chính xác, sắp xếp và thống kê (termquery,aggs). (Tham sốignore_above: 256là một lớp bảo vệ: nếu chuỗi dài hơn 256 ký tự, nó sẽ không được lưu vào bộ keyword để tiết kiệm dung lượng đĩa).
Cách dùng lúc gọi API Backend:
-
Khi user gõ vào ô tìm kiếm: Truy vấn vào trường
name. -
Khi vẽ Dashboard báo cáo / Lọc Sort: Gọi vào trường
name.raw.
Làm chủ được text và keyword, bạn đã loại bỏ được 90% các lỗi ngớ ngẩn về dữ liệu. Nhưng dữ liệu thực tế không chỉ có chuỗi, nó còn chứa thời gian, mảng, và các object JSON lồng nhau phức tạp.
All rights reserved