Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Search from the Ground Up

Search from the Ground Up

A practical hands-on guide to implementing search in any Django web application. From basic search to full-text search with PostgreSQL.

Talk given at DjangoCon US 2019.

William S. Vincent

September 24, 2019
Tweet

More Decks by William S. Vincent

Other Decks in Technology

Transcript

  1. DjangoCon US 2019 | William Vincent Search From the Ground

    Up William Vincent DjangoCon US 2019
  2. DjangoCon US 2019 | William Vincent Search Options Basic Filters

    Q Objects Full Text Search Hosted Service
  3. DjangoCon US 2019 | William Vincent Search Magic City Search

    Website Massachusetts City Search Website City Search Website • Boston, MA • Worcester, MA • Springfield, MA Form Results + Magic =
  4. DjangoCon US 2019 | William Vincent Installation / Set Up

    $ pipenv install django==2.2.5 $ pipenv shell (env) $ django-admin startproject citysearch_project . (end) $ python manage.py migrate (end) $ python manage.py startapp cities
  5. DjangoCon US 2019 | William Vincent Models # cities/models.py from

    django.db import models class City(models.Model): name = models.CharField(max_length=100) state = models.CharField(max_length=100) class Meta: verbose_name_plural = 'cities'
  6. DjangoCon US 2019 | William Vincent Migrations (env) $ python

    manage.py makemigrations cities (env) $ python manage.py migrate (env) $ python manage.py createsuperuser
  7. DjangoCon US 2019 | William Vincent Admin # cities/admin.py from

    django.contrib import admin from .models import City class CityAdmin(admin.ModelAdmin): list_display = ('name', 'state') admin.site.register(City, CityAdmin)
  8. DjangoCon US 2019 | William Vincent Django’s 4-Way Dance urls.py

    templates.html views.py models.py WEBPAGE!!!
  9. DjangoCon US 2019 | William Vincent URLs (part 1) #

    citysearch_project/urls.py from django.contrib import admin from django.urls import path, include # new urlpatterns = [ path('admin/', admin.site.urls), path('', include('cities.urls')), # new ]
  10. DjangoCon US 2019 | William Vincent URLs (part 2) #

    cities/urls.py from django.urls import path from .views import SearchResultsView, HomepageView urlpatterns = [ path('search/', SearchResultsView.as_view(), name='search_results'), path('', HomepageView.as_view(), name='home'), ]
  11. DjangoCon US 2019 | William Vincent Views # cities/views.py from

    django.views.generic import TemplateView, ListView from .models import City class HomepageView(TemplateView): template_name = 'home.html' class SearchResultsView(ListView): model = City template_name = 'search_results.html'
  12. DjangoCon US 2019 | William Vincent Template Structure Default (in

    app) citysearch_project |_ cities |_ templates |_ cities |_ home.html Custom (project-level) citysearch_project cities templates |_ home.html
  13. DjangoCon US 2019 | William Vincent Template DIRS # citysearch_projects/settings.py

    TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], # new } ]
  14. DjangoCon US 2019 | William Vincent Search Template # templates/search_results.html

    <h1>Search Results</h1> <ul> {% for city in object_list %} <li> {{ city.name }}, {{ city.state }} </li> {% endfor %} </ul>
  15. DjangoCon US 2019 | William Vincent Filtering # cities/views.py class

    SearchResultsView(ListView): model = City template_name = 'search_results.html' def get_queryset(self): # new return City.objects.filter(name__icontains='Boston')
  16. DjangoCon US 2019 | William Vincent Chaining Filters (ANDs) #

    cities/views.py class SearchResultsView(ListView): model = City template_name = 'search_results.html' def get_queryset(self): # new return City.objects.filter( name__icontains='Boston' ).exclude( state__icontains='NY' )
  17. DjangoCon US 2019 | William Vincent Q Objects (ORs) #

    cities/views.py from django.db.models import Q # new ... class SearchResultsView(ListView): model = City template_name = 'search_results.html' def get_queryset(self): # new return City.objects.filter( Q(name__icontains='Boston') | Q(state__icontains='NY') )
  18. DjangoCon US 2019 | William Vincent HTML Form # templates/home.html

    <h1>HomePage</h1> <form action={% url 'search_results' %} method='get'> <input name='q' type='text' placeholder='Search...'> </form>
  19. DjangoCon US 2019 | William Vincent Add q to Queryset

    # cities/views.py class SearchResultsView(ListView): ... def get_queryset(self): # new query = self.request.GET.get('q') object_list = City.objects.filter( Q(name__icontains=query) | Q(state__icontains=query) ) return object_list
  20. DjangoCon US 2019 | William Vincent Django Forms # cities/forms.py

    from django import forms class SearchForm(forms.Form): q = forms.CharField(label='Search', max_length=50)
  21. DjangoCon US 2019 | William Vincent View with Django Form

    # cities/views.py from django.views.generic import FormView, ListView from .forms import SearchForm from .models import City class HomepageView(FormView): template_name = 'home.html' form_class = SearchForm ...
  22. DjangoCon US 2019 | William Vincent Template with Django Form

    # templates/home.html <h1>Homepage</h1> <form action={% url 'search_results' %} method='get'> {{ form }} </form>
  23. DjangoCon US 2019 | William Vincent Search Options Basic Filters

    Q Objects Full Text Search Hosted Service
  24. DjangoCon US 2019 | William Vincent Full-Text Search Search Documents

    that satisfy a Query and sort by Relevance. • PostgreSQL support since 8.3 (2008) • Django support since version 1.10 (2016) led by Marc Tamlyn
  25. DjangoCon US 2019 | William Vincent Full Text Search Features

    • Rankings • Indexing • Phrase search • Stop words • Stemming • Accent / multiple language support • JSON[B] support
  26. DjangoCon US 2019 | William Vincent PostgreSQL Data Types tsvector:

    preprocessed documents tsquery: processed queries
  27. DjangoCon US 2019 | William Vincent tsvector Document: 'The quick

    brown fox jumps over the lazy dog.' tsvector ------------------------------------------------------------------------------------- 'brown':3 'dog':9 'fox':4 'jump':5 'lazy':8 'quick:2'
  28. DjangoCon US 2019 | William Vincent tsquery List of words

    checked against normalized tsvector 'The quick brown fox jumps over the lazy dog.' Query Match? (using @@ operator) 'dog' true 'dogs' true 'dogfood' false 'jumping' true
  29. DjangoCon US 2019 | William Vincent tsquery operators 'The quick

    brown fox jumps over the lazy dog.' Operator tsquery Result? @@ (match) 'dogs foxes' true & (and) 'dog & fox' true | (or) 'dog | cat' true ! (negation) '!pony ' true
  30. DjangoCon US 2019 | William Vincent django.contrib.postgres.search SearchVector = search

    multiple fields SearchQuery = add stemming and/or stop words SearchRank = apply rank to result of documents Weighted Queries = add weights SearchVectorField = performance upgrade with manual trigger
  31. DjangoCon US 2019 | William Vincent SearchVector # cities/views.py from

    django.contrib.postgres.search import SearchVector ... class SearchResultsView(ListView): ... def get_queryset(self): query = self.request.GET.get('q') object_list = City.objects.annotate( search=SearchVector('name', 'state'), ).filter(search=query) return object_list
  32. DjangoCon US 2019 | William Vincent SearchQuery # cities/views.py from

    django.contrib.postgres.search import SearchVector, SearchQuery def get_queryset(self): query = self.request.GET.get('q') object_list = City.objects.annotate( search=SearchVector('name', 'state'), ).filter(search=SearchQuery(query)) return object_list
  33. DjangoCon US 2019 | William Vincent SearchRank # cities/views.py from

    django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank def get_queryset(self): query = self.request.GET.get('q') vector = SearchVector('state') search_query = SearchQuery(query) object_list = City.objects.annotate( rank=SearchRank(vector, search_query) ).order_by('-rank') return object_list
  34. DjangoCon US 2019 | William Vincent Resources Source Code https://github.com/wsvincent/djangocon2019-search

    Slides https://tinyurl.com/djangocon2019-search PostgreSQL Docs: Full Text Search https://www.postgresql.org/docs/current/textsearch.html