Vấn đề với tìm kiếm hiện đại
Xây dựng một thanh tìm kiếm nghe có vẻ dễ dàng cho đến khi bạn thực sự bắt tay vào làm. Hầu hết các lập trình viên bắt đầu với một câu lệnh LIKE %query% đơn giản trong SQL. Tôi đã từng làm điều này trong MySQL, PostgreSQL và MongoDB, và nó hoạt động tốt—cho đến khi bảng dữ liệu của bạn chạm mốc 100.000 hàng. Tại thời điểm đó, hiệu suất sẽ tụt dốc không phanh. Các cơ sở dữ liệu này không được xây dựng để phục vụ full-text search; chúng thiếu khả năng chịu lỗi chính tả, gặp khó khăn với việc xếp hạng phức tạp và chạy chậm chạp khi tìm kiếm trên các khối văn bản lớn.
Trong nhiều năm, Elasticsearch đã là tiêu chuẩn của ngành. Nó cực kỳ mạnh mẽ, nhưng cũng nổi tiếng là một “kẻ ngốn RAM”. Hãy thử chạy Elasticsearch trên một VPS nhỏ với 2GB RAM và nó có khả năng sẽ bị treo trước khi quá trình khởi động hoàn tất. Nó đòi hỏi cấu hình nặng nề và sự hiểu biết sâu sắc về Java Virtual Machine (JVM) chỉ để duy trì sự ổn định.
Typesense thay đổi điều đó. Được viết bằng C++, công cụ mã nguồn mở này được xây dựng vì tốc độ tối đa và sự đơn giản cho lập trình viên. Hãy coi nó như một phiên bản “Lite” của Elasticsearch nhưng không đánh đổi về hiệu suất. Nó được thiết kế đặc biệt để mang lại trải nghiệm “tìm kiếm ngay khi nhập” (search-as-you-type) mà bạn thường thấy trên Amazon hoặc Algolia, nhưng không có sự cồng kềnh ở mức doanh nghiệp.
Tại sao nên chọn Typesense?
Trước khi đi sâu vào mã nguồn, hãy xem tại sao công cụ này lại đang thu hút sự chú ý. Không giống như các cơ sở dữ liệu truyền thống lấy dữ liệu từ đĩa cứng, Typesense lưu trữ toàn bộ chỉ mục tìm kiếm trong RAM. Kiến trúc này cho phép thời gian phản hồi dưới 50ms. Tuy nhiên, đừng lo lắng về việc mất dữ liệu; nó vẫn lưu mọi thứ xuống đĩa để dữ liệu của bạn được an toàn nếu máy chủ khởi động lại.
Dưới đây là những điểm khiến nó trở thành lựa chọn nổi bật cho các ứng dụng hiện đại:
- Chịu lỗi chính tả tức thì: Nó xử lý các lỗi nhập liệu của người dùng một cách mượt mà. Nếu người dùng nhập “ipone”, Typesense sẽ ngay lập tức tìm thấy “iPhone” mà không cần cấu hình thêm.
- RESTful API sạch sẽ: Bạn sẽ không tìm thấy bất kỳ Query DSL phức tạp nào ở đây. Nếu bạn có thể sử dụng JSON API, bạn có thể sử dụng Typesense.
- Dấu chân tài nguyên thấp: Trong khi Elasticsearch có thể cần 4GB RAM để “thở”, Typesense có thể xử lý lưu lượng truy cập đáng kể một cách thoải mái trên một máy chỉ với 512MB RAM.
- Sẵn sàng cao (High Availability) tích hợp sẵn: Việc mở rộng quy mô trở nên đơn giản với hỗ trợ phân cụm (clustering) bản địa.
Thực hành: Thiết lập với Docker
Docker là cách tốt nhất để bắt đầu. Nó giữ cho môi trường cục bộ của bạn sạch sẽ và đảm bảo thiết lập trên production hoạt động chính xác như trên máy tính cá nhân. Chúng ta sẽ xây dựng một công cụ tìm kiếm nhỏ cho một cửa hàng sách để trình diễn cách thức hoạt động.
1. Tạo tệp Docker Compose
Bắt đầu bằng cách tạo một thư mục dự án và thêm tệp docker-compose.yml. Tệp này định nghĩa container cho công cụ tìm kiếm của chúng ta.
version: '3.8'
services:
typesense:
image: typesense/typesense:0.25.1
container_name: typesense
restart: on-failure
ports:
- "8108:8108"
volumes:
- ./typesense-data:/data
command: '--data-dir /data --api-key=secret_key_123 --enable-cors'
Thiết lập này ánh xạ cổng 8108 và tạo một volume bền vững. Dữ liệu của bạn vẫn an toàn ngay cả khi bạn xóa container. --api-key là lớp bảo vệ của bạn; hãy giữ nó an toàn trong môi trường production thực tế.
2. Khởi động công cụ
Chạy lệnh sau trong terminal của bạn:
docker-compose up -d
Xác minh trạng thái bằng cách truy cập http://localhost:8108/health. Một phản hồi {"ok":true} đơn giản có nghĩa là bạn đã sẵn sàng.
3. Định nghĩa Schema
Typesense là một hệ thống “nhận biết schema”. Nó có thể tự đoán kiểu dữ liệu, nhưng việc định nghĩa chúng một cách rõ ràng sẽ giúp ngăn ngừa lỗi. Chúng ta sẽ sử dụng client Python ở đây, nhưng logic tương tự cho JavaScript, Go hoặc PHP.
import typesense
client = typesense.Client({
'nodes': [{
'host': 'localhost',
'port': '8108',
'protocol': 'http'
}],
'api_key': 'secret_key_123',
'connection_timeout_seconds': 2
})
books_schema = {
'name': 'books',
'fields': [
{'name': 'title', 'type': 'string' },
{'name': 'author', 'type': 'string' },
{'name': 'publication_year', 'type': 'int32', 'facet': True },
{'name': 'rating', 'type': 'float' }
],
'default_sorting_field': 'rating'
}
client.collections.create(books_schema)
4. Đánh chỉ mục dữ liệu
Bây giờ chúng ta cần một số nội dung. Mặc dù bạn có thể đánh chỉ mục từng tài liệu một, nhưng việc xử lý theo lô (batching) sẽ nhanh hơn nhiều đối với các tập dữ liệu lớn.
book_data = [
{'title': 'The Great Gatsby', 'author': 'F. Scott Fitzgerald', 'publication_year': 1925, 'rating': 4.4},
{'title': 'The Hobbit', 'author': 'J.R.R. Tolkien', 'publication_year': 1937, 'rating': 4.8},
{'title': '1984', 'author': 'George Orwell', 'publication_year': 1949, 'rating': 4.7}
]
for book in book_data:
client.collections['books'].documents.create(book)
5. Kiểm tra tìm kiếm
Tốc độ là điều làm cho việc này trở nên thú vị. Bạn có thể kích hoạt tìm kiếm trên mỗi phím nhấn mà không làm lag giao diện người dùng.
search_parameters = {
'q' : 'hobit',
'query_by' : 'title,author',
'sort_by' : 'rating:desc'
}
result = client.collections['books'].documents.search(search_parameters)
print(result)
Ngay cả với lỗi chính tả “hobit”, Typesense vẫn trả về “The Hobbit” ngay lập tức. Phản hồi bao gồm các đoạn trích (snippets) được làm nổi bật, cho phép bạn hiển thị cho người dùng biết chính xác lý do tại sao một kết quả phù hợp với truy vấn của họ.
Đi xa hơn nữa
Khi các bước cơ bản đã hoạt động, hãy khám phá “Faceting”. Điều này cho phép bạn tạo các bộ lọc thanh bên như “Lọc theo năm” hoặc “Lọc theo tác giả” thường thấy trên các trang thương mại điện tử. Vì chúng ta đã đặt 'facet': True trong schema, Typesense sẽ tự động đếm xem có bao nhiêu cuốn sách tồn tại cho mỗi năm trong kết quả của bạn.
Bảo mật là bước tiếp theo. Đừng bao giờ sử dụng API key chính trong mã frontend của bạn. Thay vào đó, hãy sử dụng Typesense để tạo “Scoped Search Keys”. Đây là các khóa bị giới hạn chỉ có thể thực hiện tìm kiếm và thậm chí có thể bị giới hạn trong các tài liệu cụ thể dựa trên ID người dùng.
Kết luận
Chuyển từ các câu lệnh SQL cơ bản sang một công cụ chuyên dụng như Typesense là một sự nâng cấp đáng kể. Nó cung cấp trải nghiệm người dùng cao cấp trong khi vẫn giữ cho chi phí hạ tầng của bạn ở mức thấp. Nếu Elasticsearch mang lại cảm giác quá dư thừa cho dự án tiếp theo của bạn, Typesense là điểm dừng chân hoàn hảo. Nó nhanh, thân thiện với Docker và mất chưa đầy mười phút để vận hành. Hãy thử và chứng kiến độ trễ tìm kiếm của bạn giảm xuống rõ rệt.

