Skip to content

PRD: Search indexing & query

ModuleNền tảng (CORE-16)PRD IDPRD-IDX-001
StatusShippedOwnerPhát Nguyễn
Date2026-06-15Versionv1.0
Năng lựcLập chỉ mục · truy vấn tìm kiếmURDIDX · SCH

TL;DR

Cho cả nền tảng một bề mặt tìm kiếm denormalize, luôn tươi mới mà không service nào phải ghi trực tiếp vào search engine. Mọi thay đổi cơ sở dữ liệu đã commit chảy ra thành một change-data event, và một consumer duy nhất phản chiếu nó vào đúng search collection - chín collection (organizers, merchants, categories, devices, sale-channels, products, product-variants, inventories, users) được nạp từ một danh mục các bản ghi nguồn change-data. Mỗi document được enrich với dữ liệu liên quan để một kết quả mang đủ những gì UI cần (tên merchant, giá variant, ảnh, mã quét, tồn theo vị trí, option facet), và một thay đổi ở một cha dùng chung (đổi tên merchant, đổi tên location, một mã dùng chung) sẽ lan tỏa tới mọi document phụ thuộc bằng patch có đích - không bao giờ re-index toàn bộ. Document được đánh phiên bản để sự kiện replay hoặc sai thứ tự không bao giờ làm sống lại trạng thái cũ, luồng suy giảm an toàn khi engine sập, và caller truy vấn bất kỳ collection nào qua một API tìm kiếm keyword + semantic hợp nhất.

1. Bối cảnh & Vấn đề

Dữ liệu của BANA nằm rải khắp nhiều service và schema cơ sở dữ liệu - commerce, pricing, inventory, identity. Một màn hình storefront hay back-office cần "tìm một sản phẩm theo tên, barcode, option, hoặc giá, giới hạn theo một merchant" không thể bung một query khắp tất cả các bảng đó lúc đọc, còn bắt mọi service tạo sự kiện cũng phải ghi vào một search engine sẽ làm logic index phân tán, nhân đôi mọi đường ghi, và lệch đồng bộ ngay lần đầu một service quên cập nhật.

Cái còn thiếu là một seam duy nhất biến luồng change-data sẵn có của nền tảng thành một bề mặt sẵn sàng truy vấn. Phần khó không phải lưu trữ: đó là giữ mỗi search document denormalize nhưng vẫn tươi mới (kết quả của một sản phẩm vẫn phải hiện đúng giá sau một lần sửa fare, đúng tên sau một lần đổi tên), sống sót qua replay và sự cố mà không hỏng trạng thái, và cung cấp một hợp đồng truy vấn nhất quán mọi app đều dùng được. Increment này cung cấp backbone đó.

2. Mục tiêu & Ngoài mục tiêu

Mục tiêu

  • Phản chiếu mọi ghi đã commit của một bản ghi nguồn được index vào search collection của nó, chỉ do luồng change-data điều khiển - không service tạo sự kiện nào ghi vào search (IDX).
  • Ánh xạ bản ghi nguồn tới chín collection, mỗi cái một loại bản ghi nguồn làm document cộng một tập input liên quan / dẫn xuất (IDX).
  • Enrich mỗi document với dữ liệu liên quan đã join trước khi index, để một kết quả tự chứa đủ thông tin (IDX).
  • Lan tỏa một thay đổi bản ghi dùng chung tới mọi document phụ thuộc bằng patch có đích, không re-index toàn bộ (IDX).
  • Đánh phiên bản mỗi document để chống sự kiện replay / sai thứ tự; đánh dấu delete và soft-delete bằng một dấu xóa (IDX).
  • Suy giảm an toàn - cắt mạch khi engine/dependency sự cố, cách ly poison message, cô lập lỗi theo từng document (IDX).
  • Một API truy vấn tìm kiếm keyword + semantic hợp nhất trên bất kỳ collection đã đăng ký nào, kèm count, scoping, và hợp đồng list chuẩn của nền tảng (SCH).

Ngoài mục tiêu

  • Sở hữu việc capture change-data - phát các change-data event là việc của hạ tầng change-data của nền tảng; search tiêu thụ các sự kiện đúng định dạng (URD-CON-008).
  • Index mọi loại bản ghi - đơn hàng bán là một nguồn change-data đã định nghĩa nhưng chưa được index (URD-CON-009).
  • Một màn hình re-index / backfill cho người dùng cuối - backfill là việc replay snapshot ở mức vận hành (URD-CON-010).
  • Logic ghi, giá, hay tính tồn của chính các service tạo sự kiện - chúng nằm ở Commerce, pricing, và Inventory.

3. Chỉ số thành công

Chỉ sốMục tiêu / tín hiệu
Độ tươi mớiMột ghi đã commit phản ánh vào collection của nó trong độ trễ bình thường của luồng, không cần bước thủ công
Đầy đủ khi denormalizeMột kết quả mang các trường liên quan (tên, giá, ảnh, mã, tồn, facet) mà không cần lookup lần hai
Đúng fan-outMột lần đổi tên cha / thay đổi mã dùng chung cập nhật mọi document phụ thuộc; không còn giá trị denormalize cũ nào sót lại
An toàn khi replayMột sự kiện replay hoặc sai thứ tự không bao giờ ghi đè trạng thái document mới hơn
Bền với sự cốMột sự cố engine tạm dừng rồi hồi phục luồng mà không mất dữ liệu; poison message vào hàng đợi cách ly, không vào đường live
Nhất quán truy vấnMọi app search bất kỳ collection nào qua một hợp đồng (dạng list chuẩn + header total-range), keyword hoặc semantic

4. Persona & Use Case

PersonaMục tiêu trong tính năng này
Cashier / StorefrontTìm một sản phẩm, variant, hay khách hàng tức thì theo tên, barcode, hay option facet
Owner / ManagerSearch merchants, categories, sale-channels, inventory, và users giới hạn theo cái họ quản lý
Tích hợp kênh / back-officeTruy vấn một collection theo filter + count theo một hợp đồng ổn định
Người vận hành nền tảngTin rằng luồng luôn tươi mới, sống sót qua sự cố, và cô lập message lỗi

Kịch bản cốt lõi: một chủ đổi tên một merchant. Thay đổi được bắt từ luồng change-data và index lên collection merchants; chính lần đổi tên đó lan tỏa tới products, categories, và sale-channels của merchant đó để mọi kết quả hiện tên mới - bằng patch có đích, không re-index. Vài khoảnh khắc sau, một cashier search products tìm một thức uống theo tên và nhận một kết quả đã mang sẵn giá, ảnh, option facet, và tồn theo vị trí của nó. Nếu search engine sập giữa luồng, consumer tạm dừng, thăm dò để hồi phục, và tiếp tục từ chỗ dừng - không mất sự kiện.

5. User Story

  • Là một storefront, tôi search một collection theo tên / barcode / option và nhận lại kết quả đã mang đủ thứ để render, để tôi không bao giờ phải gọi lần hai cho mỗi kết quả.
  • Là một chủ, tôi đổi tên một merchant một lần và mọi product, category, channel hiện tên đó đều cập nhật - tôi không bao giờ phải re-publish catalogue.
  • Là một tích hợp, tôi truy vấn và đếm một collection với một filter chuẩn, để search hành xử như mọi list endpoint khác.
  • Là một người vận hành, tôi tin rằng một sự kiện replay sẽ không làm sống lại dữ liệu đã xóa và một sự cố engine sẽ tạm dừng luồng thay vì rớt ghi.
  • Là một user back-office, search của tôi tự động giới hạn theo merchant của chính mình, để tôi không bao giờ thấy bản ghi mình không được phép.

6. Yêu cầu chức năng

#Yêu cầuURD ref
FR-1Mọi thay đổi đã commit của một bản ghi nguồn được index đều bắt từ luồng change-data và phản chiếu vào collection của nó - không service tạo sự kiện nào ghi vào searchURD-IDX-001
FR-2Bản ghi nguồn ánh xạ tới chín collection; mỗi collection có một loại bản ghi nguồn làm document, phần còn lại là liên quan / dẫn xuấtURD-IDX-002
FR-3Sự kiện create / update / snapshot upsert document; một delete hoặc soft-delete ghi một dấu xóa để nó rời khỏi kết quảURD-IDX-003
FR-4Mỗi document được enrich với dữ liệu liên quan đã join (tên chủ sở hữu, tập category, giá, ảnh, mã quét, tồn theo vị trí, option facet, định danh / vai trò / organizer của user) trước khi indexURD-IDX-004
FR-5Một thay đổi dùng chung / cha lan tỏa tới mọi document phụ thuộc bằng patch có đích, không re-index toàn bộURD-IDX-005
FR-6Mỗi document mang một dấu phiên bản; sự kiện replay / sai thứ tự không bao giờ ghi đè trạng thái mới hơn; patch con→cha chỉ động vào trường của riêng nóURD-IDX-006
FR-7Sự kiện xử lý theo lô từng luồng; một lô hỏng parse hoàn toàn hoặc ghi index lỗi được báo lỗi để retry, không bao giờ âm thầm bỏ quaURD-IDX-007
FR-8Sự cố engine / dependency kích hoạt một bộ cắt mạch an toàn để tạm dừng và thăm dò hồi phục; poison message chuyển sang hàng đợi cách lyURD-IDX-008
FR-9Một lần enrich lỗi vẫn index document với dữ liệu sẵn có trên nó; một cascade lỗi không bao giờ chặn các fan-out khác trong lôURD-IDX-009
FR-10Id bản ghi nhúng trong filter của search engine được kiểm tra để một id sai định dạng không bao giờ làm thay đổi tập đích của một fan-outURD-IDX-010
FR-11Full-text search bất kỳ collection đã đăng ký nào theo tên với một filter chuẩn (điều kiện / phân trang / sắp xếp / include liên quan / chọn trường), trả về theo dạng list chuẩn của nền tảng kèm header total-range, cộng một countURD-SCH-001..002
FR-12Search hỗ trợ khớp hybrid keyword + semantic (theo nghĩa) - endpoint chung mặc định là hybrid, search gắn theo tài nguyên chỉ keyword kèm opt-inURD-SCH-003 · URD-SCH-005
FR-13Một API tài nguyên có thể gắn một search + count có giới hạn, gộp một caller-scope (merchant của chính user) vào query; search và count đều được xác thực và bảo vệ bằng permissionURD-SCH-004 · URD-SCH-006..007

Toàn văn yêu cầu và tiêu chí chấp nhận nằm ở Platform URD - IDXSCH. PRD này tham chiếu chúng thay vì lặp lại.

7. Yêu cầu phi chức năng

Lĩnh vựcYêu cầu
Độ tươi mớiIndex theo luồng change-data; không service tạo sự kiện nào ghi vào search, nên chỉ có một đường ghi và không có lệch do double-write
IdempotencyDấu phiên bản (vị trí của document trong log thay đổi nguồn) làm cho việc re-delivery và replay trở nên an toàn - trạng thái mới hơn luôn thắng
Bền vữngSự cố engine / dependency tạm dừng luồng qua một bộ cắt mạch an toàn và thăm dò để hồi phục; poison message được cách ly; lô được retry khi lỗi
Cô lập lỗiEnrich lỗi → index ở trạng thái chưa enrich; một cascade lỗi → các cái khác vẫn áp; lỗi của một document không bao giờ làm hỏng cả lô
Hiệu năngFan-out dùng patch có đích, có filter với một giới hạn concurrency; một thay đổi cha động tới hàng nghìn con không bao giờ kích hoạt re-index toàn bộ
An toànId nhúng trong filter của search engine được kiểm tra; lỗi query phân loại rõ ràng (collection thiếu → rỗng, query xấu → một lỗi rõ ràng)
Tenancy & authzSearch gắn theo tài nguyên gộp một caller-scope vào query; search / count được xác thực và bảo vệ bằng permission
i18nTên đã denormalize lưu dạng object song ngữ (Anh + Việt) và search được ở cả hai ngôn ngữ

8. UX & Luồng

Một bản ghi nguồn làm document (ví dụ một product, một product variant, một bản ghi tồn kho, một user) tạo ra document collection của riêng nó; phần còn lại trong input của một collection - giá, option, ảnh, mã quét, profile, grant, bản ghi join - đều là cascade-only: thay đổi của chúng lan vào document đã tồn tại thay vì tạo một document của riêng mình.

9. Dữ liệu & Miền

Khái niệmVai trò
Search collectionMột trong chín index denormalize: organizers, merchants, categories, devices, sale-channels, products, product-variants, inventories, users
Bản ghi nguồn làm documentBản ghi change-data duy nhất mà các hàng của nó trở thành document của một collection (ví dụ một bản ghi tồn kho → inventories, một user → users)
Nguồn cascade-onlyMột bản ghi liên quan không có collection riêng; thay đổi của nó lan vào một document đã tồn tại (ví dụ một fare / bộ giá → giá variant, một product option → facet variant, một user identifier → liên hệ user)
EnrichmentBước join gộp dữ liệu liên quan của một document vào nó trước khi index
Cascade triggerMột tín hiệu có định kiểu báo rằng một thay đổi dùng chung / cha phải patch một tập document phụ thuộc
Dấu phiên bảnVị trí trong log thay đổi nguồn (và một dấu xóa) mang trên mỗi document để ghi mới nhất luôn thắng

Chỉ mang tính khái niệm - schema collection, tập mapper, và nội bộ pipeline nằm ở tài liệu developer search. Quan hệ giữa các thực thể là soft reference phân giải lúc enrich, không phải join cơ sở dữ liệu.

10. Phụ thuộc & Giả định

Phụ thuộc vào

  • Hạ tầng change-data - phát các luồng change-data theo từng bản ghi mà consumer đăng ký (URD-CON-008).
  • Search engine - kho index và engine truy vấn đứng sau mỗi collection.
  • Lõi nền tảng - danh mục bản ghi nguồn change-data, registry luồng, và các model dùng chung mà loader enrich đọc.
  • Dữ liệu Commerce / pricing / inventory / identity - các hàng nguồn và dữ liệu liên quan mà mỗi document được enrich và lan tỏa từ đó.

Giả định

  • Sự kiện change-data đúng định dạng và đủ trật tự để dấu phiên bản phân giải phần còn lại.
  • Các collection được provision trong engine trước khi luồng chạy; một collection chưa được tạo trả về rỗng thay vì lỗi.
  • Service tạo sự kiện phát các ghi nghiệp vụ của chúng bình thường; chúng không biết và không quan tâm rằng search tiêu thụ chúng.

11. Rủi ro & Câu hỏi mở

Rủi ro / câu hỏiGiảm thiểu / trạng thái
Một sự kiện replay hoặc sai thứ tự làm sống lại trạng thái cũ / đã xóaMỗi document được đánh phiên bản bằng vị trí của nó trong log thay đổi nguồn; trạng thái mới hơn luôn thắng; patch con→cha không bao giờ lật lifecycle
Một lần đổi tên cha động tới hàng nghìn con gây bão re-indexFan-out dùng patch có đích, có filter với một giới hạn concurrency - không bao giờ re-index toàn bộ
Sự cố search engine làm tắc hoặc mất luồngBộ cắt mạch an toàn tạm dừng và thăm dò để hồi phục; lô được báo lỗi và retry; không đẩy tiến độ khi lỗi
Một message lỗi hoặc một lần enrich lỗi chặn cả lôPoison message được cách ly; enrich lỗi vẫn index ở trạng thái chưa enrich; cô lập theo từng cascade để khoanh lỗi
Một id sai định dạng làm thay đổi filter đích của một fan-outId nhúng trong filter của search engine được kiểm tra trước khi dùng
Đơn hàng bán là một nguồn change-data nhưng chưa được indexGhi nhận là ngoài mục tiêu một cách có chủ đích cho increment này (URD-CON-009)

12. Kế hoạch phát hành & Tiêu chí ra mắt

Khía cạnhKế hoạch
PhaseP2 - IDXSCH trong URD feature catalog
RolloutMọi merchant; backbone chạy toàn nền tảng, không flag theo từng merchant
MigrationKhông ở tầng dữ liệu - collection được provision và backfill bằng replay snapshot
Toggle vận hànhBộ cắt mạch an toàn bật bằng cấu hình môi trường; hàng đợi cách ly cấu hình được
Tiêu chí ra mắtMột ghi đã commit đến collection của nó kèm enrich; một lần đổi tên cha lan tỏa tới các phụ thuộc; một replay không ghi đè trạng thái mới hơn; một sự cố engine tạm dừng rồi hồi phục mà không mất gì; bất kỳ collection nào cũng search + count được qua API hợp nhất
Giám sátThống kê theo từng lô (creates / updates / deletes / snapshots / parse errors / engine ok / fail / throughput), số cascade ok/failed, số lần trip và escalate của bộ cắt mạch, lượng message cách ly

13. FAQ

Mỗi service có ghi vào search engine không? Không - không service tạo sự kiện nào động vào search. Mọi ghi index đều do luồng change-data điều khiển, nên chỉ có một đường ghi và không có lệch do double-write.

Làm sao một kết quả hiện đúng giá hoặc tên sau một lần sửa? Document được enrich với dữ liệu liên quan lúc index, và một thay đổi ở một cha dùng chung (một fare, một lần đổi tên, một mã dùng chung) sẽ lan tỏa tới mọi document phụ thuộc bằng patch có đích - nên giá trị denormalize được làm tươi chứ không bị bỏ cũ.

Một bản ghi đã xóa thì sao? Một delete hoặc soft-delete ghi một dấu xóa, nên bản ghi rời khỏi kết quả trong khi thứ tự phiên bản của nó vẫn được giữ.

Một sự kiện replay có làm hỏng index không? Không - mỗi document mang một dấu phiên bản (vị trí trong log thay đổi nguồn). Một sự kiện replay hoặc sai thứ tự cũ hơn trạng thái hiện tại sẽ bị bỏ qua.

Khi search engine sập thì sao? Một bộ cắt mạch an toàn tạm dừng luồng và thăm dò để hồi phục; lô được báo lỗi và retry, nên không mất gì. Poison message chuyển sang một hàng đợi cách ly thay vì chặn đường live.

App truy vấn nó thế nào? Qua một hợp đồng: full-text search bất kỳ collection đã đăng ký nào theo tên với một filter chuẩn, cộng một count - keyword theo mặc định, với khớp hybrid semantic có sẵn. Search gắn theo tài nguyên tự động giới hạn kết quả theo merchant của caller.

Tham chiếu

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.