Tìm kiếm fulltext trong redisearch

Tìm kiếm fulltext trong redisearch

Threads
  • Hôm trước mình thấy repository này dùng TauriSvelte để viết lại ứng dụng kiểu như là Task Manager trên Window hay Monitor trên Mac á. Tò mò tải về xem thử thì bất ngờ thứ nhất là dung lượng rất nhỏ, chỉ vài MB. Tiếp theo là tốc độ khởi động cũng rất nhanh mà ứng dụng cũng rất mượt nữa chứ 🫣

    Abdenasser/neohtop

    » Xem thêm
  • Tuôi" để ý là cứ đợt nào ham đọc cái là lại lười viết, tuần nay tuôi đang đọc một lúc 3 cuốn, à phải là đọc 2 và nghe 1.

    Cuốn sách ám ảnh nhất đến thời điểm hiện tại: Đại dương đen - thuật lại 12 câu chuyện của 12 người mắc bệnh trầm cảm. Thần kinh vững, nhưng mới đọc 2 câu truyện đầu thôi mà cảm giác ngộp thở, bứt rứt thật khó tả 😰

    Câu chuyện tiếp theo đó thì mang lại cảm giác dễ thở hơn vì họ kiểm soát được bản thân. Nhưng sang tiếp câu chuyện thứ 4, thứ 5 thì lại như một có một bàn tay siết họng mình lại. Không thể nhắm mắt mà nghe được á, có gì đó rất đáng sợ.

    Một câu mà mình cảm thấy ám ảnh nhất là khi ba mẹ của người mắc trầm cảm luôn miệng hỏi tại sao con lại như thế mỗi khi sắp lên cơn và gào thét. Họ chỉ đành bất lực trả lời là "Làm sao mà con biết! Cũng giống như hỏi một người bị ốm là tại sao lại ốm? Làm sao mà biết được chứ! Có ai muốn đâu!".

    » Xem thêm
  • Mistral.ai là một công ty AI có trụ sở tại Pháp, được biết đến với nhiều mô hình ngôn ngữ lớn Mistral. Mới đây họ vừa ra mắt thêm một số mô hình có kích thước siêu lớn, siêu mạnh... Nhưng tạm khoan nói đến vì Mistral Chat cũng vừa được ra mắt với nhiều tính năng hay ho tương tự như Chat GPT mà lại miễn phí 😇

    » Xem thêm

Vấn đề

Bài toán tìm kiếm dữ liệu sao cho thật nhanh và chính xác nhất luôn là vấn đề muôn thuở của các developer. Tùy mục đích, tùy bài toán và tài nguyên cho phép mà chúng ta có thể lựa chọn những công cụ và phương pháp khác nhau.

Ví dụ khi bạn thao tác với một tập dữ liệu nhỏ thì bạn có thể sử dụng toán tử LIKE trong sql, nhưng khi dữ liệu trở nên nhiều hơn thì LIKE không còn là phương pháp tối ưu nữa. Lúc đó bạn có thể chuyển sang sử dụng những module hỗ trợ fulltext search có trong cơ sở dữ liệu đang sử dụng. Tuy nhiên những module này cũng chỉ là giải pháp tạm thời vì chúng có thể không cung cấp được hết các tính năng mạnh mẽ khác so với công cụ fulltext khác đang có mặt trên thế giới.

Elasticsearch và Apache Solr là hai thư viện rất mạnh mẽ và được cộng đồng sử dụng rất phổ biến, tuy nhiên chúng lại yêu cầu một phần cứng khá "khiêm tốn" không dễ dàng để tiếp cận cho những bạn nào phát triển dự án với mức kinh phí cũng khiêm tốn.

Redisearch ra đời dựa trên redis, nó cung cấp một cỗ máy tìm kiếm rất mạnh mẽ cùng với mức "tiêu thụ" tài nguyên ít nhất mà bạn có thể dễ dàng tích hợp vào trong dự án của mình. Cho những bạn chưa biết thì redis là một cơ sở dữ liệu dạng key-value mà dữ liệu được lưu trữ trong bộ nhớ truy cập ngẫu nhiên (RAM) cho tốc độ truy xuất rất nhanh nên thường được sử dụng để làm bộ nhớ đệm (cache). Tuy là nhỏ bé nhưng nó cũng không kém phần mạnh mẽ so với hai đàn anh đi trước. Trong bài viết này chúng ta hãy xem redisearch có thể làm được những gì nhé.

Tạo index

Việc cần làm đầu tiên là cần phải tạo một index để có thể tìm kiếm. Index như là một cách để khai báo cho cỗ máy tìm kiếm biết dữ liệu của bạn nên được xử lý như thế nào cho tối ưu nhất.

Tạo index rất đơn giản trong redisearch, ví dụ tôi tạo một index phục vụ cho việc tìm kiếm bài viết gồm có ba trường dữ liệu là title, content và created_at tương ứng với tiêu đề, nội dung và ngày tạo bài viết.

FT.CREATE article ON HASH PREFIX 1 article: SCHEMA title TEXT WEIGHT 5.0 content TEXT created_at NUMERIC SORTABLE

Index của tôi có tên là article, ở trường title tôi đặt WEIGHT = 5 để kết quả tìm kiếm trường dữ liệu title sẽ ưu tiên hơn so với content, created_at là SORTABLE để có thể sắp xếp thứ tự kết quả tìm kiếm. Nếu không khai báo SORTABLE thì bạn sẽ không thể sắp xếp được kết quả tìm kiếm.

Được rồi, sau khi tạo xong index thì chúng ta sẽ tìm hiểu cách tìm kiếm dữ liệu nhé.

Nguyên tắc tìm kiếm

Trước khi đến với cú pháp tìm kiếm, bạn cần biết một vài nguyên tắc tìm kiếm của redisearch:

  • Khi tìm kiếm một cụm từ ví dụ như hello world thì đơn giản là bạn đang tìm kiếm những đoạn có chứa cả hai từ hello và (AND) world.
  • Nếu muốn tìm kiếm chính xác cụm từ hello world thì bạn phải đặt nó trong dấu nháy kép (""). Ví dụ "hello world".
  • Khi muốn tìm kiếm theo kiểu có chứa hello hoặc world (OR) thì ngăn cách chúng bằng dấu |. Ví dụ hello|world.
  • Khi muốn tìm kiếm NOT thì sử dụng dấu -. Ví dụ tìm kiếm có chứa hello nhưng không chứa world thì là hello -world. Bạn cũng có thể kết hợp NOT nhiều từ bằng cách kết hợp với OR, ví dụ tìm kiếm trong trường title không chứa từ hello hoặc world: -@title:(hello|world).
  • Mặc định nếu không chỉ định tìm kiếm dữ liệu trên trường nào thì redisearch sẽ tìm kiếm trên tất cả các trường dữ liệu của index. Để xác định trường dữ liệu cần tìm, bạn sử dụng cú pháp @filed:query. Ví dụ @title:hello world.
  • Tìm kiếm dữ liệu trên trường NUMERIC phải sử dụng cú pháp [min max].
  • Tìm kiếm dữ liệu trên trường TAG phải sử dụng cú pháp {tag1 | tag2 | ...}
  • Fuzzy matching (gợi ý tìm kiếm) là một tính năng gợi ý từ trong tìm kiếm. Ví dụ cho trường hợp này là khi bạn gõ từ trên google thì nó gợi ý ra những từ tiếp theo. Cú pháp sẽ là %text.
  • ...

Còn một vài nguyên tắc nữa các bạn có thể xem thêm tại Search Query Syntax.

Và cuối cùng là một bảng cheatsheet thần thánh để so sánh một số lệnh tìm kiếm dữ liệu giữa SQL và redisearch:

Cú pháp tìm kiếm

Đầu tiên chúng ta hãy thêm một ít dữ liệu vào redis bằng cách sử dụng luôn index article đã tạo ở trên. Để cho đơn giản và dễ nhìn tôi sẽ thêm một vài dữ liệu nhỏ nhẹ cho mọi người dễ quan sát.

HSET article:1 url "url-1" title "bài viết số một" content "nội dung của bài viết số một" created_at 1630245601
HSET article:2 url "url-2" title "bài viết số hai" content "nội dung của bài viết số hai" created_at 1630245602
HSET article:3 url "url-3" title "bài viết số ba" content "nội dung của bài viết số ba" created_at 1630245603

Tìm tất cả record có chứa từ bài viết:

FT.SEARCH article "bài viết"

Tìm tất cả record mà content có chứa từ nội dung:

FT.SEARCH article "@content:nội dung"

Tìm tất cả record mà title có chứa từ bài viết và content không chứa từ số một:

FT.SEARCH article "@title:bài viết -@content:số một"

Tìm tất cả record mà title có chứa từ số một hoặc số hai và content không chứa từ số ba, sắp xếp theo thứ tự giảm dần của created_at:

FT.SEARCH article "@title:(số một | số hai) -@content:số ba" SORTBY created_at DESC

Stop Words

Stop words là một thuật ngữ chỉ cách mà redisearch sẽ bỏ qua một số từ quá phổ biến nhưng lại không mang lại giá trị trong tìm kiếm ví dụ như a, is, the... Những từ này nếu được đánh index sẽ chiếm nhiều không gian lưu trữ đồng thời làm ngốn lượng CPU trong lúc tìm kiếm dữ liệu.

Bởi vì redisearch được tạo ra cho tất cả người dùng thế nên họ chỉ tích hợp mặc định một số từ tiếng anh để loại bỏ Stop Words. Tuy vậy, bạn hoàn toàn có thể dịch nghĩa nó về tiếng Việt để thêm vào từ điển, hoặc bạn cũng có thể thêm những từ mà bạn không muốn sử dụng để tìm kiếm.

Stop Words được khai báo lúc tạo index. Ví dụ dưới đây tôi đang thêm 2 từ "thì", "là" vào Stop Words ở index article:

FT.CREATE article STOPWORDS 2 thì là ON HASH PREFIX 1 article: SCHEMA title TEXT WEIGHT 5.0 content TEXT created_at NUMERIC SORTABLE

Chú ý: Vì Stop Words phải được thêm ở lúc tạo index thế nên nếu các bạn đã có index từ trước thì bắt buộc phải xóa trước khi thêm lại. Sử dụng lệnh FT.DROPINDEX để xóa index, mặc định khi xóa index thì dữ liệu của index sẽ không bị xóa. Sau đó thì chúng ta tiến hành tạo lại index như bình thường.

Nếu như không muốn dùng Stop Words nữa thì đặt STOPWORDS 0 ở lệnh tạo index.

Tokenization và Escaping

Tokenization và Escaping được hiểu là trình mã hóa kí tự đầu vào và mã hóa kí tự trong lúc truy vấn dữ liệu. Dữ liệu khi được đưa vào redisearch phải qua một bước xử lý ví dụ như loại bỏ các khoảng trắng, các kí tự đặc biệt... Dưới đây là một số quy tắc Tokenization của redisearch:

  • Các kí tự ,.<>{}[]"':;!@#$%^&*()-+=~ và khoảng trắng (space) sẽ ngắt văn bản để đánh index. Ví dụ một đoạn văn bản hello-world...1 sẽ được mã hóa thành [hello world 1].
  • Nếu muốn vượt qua quy tắc trên, tức là muốn redisearch index cả những kí tự đặc biệt và khoảng trắng thì chúng ta phải thêm dấu "gạch chéo ngược" () vào trước mỗi kí tự đặc biệt. Ví dụ tôi muốn đưa được cụm hello-world vào redisearch thì phải sửa lại đoạn text thành hello\-world, khi đó lúc tìm kiếm thì cũng phải dùng hello\-world để tìm kiếm.
  • Dấu gạch dưới (_) không bị ảnh hưởng bởi Tokenization và Escaping.
  • Các khoảng trắng (space) lặp lại và các kí tự ở trong mục một được loại bỏ trong lúc truy vấn. Nếu muốn dùng thì phải thêm dấu "gạch chéo ngược" vào trước.
  • Các kí tự Latinh (A-Z a-z) đều được chuyển về chữ thường.

Đó là một số nguyên tắc của trường dữ liệu TEXT, đối với dữ liệu dạng TAG sẽ có một ít sự khác biệt mà tôi sẽ nói ở bài viết sau.

Highlighting Result

Highlighting API cho phép chúng ta thao tác với vùng dữ liệu được tìm thấy trong redisearch như chèn thêm kí tự để làm nổi bật kết quả...

Để bao bọc kết quả tìm kiếm, ví dụ như một thẻ mở/đóng xung quanh nó chúng ta sử dụng tùy chọn HIGHLIGHT:

FT.SEARCH article "bài viết" HIGHLIGHT TAGS <b> </b>

Kết quả tìm kiếm nếu có tìm được ở tất cả các trường dữ liệu sẽ được chèn vào trong thẻ <b> </b>. Nếu muốn chỉ định trường cụ thể sử dụng HIGHLIGHT chúng ta sử dụng thêm tùy chọn FIELDS:

FT.SEARCH article "bài viết" HIGHLIGHT FIELDS 1 title TAGS <b> </b>

Ngoài ra, redisearch còn hỗ trợ chúng ta hiển thị ngữ cảnh của nội dung mà chúng ta đang tìm kiếm nằm ở đâu. Ví dụ nguyên văn một câu "estacks là blog chia sẻ lập trình" thì khi tìm kiếm với từ "blog", redisearch sẽ hiển thị "...estacks là blog chia sẻ lập trình...".

FT.SEARCH article "bài viết" SUMMARIZE FIELDS 1 content

Bạn cũng có thể kết hợp cả HIGHLIGHT và SUMMARIZE trong một truy vấn.

Tổng kết

Qua bài viết này tôi hy vọng mọi người nắm được công cụ redisearch được dùng để làm gì, nó có phù hợp hay cần thiết với các bạn trong các dự án sắp tới không và các câu lệnh cơ bản để bắt đầu. Hãy không ngừng học thêm những công cụ mới để có nhiều cách giải quyết vấn đề hơn nữa!

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.
Author

Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.

Bạn thấy bài viết này có ích?
Không

Bình luận (0)

Nội dung bình luận...