Search trong Django

Search trong Django

Search dường như là một trong những tác vụ được sử dụng rất nhiều cho các ứng dụng web/app. Django là một trong những web framework mạnh nhất của Python. Do đó, trong engine của mình, Django hỗ trợ search rất tốt. Tài liệu dưới đây sẽ giới thiệu một số trường hợp sử dụng có thể và các công cụ bạn có thể sử dụng.

Standard textual queries

Với các case chỉ cần giải pháp đơn giản, Django có 2 hàm sử dụng containsicontains để hỗ trợ tìm kiếm. Anh em developer hay gọi là kiểu search LIKE.

contains

Khi sử dụng Django ORM thì syntax sẽ như thế này:

Blog.objects.get(title__contains='Viblo')

SQL query với kết quả tương đương:

SELECT ... WHERE title LIKE '%Viblo%';

Sử dụng contains có một nhược điểm cần chú ý. Như ví dụ trên, nếu search title là viblo, bạn sẽ không nhận được record nào cả 😄. Đó là lý do icontains xuất hiện.

icontains

Syntax ORM cũng khá giống với lúc sử dụng contains:

Blog.objects.get(title__icontains='Viblo')

SQL tương ứng:

SELECT ... WHERE headline ILIKE '%Viblo%';

A database’s more advanced comparison functions

Cái này nó phụ thuộc nhiều vào database engine. Nếu bạn đang sử dụng PostgreSQL, Django hỗ trợ a selection of database specific tools cho phép bạn thực hiện các truy vấn phức tạp hơn. Nếu bạn sử dụng database khác, bạn có thể kết hợp với thirth-party plugin/extension hỗ trợ. Tại thời thời điểm này, Django không hỗ trợ được cho tất cả các database engine khác nhau.

Ta sẽ làm vài ví dụ truy vấn phức tạp với PostgreSQL.

Như ví dụ trên, từ Viblo là một từ tiếng Anh. It's okay. Nhưng nếu title là non-English, ta sẽ cần thêm unaccent. unaccent được sử dụng cho CharField and TextField:

>>> Blog.objects.get(title__unaccent__icontains='Mexico')
['<Blog: Mexico>', '<Blog: Mexico City>', '<Blog: México City>']

Nhưng nếu ta thử một case khác:

>>> Blog.objects.get(title__unaccent__icontains='México')
['<Blog: México City>']

Trong trường hợp này, nếu bạn muốn search México mà mong muốn ra cả Mexico, bạn cần phải dùng thêm trigram_similar:

>>> Blog.objects.get(title__unaccent__lower__trigram_similar='México')
['<Blog: Mexico>', '<Blog: Mexico City>', '<Blog: México City>']

Các ví dụ trên sẽ làm các developer phải cân nhắc sử dụng. Bởi vì, mỗi bài toán mà mình sẽ cần sử dụng comparison functions khác nhau.

Document-based search

Ok. Như các phần trên, ta chỉ search trên một số lượng text ở mức độ khá nhỏ. Nhưng nếu ta cần search ở mức độ lớn hơn như toàn bộ nội dung của một new. Khi đó, ta sẽ cần có một vài giải pháp:

  • Ignoring “stop words”: such as “a”, “the”, “and”.
  • Stemming words: các từ như country hay countries sẽ được coi là tương tự nhau.
  • Weighting words được base trên các tiêu chí như tần xuất xuất hiện, tầm quan trọng,

Nếu bạn muốn sử dụng searching engine thay thế, một vài cái tên nổi bật như ElasticSearch and Solr. Sử dụng Django kết hợp với ElasticSearch mình sẽ giới thiệu vào một bài viết khác.

PostgreSQL support

PostgreSQL support sẵn full text search vào trong engine của mình. Mặc dù, nó khó có thể powerful so với ES (ElasticSearch), nhưng nó cũng có ưu điểm riêng. Tính năng này nằm sẵn trong database. Do đó, có thể kết hợp nó với các query phức tạp khác. Nó rất tiện ở chỗ đó.

Module django.contrib.postgres cung cấp một số trợ giúp để thực hiện các truy vấn này. Để sử dụng module này, developer cần thêm django.contrib.postgres vào trong INSTALLED_APPS.

Dưới đây là ví dụ:

>>> Entry.objects.filter(body_text__search='cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]

Khi kết hợp với nhiều query khác:

>>> Entry.objects.annotate(
...     search=SearchVector('blog__tagline', 'body_text'),
... ).filter(search='cheese')
[
    <Entry: Cheese on Toast recipes>,
    <Entry: Pizza Recipes>,
    <Entry: Dairy farming in Argentina>,
]

Bạn có thể tham khảo nhiều hơn về PostgreSQL full text search tại đây: https://docs.djangoproject.com/en/3.0/ref/contrib/postgres/search/

MySQL support

Nếu bạn đang dùng Django bản cũ, cụ thể là <= 1.9, Django có hỗ trợ full text search cho Mysql.

Entry.objects.filter(headline__search="+Django -jazz Python")

SQL equivalent:

SELECT ... WHERE MATCH(tablename, headline) AGAINST (+Django -jazz Python IN BOOLEAN MODE);

Mặc định Django sử dụng BOOLEAN MODE for full text searches. Nếu bạn muốn sử dụng tính năng này, hãy tham khảo tại đây

Trên đây là một số case search phổ biến mà Django support rất tốt. Hi vọng, qua bài viết này, anh em Django developer sẽ có những lựa chọn phù hợp khi làm tính năng search cho web/app của mình.


All Rights Reserved