docs: def search(cls, term, fields=()):
          """Generic SQL search function that uses SQL ``LIKE`` to search the
          database for matching records. The records are sorted by their
          relavancey to the search term.
  
          The query searches and sorts on the folling criteria, in order, where
          the target string is ``exactly``:
  
          1. Straight equality (``x = 'exactly'``)
          2. Right hand ``LIKE`` (``x LIKE 'exact%'``)
          3. Substring ``LIKE`` (``x LIKE %act%``)
  
          Args:
              term (str): The search term to apply to the query.
  
          Keyword Args:
              fields (list|tuple|None): An optional list of fields to apply the
                  search to. If not provided, the class variable
                  ``Meta.search_fields`` will be used by default.
          Returns:
              peewee.SelectQuery: An unexecuted query for the records.
  
          Raises:
              AttributeError: Raised if `search_fields` isn't defined in the
                  class and `fields` aren't provided for the function.
          """
          if not any((cls._meta.search_fields, fields)):
              raise AttributeError(
                  "A list of searchable fields must be provided in the class's "
                  "search_fields or provided to this function in the `fields` "
                  "kwarg."
              )
  
          # If fields are provided, override the ones in the class
          if not fields:
              fields = cls._meta.search_fields
  
          query = cls.select()
  
          # Cache the LIKE terms
          like_term = ''.join((term, '%'))
          full_like_term = ''.join(('%', term, '%'))
  
          # Cache the order by terms
          # @TODO Peewee's order_by supports an `extend` kwarg will will allow
          # for updating of the order by part of the query, but it's only
          # supported in Peewee 2.8.5 and newer. Determine if we can support this
          # before switching.
          # http://docs.peewee-orm.com/en/stable/peewee/api.html#SelectQuery.order_by
          order_by = []
  
          # Store the clauses seperately because it is needed to perform an OR on
          # them and that's somehow impossible with their query builder in
          # a loop.
          clauses = []
  
          for field_name in fields:
              # Cache the field, raising an exception if the field doesn't
              # exist.
              field = getattr(cls, field_name)
  
              # Apply the search term case insensitively
              clauses.append(
                  (field == term) |
                  (field ** like_term) |
                  (field ** full_like_term)
              )
  
              order_by.append(case(None, (
                  # Straight matches should show up first
                  (field == term, 0),
                  # Similar terms should show up second
                  (field ** like_term, 1),
                  # Substring matches should show up third
                  (field ** full_like_term, 2),
              ), default=3).asc())
  
          # Apply the clauses to the query
          query = query.where(reduce(operator.or_, clauses))
  
          # Apply the sort order so it's influenced by the search term relevance.
          query = query.order_by(*order_by)
  
          return query