diff --git a/django/db/backends/ado_mssql/base.py b/django/db/backends/ado_mssql/base.py index c5a3b2cc33c..b645b053bf0 100644 --- a/django/db/backends/ado_mssql/base.py +++ b/django/db/backends/ado_mssql/base.py @@ -125,6 +125,9 @@ def get_limit_offset_sql(limit, offset=None): def get_random_function_sql(): return "RAND()" +def get_fulltext_search_sql(field_name): + raise NotImplementedError + def get_drop_foreignkey_sql(): return "DROP CONSTRAINT" diff --git a/django/db/backends/dummy/base.py b/django/db/backends/dummy/base.py index 89fec00c1d4..985fe96469a 100644 --- a/django/db/backends/dummy/base.py +++ b/django/db/backends/dummy/base.py @@ -33,5 +33,6 @@ get_date_extract_sql = complain get_date_trunc_sql = complain get_limit_offset_sql = complain get_random_function_sql = complain +get_fulltext_search_sql = complain get_drop_foreignkey_sql = complain OPERATOR_MAPPING = {} diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 6a53956cadd..4a13450c67d 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -152,6 +152,9 @@ def get_limit_offset_sql(limit, offset=None): def get_random_function_sql(): return "RAND()" +def get_fulltext_search_sql(field_name): + return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name + def get_drop_foreignkey_sql(): return "DROP FOREIGN KEY" diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 2af80ef099c..e9818051081 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -108,6 +108,9 @@ def get_limit_offset_sql(limit, offset=None): def get_random_function_sql(): return "DBMS_RANDOM.RANDOM" +def get_fulltext_search_sql(field_name): + raise NotImplementedError + def get_drop_foreignkey_sql(): return "DROP FOREIGN KEY" diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index a365434318a..decb160ee9c 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -102,6 +102,9 @@ def get_limit_offset_sql(limit, offset=None): def get_random_function_sql(): return "RANDOM()" +def get_fulltext_search_sql(field_name): + raise NotImplementedError + def get_drop_foreignkey_sql(): return "DROP CONSTRAINT" diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 13e7be7a982..697a33bb763 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -108,6 +108,9 @@ def get_limit_offset_sql(limit, offset=None): def get_random_function_sql(): return "RANDOM()" +def get_fulltext_search_sql(field_name): + raise NotImplementedError + def get_drop_foreignkey_sql(): return "DROP CONSTRAINT" diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 7c3018aed9b..7b51967416a 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -124,6 +124,9 @@ def get_limit_offset_sql(limit, offset=None): def get_random_function_sql(): return "RANDOM()" +def get_fulltext_search_sql(field_name): + raise NotImplementedError + def get_drop_foreignkey_sql(): return "" diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index b5245d66244..2f8a8651a1b 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -162,7 +162,7 @@ class Field(object): def get_db_prep_lookup(self, lookup_type, value): "Returns field's value prepared for database lookup." - if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'year', 'month', 'day'): + if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'year', 'month', 'day', 'search'): return [value] elif lookup_type in ('range', 'in'): return value diff --git a/django/db/models/query.py b/django/db/models/query.py index 3517d6bed58..4bd9b3b9fe2 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -615,6 +615,8 @@ def get_where_clause(lookup_type, table_prefix, field_name, value): return "%s = %%s" % backend.get_date_extract_sql(lookup_type, table_prefix + field_name) elif lookup_type == 'isnull': return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or '')) + elif lookup_type == 'search': + return backend.get_fulltext_search_sql(table_prefix + field_name) raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type) def get_cached_row(klass, row, index_start): diff --git a/docs/db-api.txt b/docs/db-api.txt index 0f1064efb57..36246206092 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -1035,6 +1035,15 @@ SQL equivalent:: SELECT ... WHERE pub_date IS NULL; +search +~~~~~~ + +A boolean full-text search, taking advantage of full-text indexing. This is +like ``contains`` but is significantly faster due to full-text indexing. + +Note this is only available in MySQL and requires direct manipulation of the +database to add the full-text index. + Default lookups are exact ~~~~~~~~~~~~~~~~~~~~~~~~~