0

Elasticsearch Bài học 26: Bool Query - Xương sống của mọi câu query phức tạp (Must, Should, Must_not, Filter)

Trong thực tế làm backend cho các hệ thống như Hasaki hay kiểm soát luồng giao dịch, người dùng hiếm khi chỉ tìm kiếm với một điều kiện duy nhất. Họ sẽ đưa ra những yêu cầu kiểu: "Tìm tất cả sữa rửa mặt trị mụn, giá dưới 300k, thuộc hãng Cetaphil hoặc La Roche-Posay, nhưng tuyệt đối không lấy hàng sắp hết hạn".

Để giải quyết bài toán đa điều kiện này, Elasticsearch cung cấp Bool Query. Nó đóng vai trò là "chiếc hộp chứa" (Compound query), cho phép bạn nhét nhiều truy vấn lá (Leaf queries) vào bên trong và kết nối chúng bằng logic Toán học (Boolean Logic).

1. Cấu trúc tứ trụ của Bool Query

Bool Query được xây dựng dựa trên 4 mệnh đề cốt lõi. Mỗi mệnh đề nhận vào một mảng (array) các câu query con.

A. must (BẮT BUỘC CÓ - AND)

  • Logic: Tất cả các điều kiện nằm trong mảng must đều bắt buộc phải thỏa mãn (Giống hệt toán tử AND trong SQL).

  • Điểm số (Scoring): Các truy vấn trong must ĐÓNG GÓP vào điểm Relevance Score. Càng khớp nhiều, điểm càng cao.

  • Ví dụ: Sản phẩm phải chứa chữ "sữa rửa mặt".

B. filter (BẮT BUỘC CÓ - Nhưng KHÔNG TÍNH ĐIỂM)

  • Logic: Tương tự như must, các điều kiện trong filter bắt buộc phải thỏa mãn.

  • Điểm số: KHÔNG đóng góp vào Relevance Score.

  • Sức mạnh vô địch: Vì không phải tính điểm phức tạp, kết quả của filter được Elasticsearch Cache (lưu vào bộ nhớ đệm) tự động. Nó chạy cực kỳ nhanh.

  • Quy tắc ngầm: Nếu bạn đang lọc theo keyword (Hãng, Mã SKU), hoặc số học (Giá tiền, Ngày tháng), HÃY LUÔN ĐẶT NÓ VÀO filter, tuyệt đối không để ở must.

C. should (NÊN CÓ - OR)

  • Logic: Đóng vai trò như toán tử OR.

  • Tương tác với Must/Filter: Nếu trong khối bool CÓ chứa must hoặc filter, thì should chỉ đóng vai trò "tăng điểm thưởng". Tức là Document không thỏa mãn should vẫn được trả về, nhưng nếu thỏa mãn thì nó sẽ được cộng thêm điểm để leo lên Top 1.

  • Tương tác khi đứng một mình: Nếu trong khối bool KHÔNG CÓ must hay filter, thì Document bắt buộc phải thỏa mãn ít nhất 1 điều kiện trong should mới được trả về. (Có thể tinh chỉnh bằng tham số minimum_should_match).

D. must_not (TUYỆT ĐỐI KHÔNG - NOT)

  • Logic: Các điều kiện trong này tuyệt đối không được xuất hiện (Giống NOT trong SQL).

  • Điểm số: Nó được chạy trong Filter context, nghĩa là hoàn toàn không dính dáng đến tính toán điểm số (Scoring) và cũng được Cache cực nhanh.

2. Thực hành: Lắp ráp câu query E-commerce hoàn chỉnh

Bây giờ, hãy chuyển ngữ yêu cầu ở đầu bài toán thành một câu Query DSL chuẩn mực:

JSON

GET products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "sữa rửa mặt trị mụn" } }
      ],
      "filter": [
        { "range": { "price": { "lt": 300000 } } },
        { "terms": { "brand.raw": ["Cetaphil", "La Roche-Posay"] } }
      ],
      "should": [
        { "term": { "is_best_seller": true } } // Nếu là Best seller thì cộng thêm điểm
      ],
      "must_not": [
        { "term": { "tags": "sắp hết hạn" } }
      ]
    }
  }
}

Cách Elasticsearch thực thi câu query này:

  1. Bước 1 (Bộ lọc siêu tốc): Elasticsearch nhìn vào filter và must_not trước. Nó dùng cơ chế Inverted Index và Caching để loại bỏ hàng triệu Document sai hãng, sai giá, hoặc sắp hết hạn. Lúc này tập kết quả chỉ còn lại vài nghìn Document.

  2. Bước 2 (Chấm điểm): Nó lấy vài nghìn Document đó đưa qua khối must. Nó chạy thuật toán tính điểm TF/IDF để xem sản phẩm nào khớp chữ "sữa rửa mặt trị mụn" nhiều nhất.

  3. Bước 3 (Thưởng điểm): Nó nhìn vào khối should. Trong những sản phẩm đã lọt qua Bước 2, món nào có cờ is_best_seller = true sẽ được cộng thêm một lượng điểm kha khá.

  4. Bước 4 (Trả kết quả): Sắp xếp danh sách theo điểm số từ cao xuống thấp và trả về Top 10 cho API của bạn.

3. Cạm bẫy thiết kế: Lồng ghép bool vô hạn

Vì bản thân bool là một câu query, bạn hoàn toàn có thể nhét một khối bool khác vào bên trong must, should, hoặc filter của nó.

Ví dụ: "Lọc sản phẩm Cetaphil giá < 300k HOẶC La Roche-Posay giá < 400k".

JSON

"query": {
  "bool": {
    "should": [
      {
        "bool": {
          "filter": [
            { "term": { "brand.raw": "Cetaphil" } },
            { "range": { "price": { "lt": 300000 } } }
          ]
        }
      },
      {
        "bool": {
          "filter": [
            { "term": { "brand.raw": "La Roche-Posay" } },
            { "range": { "price": { "lt": 400000 } } }
          ]
        }
      }
    ]
  }
}

Việc lồng ghép (Nesting) này mang lại sức mạnh không giới hạn. Nhưng hãy cẩn thận! Với code backend (PHP/Golang), nếu bạn nối mảng không tốt, khối JSON sinh ra có thể sâu tới 10 tầng, làm cho Elasticsearch tốn quá nhiều CPU để "gỡ rối" (parse) logic và gây ra nghẽn cổ chai.

Tóm lại, bool query chính là "khung xương" để bạn xây dựng mọi tính năng tìm kiếm và lọc. Đã đến lúc chúng ta nhìn kỹ hơn vào cái gọi là Relevance Score (Điểm liên quan) mà nãy giờ chúng ta nhắc tới. Làm sao Elasticsearch biết sản phẩm A xứng đáng được 1.5 điểm, còn sản phẩm B chỉ được 0.8 điểm?


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí