<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 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