Không phải bug hiển thị dữ liệu sai. Đây là hệ quả của cách Convex full-text search tách từ (tokenize) và xếp hạng theo số từ khớp, không yêu cầu khớp toàn bộ cụm tìm kiếm.
Chuỗi displayId dạng S-135 bị tách thành hai token s + 135 vì dấu gạch ngang là ký tự phân tách. Chuỗi query Noo's Question cũng bị tách thành noo + s + question vì dấu nháy đơn cũng là ký tự phân tách. Hai bên trùng nhau ở token rác s — nên gần như mọi survey/poll có ID dạng S-xxx đều "khớp" một phần, dù nội dung không liên quan.
Quyết định: giữ nguyên cơ chế .withSearchIndex() hiện tại — không can thiệp code. Xem lý do ở mục 04.
Các điểm dưới đây lấy trực tiếp từ tài liệu chính thức của Convex, vì đây là phần dễ hiểu nhầm nhất và là gốc rễ của vấn đề.
-, ', v.v), sau đó lowercase. Mỗi token tối đa 32 ký tự..eq()) — filter là so khớp tuyệt đối, áp dụng sau khi search index đã trả kết quả.Field searchContent trên bảng surveys được ghép sẵn từ displayId + title + module title/code trong SurveySearchContentHelper.buildSurveySearchContent surveySearchContent.helper.ts:13, rồi được index bằng .searchIndex('search_surveys', { searchField: 'searchContent' }). Truy vấn thực hiện ở survey.repo.ts:167-172 qua .withSearchIndex(...).take(200) trước khi cắt theo trang.
Sơ đồ dưới minh hoạ đúng ví dụ trong screenshot: query Noo's Question so với hai document thật trong kết quả trả về.
Noo's và token s tách ra từ S-135 là cùng một token theo tokenizer. Vì matching không yêu cầu khớp hết, document thứ ba vẫn lọt vào kết quả — chỉ xếp hạng thấp hơn.
| ID | Tên | Token khớp | Vì sao xuất hiện |
|---|---|---|---|
| S-140 | Noo's question | noo, s, question | Khớp đúng |
| S-146 | Noo's question 1 | noo, s, question | Khớp đúng |
| S-145 | Noo | noo, s | Khớp đúng (tên) |
| S-147 | Noo | noo, s | Khớp đúng (tên) |
| S-135 | Survey Poll S-135 | s | Chỉ trùng "s" từ ID |
| S-134 | Survey Poll 134 | s | Chỉ trùng "s" từ ID |
| S-133 | Survey Poll 133 | s | Chỉ trùng "s" từ ID |
| S-132 | Survey Poll Double Check | s | Chỉ trùng "s" từ ID |
Mọi cách can thiệp đều đụng vào chính cơ chế đang giữ cho tab Surveys/Polls nhanh, và đều đổi một rủi ro vận hành lấy một cải thiện nhỏ về độ chính xác:
- trong displayId khi build lại field index sẽ vá đúng case S-xxx, nhưng là thay đổi dữ liệu đã lưu — cần backfill toàn bộ record cũ, và vẫn còn khả năng va chạm token khác trong tương lai (không triệt để)..withSearchIndex() trả kết quả vẫn phải chạy trên tập đã lấy về (take(200)) ở mọi lần search — thêm một bước xử lý trên request path, tức vẫn ảnh hưởng hiệu năng, chỉ là ở mức nhỏ hơn full scan.Nói cách khác: đây là đặc thù kỹ thuật (trade-off) vốn có của withSearchIndex — Convex đánh đổi độ chính xác tuyệt đối lấy tốc độ, bằng cách xếp hạng theo số từ khớp thay vì lọc AND/substring đầy đủ. Giữ nguyên hành vi này là lựa chọn nhất quán với lý do ban đầu app dùng search index thay vì quét bảng.