DEV Community

Hoàng Mạnh Khiêm
Hoàng Mạnh Khiêm

Posted on

Meilisearch

1. Giới thiệu

Meilisearch là một search engine mã nguồn mở, tốc độ cao, tập trung vào trải nghiệm developer. Được viết bằng Rust, thiết kế theo hướng search-as-you-type (tìm kiếm ngay khi gõ).

2. Điểm nổi bật:

Tính năng Mô tả
Siêu nhanh Phản hồi dưới 50ms ngay cả khi xử lý lượng dữ liệu lớn
Typo Tolerance Tự động xử lý lỗi chính tả và từ khóa gần đúng
Full-text Search Hỗ trợ tìm kiếm toàn văn bản với độ chính xác cao
Faceted Search Hỗ trợ lọc dữ liệu theo danh mục, thuộc tính (facet)
RESTful API Dễ dàng tích hợp thông qua giao tiếp HTTP API
Self-hosted Triển khai trên server riêng, không phụ thuộc dịch vụ bên thứ ba
Multi-language Hỗ trợ đa ngôn ngữ, bao gồm cả tiếng Việt

3. Header bắt buộc

Content-Type: application/json
Authorization: Bearer <token>

4. Các loại Search

4.1. Single Index Search — Tìm kiếm trong một index

Tìm kiếm thông thường trong một index cụ thể.

POST /indexes/{indexUid}/search

4.2. Multi-Search — Tìm kiếm nhiều index cùng lúc

Cho phép gửi nhiều query trong một request duy nhất, giảm số lần gọi API.

POST /multi-search
JSON
{
"queries": [
{ "indexUid": "movies", "q": "action" },
{ "indexUid": "books", "q": "thriller" }
]
}

4.3. Federated Search — Tìm kiếm liên hợp

Tìm kiếm trên nhiều index và gộp kết quả thành một danh sách duy nhất, có ranking đồng nhất.

POST /multi-search
JSON
{
"federation": {},
"queries": [
{ "indexUid": "movies", "q": "batman" },
{ "indexUid": "comics", "q": "batman" }
]
}

4.4. Search with Filters — Tìm kiếm kết hợp lọc

Dùng thuộc tính filter để thu hẹp kết quả tìm kiếm theo điều kiện.

4.5. Faceted Search — Tìm kiếm phân loại

Dùng facets để lấy thống kê số lượng theo từng nhóm giá trị (ví dụ: bao nhiêu phim thuộc thể loại "Action").

4.6. Search with Geo (Geosearch) — Tìm kiếm theo vị trí địa lý

Tìm kiếm và sort theo tọa độ GPS.

JSON
{
"q": "restaurant",
"sort": ["_geoPoint(48.8566, 2.3522):asc"]
}

5. Các tham số trong Search API

Endpoint: GET\POST /indexes/{indexUid}/search

5.1. Tham số truy vấn cơ bản

Tham số Kiểu Default Mô tả
q string Chuỗi tìm kiếm. Để trống → placeholder search (trả về tất cả documents). Dùng "..." cho phrase search. Dùng -word để loại trừ từ khóa. Chỉ xét tối đa 10 từ đầu tiên
limit integer 20 Số kết quả tối đa trả về. Không được vượt quá maxTotalHits. Bị bỏ qua khi sử dụng page / hitsPerPage
offset integer 0 Bỏ qua N kết quả đầu tiên (phân trang thủ công). Bị bỏ qua khi sử dụng page / hitsPerPage
page integer Số trang hiện tại (bắt đầu từ 1). Dùng kèm với hitsPerPage. Khi thiết lập → response trả về totalHitstotalPages thay vì estimatedTotalHits
hitsPerPage integer 20 Số kết quả trên mỗi trang. Đặt 0 để chỉ lấy totalHits chính xác mà không trả về documents

5.2. Tham số lọc & sắp xếp

Tham số Kiểu Default Mô tả
filter string Biểu thức lọc kết quả. Tất cả attributes dùng trong filter bắt buộc phải có trong filterableAttributes. Hỗ trợ geo filter: _geoRadius(), _geoBoundingBox(), _geoPolygon()
sort string[] Sắp xếp kết quả. Format: ["attribute:asc", "attribute:desc"]. Attribute phải có trong sortableAttributes. Hỗ trợ geo sort: _geoPoint(lat,lng):asc
facets string[] Trả về số lượng match theo từng giá trị facet. Dùng ["*"] để lấy tất cả filterableAttributes. Response sẽ có thêm facetDistributionfacetStats
distinct string Chỉ trả về 1 document đại diện cho mỗi giá trị unique của field này. Attribute phải có trong filterableAttributes. Override distinctAttribute của index
matchingStrategy enum last Chiến lược match khi thiếu kết quả: last (bỏ bớt từ từ cuối), all (chỉ lấy kết quả khớp hoàn toàn), frequency (bỏ bớt từ phổ biến nhất trước)
attributesToSearchOn string[] Giới hạn tìm kiếm chỉ trong các attributes được liệt kê. Mỗi attribute phải tồn tại trong searchableAttributes. Thứ tự không ảnh hưởng đến relevancy
rankingScoreThreshold float Loại bỏ các kết quả có điểm ranking thấp hơn ngưỡng này (0.0 – 1.0). Các hits bị loại sẽ không được tính vào estimatedTotalHits, totalHits, hoặc facet
locales enum[] Chỉ định ngôn ngữ của query theo chuẩn ISO-639. Override auto-detection. Ví dụ: ["vi", "en"]

Ví dụ filter nâng cao:

{
"filter": "genre = 'Action' AND rating > 7 AND NOT country = 'US'",
"sort": ["release_date:desc"]
}

5.3. Tham số hiển thị kết quả

Tham số Kiểu Default Mô tả
attributesToRetrieve string[] ["*"] Danh sách fields trả về trong mỗi document. Dùng ["*"] để lấy tất cả. Attributes không có trong displayedAttributes sẽ bị bỏ qua
attributesToHighlight string[] Bôi đậm từ khóa match trong các attributes chỉ định. Kết quả xuất hiện trong _formatted. Dùng ["*"] để highlight tất cả fields trong attributesToRetrieve. Hỗ trợ cả synonyms và stop words
highlightPreTag string "<em>" Chuỗi chèn trước từ được highlight. Có thể là HTML tag hoặc ký tự bất kỳ
highlightPostTag string "</em>" Chuỗi chèn sau từ được highlight. Dùng cùng với highlightPreTag để tránh tạo HTML không hợp lệ
attributesToCrop string[] Cắt ngắn nội dung và trả về đoạn trích (snippet) trong _formatted. Hỗ trợ syntax attribute:length để override theo từng attribute. Đoạn cắt được căn giữa quanh từ khóa match
cropLength integer 10 Số từ tối đa trong đoạn trích. Cả query terms và stop words đều được tính vào độ dài này
cropMarker string "…" Ký hiệu đánh dấu vị trí bị cắt. Đặt null hoặc chuỗi rỗng nếu không muốn hiển thị marker. Chỉ xuất hiện khi có nội dung thực sự bị cắt
showMatchesPosition boolean false Khi đặt true, mỗi hit sẽ có thêm _matchesPosition chứa byte offset (start + length) của từng term match. Hữu ích cho custom highlighting. Đơn vị là bytes, không phải characters

5.4. Tham số nâng cao

Tham số Kiểu Default Mô tả
showRankingScore boolean false Khi đặt true, mỗi document sẽ có thêm field _rankingScore (0.0 – 1.0). Điểm càng cao → mức độ liên quan càng lớn. Rule sort không ảnh hưởng đến giá trị này
showRankingScoreDetails boolean false Khi đặt true, mỗi document sẽ có thêm _rankingScoreDetails — breakdown điểm theo từng ranking rule. Hữu ích để debug relevancy
showPerformanceDetails boolean false Khi đặt true, response sẽ có thêm object performanceDetails chứa timing breakdown của từng bước xử lý query

Ví dụ request đầy đủ:

{
"q": "avengers",
"limit": 10,
"offset": 0,
"filter": "genre = 'Action' AND rating >= 7",
"sort": ["release_date:desc"],
"facets": ["genre", "director"],
"attributesToRetrieve": ["title", "poster", "rating", "genre"],
"attributesToHighlight": ["title", "description"],
"highlightPreTag": "<mark>",
"highlightPostTag": "</mark>",
"attributesToCrop": ["description"],
"cropLength": 20,
"showRankingScore": true,
"matchingStrategy": "all"
}

6. Cấu hình Settings (Index Settings)

Endpoint: GET/PATCH/DELETE /indexes/{indexUid}/settings

6.1. Searchable Attributes

Xác định trường nào được đưa vào full-text search index. Thứ tự trong array = mức độ ưu tiên (trường đầu tiên được ưu tiên cao nhất).

PUT /indexes/{indexUid}/settings/searchable-attributes
["title", "description", "tags"]

6.2. Displayed Attributes: Các trường được trả về trong kết quả search.

PUT /indexes/{indexUid}/settings/displayed-attributes
["title", "poster_url", "rating", "genre"]

6.3. Filterable Attributes: Khai báo các trường có thể dùng trong filter và facets. Bắt buộc phải khai báo trước khi dùng filter.

PUT /indexes/{indexUid}/settings/filterable-attributes
JSON
["genre", "rating", "country", "release_year"]

6.4. Sortable Attributes: Khai báo các trường có thể dùng để sort. Bắt buộc phải khai báo trước khi dùng sort.

PUT /indexes/{indexUid}/settings/sortable-attributes
JSON
["rating", "release_date", "price"]

6.5. Typo Tolerance: Kiểm soát mức độ chấp nhận lỗi chính tả.

PATCH /indexes/{indexUid}/settings/typo-tolerance
JSON
{
"enabled": true,
"minWordSizeForTypos": {
"oneTypo": 5,
"twoTypos": 9
},
"disableOnWords": ["iphone", "batman"],
"disableOnAttributes": ["serial_number"]
}

6.6. Synonyms: Cấu hình từ đồng nghĩa để mở rộng khả năng tìm kiếm.

PUT /indexes/{indexUid}/settings/synonyms
JSON
{
"phone": ["smartphone", "mobile", "điện thoại"],
"car": ["automobile", "vehicle", "xe hơi"],
"ai": ["artificial intelligence", "machine learning"]
}

6.7. Stop Words: Loại bỏ các từ không có giá trị tìm kiếm khỏi query.

PUT /indexes/{indexUid}/settings/stop-words
JSON
["the", "a", "an", "is", "are", "và", "của", "cho"]

6.8. Distinct Attribute: Chỉ lấy 1 kết quả đại diện cho mỗi giá trị unique.

PUT /indexes/{indexUid}/settings/distinct-attribute
JSON
"product_id"

6.9. Faceting: Cấu hình tính năng faceted search.

PATCH /indexes/{indexUid}/settings/faceting
JSON
{
"maxValuesPerFacet": 100,
"sortFacetValuesBy": {
"*": "alpha",
"rating": "count"
}
}

6.10. Pagination: Cấu hình giới hạn phân trang.

PATCH /indexes/{indexUid}/settings/pagination
JSON
{
"maxTotalHits": 1000
}

6.11. Embedders (Semantic Search): Cấu hình vector embedding cho AI/semantic search.

PATCH /indexes/{indexUid}/settings/embedders
JSON
{
"myEmbedder": {
"source": "openAi",
"apiKey": "sk-...",
"model": "text-embedding-3-small",
"documentTemplate": "{{doc.title}} {{doc.description}}"
}
}

7. Ranking Rules

Ranking Rules xác định thứ tự ưu tiên khi tính điểm liên quan của kết quả. Rule đứng trước có độ ưu tiên cao hơn.

Endpoint: GET/PUT /indexes/{indexUid}/settings/ranking-rules

Rule Mô tả
words Ưu tiên document chứa nhiều từ trong query hơn
typo Ưu tiên document có ít lỗi chính tả hơn
proximity Ưu tiên document có các từ xuất hiện gần nhau hơn trong nội dung
attribute Ưu tiên match ở field có độ ưu tiên cao hơn (theo thứ tự trong searchableAttributes)
sort Áp dụng tiêu chí sắp xếp do người dùng truyền vào
exactness Ưu tiên document khớp chính xác với từ khóa hơn

Mặc định:

["words", "typo", "proximity", "attribute", "sort", "exactness"]

Custom ranking rules: Có thể thêm rule tùy chỉnh theo trường cụ thể:

["words", "typo", "proximity", "attribute", "sort", "exactness", "rating:desc", "release_date:asc"]
_
Tips: Đặt sort lên trước nếu muốn ưu tiên tiêu chí sắp xếp của người dùng hơn là độ liên quan ngữ nghĩa. Đặt exactness lên trước nếu cần match chính xác cao._

8. Các API xóa dữ liệu

8.1. Xóa toàn bộ Index

DELETE /indexes/{indexUid} : Xóa cả cấu hình lẫn dữ liệu của index.

8.2. Xóa tất cả Documents trong Index

DELETE /indexes/{indexUid}/documents

8.3. Xóa Document theo ID

DELETE /indexes/{indexUid}/documents/{documentId}

8.4. Xóa nhiều Documents theo danh sách ID

POST /indexes/{indexUid}/documents/delete-batch
[1, 2, 3, 42, 100]

8.5. Xóa Documents theo Filter

POST /indexes/{indexUid}/documents/delete
{
"filter": "genre = 'Horror' AND release_year < 2000"
}

8.6. Reset Settings về mặc định

DELETE /indexes/{indexUid}/settings: Reset từng phần setting cụ thể:

DELETE /indexes/{indexUid}/settings/synonyms
DELETE /indexes/{indexUid}/settings/stop-words
DELETE /indexes/{indexUid}/settings/ranking-rules
DELETE /indexes/{indexUid}/settings/filterable-attributes
DELETE /indexes/{indexUid}/settings/sortable-attributes
DELETE /indexes/{indexUid}/settings/searchable-attributes
DELETE /indexes/{indexUid}/settings/typo-tolerance
DELETE /indexes/{indexUid}/settings/faceting
DELETE /indexes/{indexUid}/settings/pagination
DELETE /indexes/{indexUid}/settings/embedders

8.7. Hủy Task đang chờ xử lý

Cho phép hủy các task chưa hoàn thành (enqueued, processing) theo nhiều điều kiện khác nhau.

POST /tasks/cancel?uids=1,2,3
POST /tasks/cancel?indexUids=movies
POST /tasks/cancel?statuses=enqueued,processing

Tham số Kiểu Mô tả
uids string Danh sách task UID cần hủy, phân tách bằng dấu phẩy
indexUids string Hủy tất cả task thuộc các index được chỉ định
statuses string Hủy task theo trạng thái (enqueued, processing)

Response

{
"taskUid": 42,
"indexUid": null,
"status": "enqueued",
"type": "taskCancelation",
"enqueuedAt": "2026-05-21T10:00:00Z"
}

8.8. Xóa lịch sử Task

Cho phép xóa lịch sử task đã hoàn thành hoặc thất bại khỏi hệ thống.

DELETE /tasks?uids=1,2,3
DELETE /tasks?indexUids=movies
DELETE /tasks?statuses=succeeded,failed

Query Parameters

Tham số Kiểu Mô tả
uids string Danh sách task UID cần xóa
indexUids string Xóa tất cả task thuộc các index được chỉ định
statuses string Xóa task theo trạng thái (succeeded, failed)

Response trả về thông tin task deletion đã được enqueue:

{
"taskUid": 43,
"indexUid": null,
"status": "enqueued",
"type": "taskDeletion",
"enqueuedAt": "2026-05-21T10:05:00Z"
}

Top comments (0)