Bỏ qua để đến nội dung
Search & RAG

7.9 Payload index

7.8 đã cho thấy payload index không chỉ là “index để lọc nhanh hơn” — nó là điều kiện tiên quyết để cả cơ chế filterable HNSW hoạt động. Mục này là bảng tra đầy đủ.

Một câu để mang theo:

Không có payload index thì filter vẫn chạy, chỉ là chạy sai chỗ đắt và làm hỏng recall. Đây là loại lỗi không báo lỗi.

LoạiDùng choĐiều kiện lọc hỗ trợ
keywordchuỗi định danh: tenant_id, phong_ban, ngon_ngumatch (value / any / except), prefix
integersố nguyênmatch, range
floatsố thựcrange
booltrue/falsematch
datetimemốc thời gian (RFC 3339)range
uuidUUIDmatch
geotoạ độgeo_bounding_box, geo_radius, geo_polygon
textfull-textmatch: {text / text_any / phrase}

Phân biệt quan trọng: keyword là so khớp cả giá trị, text là so khớp token bên trong giá trị. keyword cho "Thanh toán thất bại" chỉ khớp khi truy vấn đúng nguyên chuỗi đó; text khớp khi truy vấn chứa “thanh toán”.

Ba tham số có mặt ở gần như mọi loại

Phần tiêu đề “Ba tham số có mặt ở gần như mọi loại”
Tham sốMặc địnhÝ nghĩa
memorypinnedTầng bộ nhớ của index này (pinned / cached / cold)
enable_hnswtrueCó tạo cạnh phụ trong đồ thị HNSW cho trường này không
is_tenant / is_principalfalseTối ưu bố cục đĩa (xem dưới)

enable_hnsw đáng chú ý: đây chính là công tắc của cơ chế ở 7.8. Mặc định bật. Bạn tắt nó cho những trường mà bạn chỉ dùng để lọc trong scroll/count chứ không bao giờ dùng trong truy vấn vector — để khỏi tốn cạnh phụ vô ích.

memory: pinned mặc định nghĩa là trường đã index luôn nằm trong RAM, kể cả khi payload storage của bạn là cold. Đây là điều tốt (filter nhanh) nhưng cũng có nghĩa: mỗi payload index bạn tạo là RAM bị chiếm. Đừng index “cho chắc”.

is_tenantis_principal — sắp lại dữ liệu trên đĩa

Phần tiêu đề “is_tenant và is_principal — sắp lại dữ liệu trên đĩa”

Hai cờ này không làm filter đúng hơn. Chúng thay đổi bố cục vật lý: Qdrant gom các điểm cùng giá trị lại gần nhau trên đĩa, để một truy vấn lọc theo trường đó đọc ít trang đĩa hơn.

CờĐặt trên loạiDùng khi
is_tenant: truekeyword, uuidTrường phân tách khách hàng / không gian dữ liệu — mỗi truy vấn luôn kèm nó
is_principal: trueinteger, float, datetimeTrường là trục lọc chính, điển hình là dấu thời gian

Ví dụ dễ hình dung: nếu 100% truy vấn của bạn có tenant_id = X, thì việc các điểm của X nằm rải rác khắp file là lãng phí thuần tuý. is_tenant: true gom chúng lại. is_principal cũng vậy nhưng cho dữ liệu chuỗi thời gian: truy vấn “30 ngày gần nhất” chỉ cần chạm phần cuối file.

Chi tiết cách dùng cho hệ nhiều khách hàng: 7.17.

Đây là chỗ tài liệu tiếng Anh không giúp được bạn, nên nói kỹ.

Tham số của index text:

Tham sốMặc địnhGhi chú cho tiếng Việt
tokenizerwordword tách theo khoảng trắng và dấu câu — không tách từ ghép tiếng Việt
lowercasetrueGiữ nguyên
ascii_foldingfalseCân nhắc kỹ — xem dưới
min_token_len / max_token_lenĐặt min_token_len thấp, tiếng Việt nhiều từ 1 âm tiết
stemmertắtSnowball không có tiếng Việt. Bỏ qua
stopwordstắtKhông có danh sách tiếng Việt dựng sẵn
phrase_matchingfalseBật nếu cần khớp cụm chính xác

Ba điều rút ra, khớp với 3.103.15:

  1. Không có tách từ tiếng Việt. tokenizer: word coi “ngân hàng” là hai token độc lập. Với nhiều truy vấn thì vẫn dùng được, nhưng đừng kỳ vọng nó hiểu từ ghép.
  2. ascii_folding là con dao hai lưỡi. Bật thì “thanh toán” khớp với “thanh toan” — tốt cho người gõ không dấu. Nhưng nó cũng làm “má” ≡ “ma” ≡ “mà”, tức là bạn vứt bỏ thanh điệu. Với corpus có nhiều mã định danh và thuật ngữ, đây thường là đánh đổi có lợi; với văn bản đời thường thì cần đo.
  3. Nếu chất lượng lexical tiếng Việt là quan trọng, đừng dựa vào full-text index. Dùng nhánh sparse (7.3) với tokenizer tiếng Việt ở phía ngoài, rồi đưa sparse vector vào Qdrant. Full-text index của Qdrant hợp làm bộ lọc, không hợp làm bộ ranking chính cho tiếng Việt.

Chưa có số đo. Cẩm nang chưa đo full-text index của Qdrant trên data/corpus.jsonl. Ba nhận định trên suy ra từ tài liệu tham số của Qdrant cộng với các kết quả tokenizer tiếng Việt đã đo ở 3.15, không phải số đo trực tiếp trên Qdrant.

Đây là phần bạn nên chép ra dán lên tường:

1. Tạo collection
2. TẠO TẤT CẢ PAYLOAD INDEX ← trước, không phải sau
3. Nạp dữ liệu
4. Đợi optimizer xây xong HNSW
5. Đo (a)(b)(c)(d) như ở 7.8

Làm sai thứ tự — nạp dữ liệu rồi mới tạo index — thì filter vẫn hoạt động về mặt kết quả, nhưng cạnh phụ trong HNSW không tồn tại và bạn mất phần lớn lợi ích. Cách chữa: buộc dựng lại HNSW bằng cách tăng ef_construct lên 1.

  1. Index mọi trường “cho chắc”. Mỗi index tốn RAM (memory: pinned mặc định) và tốn cạnh phụ trong HNSW. Chỉ index trường thật sự xuất hiện trong filter.
  2. Dùng keyword cho trường muốn tìm theo token. keyword khớp cả chuỗi. Nếu bạn muốn “tìm tài liệu có chứa chữ hoàn tiền” thì đó là text.
  3. Quên datetime và dùng chuỗi. Lưu ngày dưới dạng keyword thì không lọc khoảng được. Dùng datetime với RFC 3339, và cân nhắc is_principal: true nếu ngày là trục lọc chính.
Phần 7 — Qdrant