Qanday qilib Qidirish(Search) tizimini qo'shish mumkin Django da PostgreSQL dan foydalanib.

Qanday qilib Qidirish(Search) tizimini qo'shish mumkin Django da PostgreSQL dan foydalanib.

Shukurali Rezamonov
Search Feature

Search - bu biron bir ma'lumotni bittalab kurib chiqib topishning urniga qidirish orqali topishni imkonini beradi.

Ko'pincha ma'lumot ko'p bo'lgan website, application yoki boshqa kottaroq platformalarda ko'zlangan(qidirilayotgan) ma'lumotni qisqa vaqt ichida topish asosan uchun filter va search ishlatiladi.

Filters
Search bar


Tepadagi search va filter orqali qidirilayotgan ma'lumot qanday qilib Ma'lumotlar bazasidan(Database) topib chiqarib beriladi ?

Endi shu savolga biz Django frameworki hamda PostgreSQL database dan foydalanib javob topishga harakat qilamiz.


  1. Environment yaratib olamiz: python3 -m venv env
  2. Bizga kerak buladigan Dependencies ni urnatib olamiz.
    * Django == 4.2
    * psycopg2-binary == 2.9.6
  3. Django project va App yaratib olamiz.
  4. INSTALLED_APP ga qushamiz yaratgan appimizni.
  5. Postgresql konfiguratsiyasini soslaymiz settings.py da.
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "django_search_db",
"USER": "postgres",
"PASSWORD": "your_db_password",
"HOST": "localhost",
"PORT": 5432,
}
}


  1. Migratsiyalarni yaratamiz: python3 manage.py migrate
  2. Quyidagi modelni app dagi models.py file ga qushamiz
class Post(models.Model):
title = models.CharField(max_length=120)
body = models.TextField(max_length=1000)

def __str__(self):
return self.title


7. Migrations yaratamiz model uchun:

python3 manage.py makemigrations

python3 manage.py migrate

8. Faker package ni urnatamiz dbga fake malumot generate qilib tuldirish uchun.

* Faker==19.1.0

9. App imizni ichiga myapp/management/commands/add_posts.py shu ketmaketlikda folderlar va file ochib olamiz va quyidagi kodni kiritamiz.

from django.core.management.base import BaseCommand
from faker import Faker
from ...models import Post

class Command(BaseCommand):
help = "Adds posts to the database"

def handle(self, *args, **options):
fake = Faker()

for _ in range(10000):
Post.objects.create(title=fake.name(), body=fake.text())

print("Completed!!! Check your database.")


10. Faker ni ishga tushirish uchun quyidagi komandani yozamiz:

python3 manage.py add_posts

Result after above command


11. App ni ichida templates folder ochib posts.html va search.html file larni yaratib olamiz.

12. Keyingi bosqichda biz Postlarimizni listini chiqarishimiz uchun View yozamiz.

class PostListView(ListView):
model = Post
context_object_name = "posts"
template_name = "posts.html"


posts.html: body qismi.


<body>
<div class="container">
<br><br>
<h1>All Posts</h1>
<br><br>
<form action="{% url 'search_result' %}" method="get">
<input type="search" name="q" placeholder="Search by name or quote..." class="form-control">
</form>
<br><br>
{% for post in posts %}
<ul>
<li><b>{{ post.title }}</b> - <i>{{ post.body }}</i></li>
</ul>
{% empty %}
<p>The database is empty. Add some quotes.</p>
{% endfor %}
<br><br>
</div>
</body>

{% url 'search_result' %} bu yerdagi search_result search/ url pattern uchun quyilgan name.

13. Shu urinda urls.py file yaratamiz app ni ichida va tepedagi PostListView miz uchun url pattern yaratamiz, keyin uni asosiy projectimizni urls.py file da kursatib utamiz.

14. python3 manage.py runserver orqali loyihani ishga tushirib http://127.0.0.1:8000/posts/ shu manzilga brauzer orqali kiramiz va quyidagi sahifani kuramiz.

bu yerda tepada yozgan PostListView miz orqali hamma postlar chiqib keldi endi biz bularni ichidan keraklisini search qilishimiz uchun Search funksiyasini yozishimiz kerak va search qilingandan keyingi results chiqadigan alohida sahifa ham qilishimiz kerak.

15. views,py file da SearchResultView class yaratamiz va djangoning contains va icontains lookups orqali querysetni filter qilib olib kuramiz.

class SearchResultView(ListView):
model = Post
context_object_name = "posts"
template_name = "search.html"

def get_queryset(self):
query = self.request.GET.get("q")
return Post.objects.filter(
Q(title__contains=query) | Q(body__contains=query)
)

contains va icontains ni farqi shundaki title__contains deb filter qilayotganimizda query="Django" kelgan bulsa db dan faqat 'Django' ni qidiradi uz holicha.

title__icontains qilsak va query="Django" bulsa db dan "Django" yoki "django", "DjAngo" va shunga uxshash text larni filter qiladi ya'ni harflarni katta kichikligiga etibor bermaydi.


Endi tepadagi view uchun bizga search.html file kerak buladi. Buning uchun biz templates folderni ichida search.html file ochib quyidagi kod ni quyamiz.

search.html: body qismi.

<body>
<div class="container">
<br><br>
<h1 style="color: red;">Search Results</h1>
<br><br>
{% for post in posts %}
<ul>
<!-- <li>{{ quote.headline | safe }} - <b>By <i>{{ quote.name }}</i></b></li> -->
<li>{{ post.title | safe }} - <b>By <i>{{ post.body }}</i></b></li>
</ul>
{% empty %}
<p>No results found. Try again.</p>
{% endfor %}
<br><br>
<a href="{% url 'all_posts' %}" class="btn btn-primary">All Quotes</a>
<br><br>
</div>
</body>


Natijani kuradigan bulsak.
Bu holatda biz contains ni ishlatib query='rachel Maxwell' berdik lekin r kichik harf bulgani uchun natija topilmadi.

contains result 1

lekin query='Rachel Maxwell' qilib berganimizda natija chiqdi.

contains result 2


icontains orqali sinab kuradigan bulsak bu holatda R yoki r bulsa ham natija chiqadi.

ya'ni harflarni turli xil xolatda uzgartirsam ham natija topildi.

icontains result


16. Endi Full Text search ni kuramiz. Bu hozir kurgan simple text search dan farq qiladi ya'ni misol uchun bizda query='middle' bulsin va bizning db mizda 2 ta middle qatanshgan matn bor.

  1. I am in the middle
  2. I don't like middle school
Natija

Quyidagicha natija olishimiz mumkin.


17. Full Text searchni implementatsiya qilishimiz uchun

    1. INSTALLED_APPS ga "django.contrib.postgres", ni qushamiz.
    
    2. views.py filedagi SearchResultView mizning get_queryset methodiga quyidagi queryset filter statement ni quyamiz. 
    
      return Post.objects.filter(body__search=query) 

Natija Full-Text search

Natija quyidagicha qeery='single bar' bulgandi lekin bu yerda aynan shu text bilan bir xil bulmasa ham Single yoki bar so'zi bulgan post larni ham berdi.


18. Endi bizda bu holatda faqat bitta field buyicha filter qila olyapmzi ammo buni bir nechta fieldlar bilan ham qilishimiz mumkin. Buning uchun biz SearchVector class i dan foydalanamiz.

Django Postgres Engine dan Import qilib olamiz.

from django.contrib.postgres.searbuch import SearchVector

views.py file ga uzgartirish kiritamiz:

return Quote.objects.annotate(search=SearchVector("title", "body")).filter(
            search=query
        )


19. Steaming va Rangking. Biz bu SearchVector ,SearchQuery, SearchRank orqali judayam ishonchi va puxta search engine ishlab chiqamiz.

Ya'ni biz bulardan foydalanganimizda so'zlarni filter qilish davomida ularning o'zagi, asosiy qismi va ildizi bilan solishtirilib olinadi. Bunda misol uchun bizda 'child', 'children' kabi suzlar bir xil deb kuriladi.

Quyida qanday qilib implementatsiya qilishni kuramiz.

1. Import qilamiz.

from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank


2.queryset method ni yangilaymiz.

def get_queryset(self):
        query = self.request.GET.get("q")
        search_vector = SearchVector("name", "quote")
        search_query = SearchQuery(query)
        return (
            Quote.objects.annotate(
                search=search_vector, rank=SearchRank(search_vector, search_query)
            )
            .filter(search=search_query)
            .order_by("-rank")
        )


SearchQuery - bizga taqdim etilgan so'zlarni(field name) shakldan so'rov(query) sifatida tarjima qiladi, ularni asosiy algoritmdan o'tkazadi va keyin barcha olingan atamalar uchun moslikni qidiradi.

SearchRank - natijalarni moslik bo'yicha tartiblashga imkonini beradi. Textda so'rov shartlari qanchalik tez-tez korinishi, shartlar textga qanchalik yaqin ekanligi va textning bir qismi ular paydo bo'lgan joyda qanchalik muhimligini hisobga oladi


Quyida Oddiy simple search bilan Full-Text search solishtirilgan.

Bunda judayam aniq farqlar bor.

Full-Text searchda eng yuqori natijalarga ega so'rov birinchi bo'lib ko'rsatiladi. Bu SearchRankning kuchi. SearchVector, SearchQuery va SearchRank-ni birlashtirish asosiy qidiruvga qaraganda ancha kuchli va aniq qidiruvni yaratishning tezkor usulidir.


Xulosa

Django da text yoki words ni search qilish uchun django ning uzidagi queryset lookups laridan tortib to postgresql ni SearchVector, Rank oraqi sifatli va kuchli search tizimi qilish mumkinligini kurdik. Lekin hozirda ko'pchilik platformalarda Elastiksearch kutubxoasini ishlatishadi Search engine qurishda. Ammo bu kutubxonani ham uziga yarasha kamchiligi bor ya'ni uni implementatsiya qilish va maintain qilish nisbatan qiyinroq. Agar sizlarga qiziq bulsa LinkedIn dagi aynan shu postning commentiga yozib qoldiring va men kelgusi safar bu haqida ham maqola chiqarishga harakat qilaman.

Shu bilan maqolamizga yakun yasaymiz. Foydali bulgan bulsa ajratgan vaqtingizga rozi bulasiz degan umiddaman.

Keyingi maqolalarda Elastiksearch haqida yoki Django queryset da ishlatiladigan hamma lookup ,methodlar, extra objectlar haqida bulishi mumkin.


Maqola haqidagi fikringizni yoki bilgan kamchiliklaringiz bulsa LinkedIn dagi post commentiga yozishingizni surab qolgan bulardim.


Maqolada ishlatilgan Project Source code: https://github.com/ShukuraliProgrammer/django-search-app.git


Contact: https://t.me/Shukurali_Rezamonov
Telegram Channel: https://t.me/shukurdev
LinkedIn: https://www.linkedin.com/in/shukurdev/


Report Page