# From: http://www.mercurytide.co.uk/news/article/django-full-text-search/ from django.db import connection, models from django.db.models.query import QuerySet class SearchQuerySet(QuerySet): DEFAULT = "" BOOLEAN = " IN BOOLEAN MODE" NATURAL_LANGUAGE = " IN NATURAL LANGUAGE MODE" QUERY_EXPANSION = " WITH QUERY EXPANSION" def __init__(self, fields=None, **kwargs): super(SearchQuerySet, self).__init__(**kwargs) self._search_fields = fields def _clone(self, *args, **kwargs): queryset = super(SearchQuerySet, self)._clone(*args, **kwargs) queryset._search_fields = self._search_fields return queryset def search(self, query, mode=DEFAULT): meta = self.model._meta # Get the table name and column names from the model # in `table_name`.`column_name` style columns = [meta.get_field(name).column for name in self._search_fields] full_names = [ "%s.%s" % ( connection.ops.quote_name(meta.db_table), connection.ops.quote_name(column), ) for column in columns ] # Create the MATCH...AGAINST expressions fulltext_columns = ", ".join(full_names) match_expr = "MATCH(%s) AGAINST (%%s%s)" % (fulltext_columns, mode) # Add the extra SELECT and WHERE options return self.extra( select={"relevance": match_expr}, select_params=[query], where=[match_expr], params=[query], ) class SearchManager(models.Manager): def __init__(self, fields=None): super(SearchManager, self).__init__() self._search_fields = fields def get_queryset(self): if self._search_fields is not None: return SearchQuerySet(model=self.model, fields=self._search_fields) return super(SearchManager, self).get_queryset() def search(self, *args, **kwargs): return self.get_queryset().search(*args, **kwargs)