2011-08-20 17:08:00 +00:00
""" Search backends used by django-watson. """
2012-02-28 16:40:01 +00:00
import re , abc
2011-08-24 10:22:51 +00:00
2011-08-21 16:58:41 +00:00
from django . contrib . contenttypes . models import ContentType
2012-02-28 13:39:13 +00:00
from django . db import connection
2011-08-21 16:58:41 +00:00
from django . db . models import Q
2013-05-01 14:46:10 +00:00
from django . utils . encoding import force_text
from django . utils import six
2011-08-20 17:27:57 +00:00
2011-08-21 17:14:43 +00:00
from watson . models import SearchEntry , has_int_pk
2011-08-20 17:08:00 +00:00
2011-08-24 12:36:26 +00:00
def regex_from_word ( word ) :
""" Generates a regext from the given search word. """
2012-02-28 14:37:39 +00:00
return u " ( \ s {word} )|(^ {word} ) " . format (
2011-08-24 12:36:26 +00:00
word = re . escape ( word ) ,
2011-08-23 17:14:38 +00:00
)
2012-03-14 10:40:52 +00:00
def make_escaper ( badchars ) :
""" Creates an efficient escape function that strips the given characters from the string. """
translation_table = dict ( ( ord ( c ) , None ) for c in badchars )
def escaper ( text ) :
2013-05-01 14:46:10 +00:00
return force_text ( text , errors = " ignore " ) . translate ( translation_table )
2012-03-14 10:40:52 +00:00
return escaper
2011-08-23 17:14:38 +00:00
2013-05-01 15:28:46 +00:00
class SearchBackend ( six . with_metaclass ( abc . ABCMeta ) ) :
2011-08-20 17:08:00 +00:00
""" Base class for all search backends. """
2011-09-08 15:53:07 +00:00
def is_installed ( self ) :
""" Checks whether django-watson is installed. """
return True
2011-08-20 17:27:57 +00:00
def do_install ( self ) :
2011-09-08 15:53:07 +00:00
""" Executes the SQL needed to install django-watson. """
2011-08-23 16:12:35 +00:00
pass
2011-09-11 19:42:28 +00:00
def do_uninstall ( self ) :
""" Executes the SQL needed to uninstall django-watson. """
pass
2011-08-29 10:02:37 +00:00
2011-09-11 20:09:01 +00:00
requires_installation = False
2011-08-29 10:02:37 +00:00
supports_ranking = False
2012-02-28 16:58:01 +00:00
supports_prefix_matching = False
2011-08-23 17:14:38 +00:00
2011-08-29 10:51:40 +00:00
def do_search_ranking ( self , engine_slug , queryset , search_text ) :
2011-08-29 10:02:37 +00:00
""" Ranks the given queryset according to the relevance of the given search text. """
return queryset . extra (
select = {
2011-08-29 10:53:23 +00:00
" watson_rank " : " 1 " ,
2011-08-29 10:02:37 +00:00
} ,
)
2012-02-28 16:58:01 +00:00
@abc.abstractmethod
def do_search ( self , engine_slug , queryset , search_text ) :
""" Filters the given queryset according the the search logic for this backend. """
raise NotImplementedError
2012-02-28 16:40:01 +00:00
def do_filter_ranking ( self , engine_slug , queryset , search_text ) :
""" Ranks the given queryset according to the relevance of the given search text. """
return queryset . extra (
select = {
" watson_rank " : " 1 " ,
} ,
)
@abc.abstractmethod
def do_filter ( self , engine_slug , queryset , search_text ) :
""" Filters the given queryset according the the search logic for this backend. """
raise NotImplementedError
2013-05-01 14:46:10 +00:00
class RegexSearchMixin ( six . with_metaclass ( abc . ABCMeta ) ) :
2012-02-28 16:40:01 +00:00
2012-02-28 16:58:01 +00:00
""" Mixin to adding regex search to a search backend. """
2012-02-28 16:40:01 +00:00
2012-02-28 16:58:01 +00:00
supports_prefix_matching = True
def do_search ( self , engine_slug , queryset , search_text ) :
""" Filters the given queryset according the the search logic for this backend. """
word_query = Q ( )
for word in search_text . split ( ) :
regex = regex_from_word ( word )
word_query & = ( Q ( title__iregex = regex ) | Q ( description__iregex = regex ) | Q ( content__iregex = regex ) )
return queryset . filter (
word_query
)
2011-08-29 10:51:40 +00:00
def do_filter ( self , engine_slug , queryset , search_text ) :
2011-08-23 17:14:38 +00:00
""" Filters the given queryset according the the search logic for this backend. """
2012-02-06 13:21:58 +00:00
model = queryset . model
db_table = connection . ops . quote_name ( SearchEntry . _meta . db_table )
model_db_table = connection . ops . quote_name ( model . _meta . db_table )
pk = model . _meta . pk
id = connection . ops . quote_name ( pk . db_column or pk . attname )
# Add in basic filters.
word_query = [ u """
( { db_table } . { engine_slug } = % s )
""" , """
( { db_table } . { content_type_id } = % s )
""" ]
word_kwargs = {
u " db_table " : db_table ,
u " model_db_table " : model_db_table ,
u " engine_slug " : connection . ops . quote_name ( u " engine_slug " ) ,
u " title " : connection . ops . quote_name ( u " title " ) ,
u " description " : connection . ops . quote_name ( u " description " ) ,
u " content " : connection . ops . quote_name ( u " content " ) ,
u " content_type_id " : connection . ops . quote_name ( u " content_type_id " ) ,
u " object_id " : connection . ops . quote_name ( u " object_id " ) ,
u " object_id_int " : connection . ops . quote_name ( u " object_id_int " ) ,
u " id " : id ,
2012-02-28 16:58:01 +00:00
u " iregex_operator " : connection . operators [ " iregex " ] ,
2012-02-06 13:21:58 +00:00
}
word_args = [
engine_slug ,
ContentType . objects . get_for_model ( model ) . id ,
]
# Add in join.
if has_int_pk ( model ) :
word_query . append ( """
( { db_table } . { object_id_int } = { model_db_table } . { id } )
""" )
else :
word_query . append ( """
( { db_table } . { object_id } = { model_db_table } . { id } )
""" )
# Add in all words.
2011-08-24 12:36:26 +00:00
for word in search_text . split ( ) :
regex = regex_from_word ( word )
2012-02-06 13:21:58 +00:00
word_query . append ( u """
2012-02-28 16:58:01 +00:00
( { db_table } . { title } { iregex_operator } OR { db_table } . { description } { iregex_operator } OR { db_table } . { content } { iregex_operator } )
2012-02-06 13:21:58 +00:00
""" )
word_args . extend ( ( regex , regex , regex ) )
# Compile the query.
full_word_query = u " AND " . join ( word_query ) . format ( * * word_kwargs )
return queryset . extra (
tables = ( db_table , ) ,
where = ( full_word_query , ) ,
params = word_args ,
2011-08-23 17:14:38 +00:00
)
2012-02-28 15:20:45 +00:00
2012-02-28 16:58:01 +00:00
class RegexSearchBackend ( RegexSearchMixin , SearchBackend ) :
""" A search backend that works with SQLite3. """
2012-10-28 14:08:38 +00:00
escape_postgres_query_chars = make_escaper ( u " ():|!&* ' " )
2012-03-14 10:40:52 +00:00
2011-08-20 17:08:00 +00:00
class PostgresSearchBackend ( SearchBackend ) :
""" A search backend that uses native PostgreSQL full text indices. """
2012-04-19 23:09:24 +00:00
2012-04-20 10:24:43 +00:00
search_config = " pg_catalog.english "
2012-04-19 23:09:24 +00:00
""" Text search configuration to use in `to_tsvector` and `to_tsquery` functions """
2012-02-28 16:30:38 +00:00
def escape_postgres_query ( self , text ) :
""" Escapes the given text to become a valid ts_query. """
return u " & " . join (
2012-03-29 14:44:17 +00:00
u " {0} :* " . format ( word )
2012-02-28 16:30:38 +00:00
for word
2012-03-14 10:40:52 +00:00
in escape_postgres_query_chars ( text ) . split ( )
2012-02-28 16:30:38 +00:00
)
2011-09-08 15:53:07 +00:00
def is_installed ( self ) :
""" Checks whether django-watson is installed. """
cursor = connection . cursor ( )
cursor . execute ( """
SELECT attname FROM pg_attribute
WHERE attrelid = ( SELECT oid FROM pg_class WHERE relname = ' watson_searchentry ' ) AND attname = ' search_tsv ' ;
""" )
return bool ( cursor . fetchall ( ) )
2011-08-20 17:27:57 +00:00
def do_install ( self ) :
2011-09-08 15:53:07 +00:00
""" Executes the PostgreSQL specific SQL code to install django-watson. """
2011-08-21 16:38:04 +00:00
connection . cursor ( ) . execute ( """
2011-08-23 16:19:03 +00:00
- - Ensure that plpgsql is installed .
CREATE OR REPLACE FUNCTION make_plpgsql ( ) RETURNS VOID LANGUAGE SQL AS
$ $
CREATE LANGUAGE plpgsql ;
$ $ ;
SELECT
CASE
WHEN EXISTS (
SELECT 1
FROM pg_catalog . pg_language
WHERE lanname = ' plpgsql '
)
THEN NULL
ELSE make_plpgsql ( ) END ;
DROP FUNCTION make_plpgsql ( ) ;
2011-08-21 16:38:04 +00:00
2011-08-23 16:19:03 +00:00
- - Create the search index .
ALTER TABLE watson_searchentry ADD COLUMN search_tsv tsvector NOT NULL ;
CREATE INDEX watson_searchentry_search_tsv ON watson_searchentry USING gin ( search_tsv ) ;
2012-04-19 23:09:24 +00:00
2011-08-23 16:19:03 +00:00
- - Create the trigger function .
2012-11-22 01:00:47 +00:00
CREATE OR REPLACE FUNCTION watson_searchentry_trigger_handler ( ) RETURNS trigger AS $ $
2011-08-23 16:19:03 +00:00
begin
new . search_tsv :=
2012-04-20 10:24:43 +00:00
setweight ( to_tsvector ( ' {search_config} ' , coalesce ( new . title , ' ' ) ) , ' A ' ) | |
setweight ( to_tsvector ( ' {search_config} ' , coalesce ( new . description , ' ' ) ) , ' C ' ) | |
setweight ( to_tsvector ( ' {search_config} ' , coalesce ( new . content , ' ' ) ) , ' D ' ) ;
2011-08-23 16:19:03 +00:00
return new ;
end
$ $ LANGUAGE plpgsql ;
2011-08-29 12:14:22 +00:00
CREATE TRIGGER watson_searchentry_trigger BEFORE INSERT OR UPDATE
2011-08-23 16:19:03 +00:00
ON watson_searchentry FOR EACH ROW EXECUTE PROCEDURE watson_searchentry_trigger_handler ( ) ;
2012-04-19 23:09:24 +00:00
""" .format(
2012-04-20 10:24:43 +00:00
search_config = self . search_config
2012-04-19 23:09:24 +00:00
) )
2011-09-08 15:53:07 +00:00
def do_uninstall ( self ) :
""" Executes the PostgreSQL specific SQL code to uninstall django-watson. """
connection . cursor ( ) . execute ( """
ALTER TABLE watson_searchentry DROP COLUMN search_tsv ;
2012-04-19 23:09:24 +00:00
2011-09-08 15:53:07 +00:00
DROP TRIGGER watson_searchentry_trigger ON watson_searchentry ;
2012-04-19 23:09:24 +00:00
2011-09-08 15:53:07 +00:00
DROP FUNCTION watson_searchentry_trigger_handler ( ) ;
2011-08-21 16:38:04 +00:00
""" )
2011-09-11 20:09:01 +00:00
requires_installation = True
2011-08-29 10:02:37 +00:00
supports_ranking = True
2012-02-28 16:58:01 +00:00
supports_prefix_matching = True
2011-08-21 16:38:04 +00:00
2011-08-29 10:51:40 +00:00
def do_search ( self , engine_slug , queryset , search_text ) :
2011-08-21 16:38:04 +00:00
""" Performs the full text search. """
2011-08-29 10:02:37 +00:00
return queryset . extra (
2012-04-20 10:24:43 +00:00
where = ( " search_tsv @@ to_tsquery( ' {search_config} ' , %s ) " . format (
search_config = self . search_config
2012-04-19 23:09:24 +00:00
) , ) ,
2012-02-28 16:30:38 +00:00
params = ( self . escape_postgres_query ( search_text ) , ) ,
2011-08-29 10:02:37 +00:00
)
2011-08-29 10:51:40 +00:00
def do_search_ranking ( self , engine_slug , queryset , search_text ) :
2011-08-29 10:02:37 +00:00
""" Performs full text ranking. """
2011-08-21 16:38:04 +00:00
return queryset . extra (
select = {
2012-04-20 10:24:43 +00:00
" watson_rank " : " ts_rank_cd(watson_searchentry.search_tsv, to_tsquery( ' {search_config} ' , %s )) " . format (
search_config = self . search_config
2012-04-19 23:09:24 +00:00
) ,
2011-08-21 16:38:04 +00:00
} ,
2012-02-28 16:30:38 +00:00
select_params = ( self . escape_postgres_query ( search_text ) , ) ,
2011-08-29 10:02:37 +00:00
order_by = ( " -watson_rank " , ) ,
2011-08-23 17:22:17 +00:00
)
2011-08-29 10:51:40 +00:00
def do_filter ( self , engine_slug , queryset , search_text ) :
2011-08-23 17:22:17 +00:00
""" Performs the full text filter. """
model = queryset . model
2011-08-24 12:36:26 +00:00
content_type = ContentType . objects . get_for_model ( model )
2011-10-02 17:46:19 +00:00
pk = model . _meta . pk
2011-08-23 17:22:17 +00:00
if has_int_pk ( model ) :
ref_name = " object_id_int "
else :
ref_name = " object_id "
return queryset . extra (
tables = ( " watson_searchentry " , ) ,
where = (
2011-08-29 10:51:40 +00:00
" watson_searchentry.engine_slug = %s " ,
2012-04-20 10:24:43 +00:00
" watson_searchentry.search_tsv @@ to_tsquery( ' {search_config} ' , %s ) " . format (
search_config = self . search_config
2012-04-19 23:09:24 +00:00
) ,
2011-08-23 17:22:17 +00:00
" watson_searchentry. {ref_name} = {table_name} . {pk_name} " . format (
ref_name = ref_name ,
table_name = connection . ops . quote_name ( model . _meta . db_table ) ,
2011-10-02 17:46:19 +00:00
pk_name = connection . ops . quote_name ( pk . db_column or pk . attname ) ,
2011-08-23 17:22:17 +00:00
) ,
2011-08-24 12:36:26 +00:00
" watson_searchentry.content_type_id = %s "
2011-08-23 17:22:17 +00:00
) ,
2012-02-28 16:30:38 +00:00
params = ( engine_slug , self . escape_postgres_query ( search_text ) , content_type . id ) ,
2011-08-29 10:02:37 +00:00
)
2011-08-29 10:51:40 +00:00
def do_filter_ranking ( self , engine_slug , queryset , search_text ) :
2011-08-29 10:02:37 +00:00
""" Performs the full text ranking. """
return queryset . extra (
select = {
2012-04-20 10:24:43 +00:00
" watson_rank " : " ts_rank_cd(watson_searchentry.search_tsv, to_tsquery( ' {search_config} ' , %s )) " . format (
search_config = self . search_config
2012-04-19 23:09:24 +00:00
) ,
2011-08-29 10:02:37 +00:00
} ,
2012-02-28 16:30:38 +00:00
select_params = ( self . escape_postgres_query ( search_text ) , ) ,
2011-08-29 10:02:37 +00:00
order_by = ( " -watson_rank " , ) ,
2011-08-21 16:38:04 +00:00
)
2011-08-20 17:08:00 +00:00
2012-02-28 16:30:38 +00:00
class PostgresLegacySearchBackend ( PostgresSearchBackend ) :
"""
A search backend that uses native PostgreSQL full text indices .
This backend doesn ' t support prefix matching, and works with PostgreSQL 8.3 and below.
"""
supports_prefix_matching = False
def escape_postgres_query ( self , text ) :
""" Escapes the given text to become a valid ts_query. """
2012-03-14 10:40:52 +00:00
return u " & " . join ( escape_postgres_query_chars ( text ) . split ( ) )
2012-02-28 16:58:01 +00:00
class PostgresPrefixLegacySearchBackend ( RegexSearchMixin , PostgresLegacySearchBackend ) :
"""
A legacy search backend that uses a regexp to perform matches , but still allows
relevance rankings .
Use if your postgres vesion is less than 8.3 , and you absolutely can ' t live without
prefix matching . Beware , this backend can get slow with large datasets !
"""
2012-02-28 16:30:38 +00:00
2011-08-29 12:30:12 +00:00
2012-03-14 10:40:52 +00:00
escape_mysql_boolean_query_chars = make_escaper ( u " +-<>()* \" " )
2011-08-29 12:30:12 +00:00
def escape_mysql_boolean_query ( search_text ) :
return u " " . join (
2012-02-28 15:55:01 +00:00
u ' + {word} * ' . format (
word = word ,
2011-08-29 12:30:12 +00:00
)
2012-03-14 10:40:52 +00:00
for word in escape_mysql_boolean_query_chars ( search_text ) . split ( )
2011-08-29 12:30:12 +00:00
)
2011-08-29 12:14:22 +00:00
class MySQLSearchBackend ( SearchBackend ) :
2011-09-11 19:38:55 +00:00
def is_installed ( self ) :
""" Checks whether django-watson is installed. """
cursor = connection . cursor ( )
cursor . execute ( " SHOW INDEX FROM watson_searchentry WHERE Key_name = ' watson_searchentry_fulltext ' " ) ;
return bool ( cursor . fetchall ( ) )
2011-08-29 12:14:22 +00:00
def do_install ( self ) :
2011-09-11 19:42:28 +00:00
""" Executes the MySQL specific SQL code to install django-watson. """
2011-08-29 12:14:22 +00:00
cursor = connection . cursor ( )
# Drop all foreign keys on the watson_searchentry table.
cursor . execute ( " SELECT CONSTRAINT_NAME FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND TABLE_NAME = ' watson_searchentry ' AND CONSTRAINT_TYPE = ' FOREIGN KEY ' " )
for constraint_name , in cursor . fetchall ( ) :
cursor . execute ( " ALTER TABLE watson_searchentry DROP FOREIGN KEY {constraint_name} " . format (
constraint_name = constraint_name ,
) )
# Change the storage engine to MyISAM.
cursor . execute ( " ALTER TABLE watson_searchentry ENGINE = MyISAM " )
# Change the collaction to a case-insensitive one.
cursor . execute ( " ALTER TABLE watson_searchentry CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci " )
2011-09-11 19:42:28 +00:00
# Add the full text indexes.
2011-08-29 12:14:22 +00:00
cursor . execute ( " CREATE FULLTEXT INDEX watson_searchentry_fulltext ON watson_searchentry (title, description, content) " )
2011-08-29 18:18:27 +00:00
cursor . execute ( " CREATE FULLTEXT INDEX watson_searchentry_title ON watson_searchentry (title) " )
cursor . execute ( " CREATE FULLTEXT INDEX watson_searchentry_description ON watson_searchentry (description) " )
cursor . execute ( " CREATE FULLTEXT INDEX watson_searchentry_content ON watson_searchentry (content) " )
2011-08-29 12:14:22 +00:00
2011-09-11 19:42:28 +00:00
def do_uninstall ( self ) :
""" Executes the SQL needed to uninstall django-watson. """
cursor = connection . cursor ( )
# Destroy the full text indexes.
cursor . execute ( " DROP INDEX watson_searchentry_fulltext ON watson_searchentry " )
cursor . execute ( " DROP INDEX watson_searchentry_title ON watson_searchentry " )
cursor . execute ( " DROP INDEX watson_searchentry_description ON watson_searchentry " )
cursor . execute ( " DROP INDEX watson_searchentry_content ON watson_searchentry " )
2012-02-28 16:58:01 +00:00
supports_prefix_matching = True
2011-09-11 20:09:01 +00:00
requires_installation = True
2011-08-29 12:14:22 +00:00
supports_ranking = True
def do_search ( self , engine_slug , queryset , search_text ) :
""" Performs the full text search. """
return queryset . extra (
2011-08-29 12:30:12 +00:00
where = ( " MATCH (title, description, content) AGAINST ( %s IN BOOLEAN MODE) " , ) ,
params = ( escape_mysql_boolean_query ( search_text ) , ) ,
2011-08-29 12:14:22 +00:00
)
def do_search_ranking ( self , engine_slug , queryset , search_text ) :
""" Performs full text ranking. """
2011-08-29 18:18:27 +00:00
search_text = escape_mysql_boolean_query ( search_text )
2011-08-29 12:14:22 +00:00
return queryset . extra (
select = {
2011-08-29 18:18:27 +00:00
" watson_rank " : """
( ( MATCH ( title ) AGAINST ( % s IN BOOLEAN MODE ) ) * 3 ) +
( ( MATCH ( description ) AGAINST ( % s IN BOOLEAN MODE ) ) * 2 ) +
( ( MATCH ( content ) AGAINST ( % s IN BOOLEAN MODE ) ) * 1 )
""" ,
2011-08-29 12:14:22 +00:00
} ,
2011-08-29 18:18:27 +00:00
select_params = ( search_text , search_text , search_text , ) ,
2011-08-29 12:14:22 +00:00
order_by = ( " -watson_rank " , ) ,
)
def do_filter ( self , engine_slug , queryset , search_text ) :
""" Performs the full text filter. """
model = queryset . model
content_type = ContentType . objects . get_for_model ( model )
2011-10-02 17:46:19 +00:00
pk = model . _meta . pk
2011-08-29 12:14:22 +00:00
if has_int_pk ( model ) :
ref_name = " object_id_int "
else :
ref_name = " object_id "
return queryset . extra (
tables = ( " watson_searchentry " , ) ,
where = (
" watson_searchentry.engine_slug = %s " ,
2011-08-29 12:30:12 +00:00
" MATCH (watson_searchentry.title, watson_searchentry.description, watson_searchentry.content) AGAINST ( %s IN BOOLEAN MODE) " ,
2011-08-29 12:14:22 +00:00
" watson_searchentry. {ref_name} = {table_name} . {pk_name} " . format (
ref_name = ref_name ,
table_name = connection . ops . quote_name ( model . _meta . db_table ) ,
2011-10-02 17:46:19 +00:00
pk_name = connection . ops . quote_name ( pk . db_column or pk . attname ) ,
2011-08-29 12:14:22 +00:00
) ,
2011-08-29 12:30:12 +00:00
" watson_searchentry.content_type_id = %s " ,
2011-08-29 12:14:22 +00:00
) ,
2011-08-29 12:30:12 +00:00
params = ( engine_slug , escape_mysql_boolean_query ( search_text ) , content_type . id ) ,
2011-08-29 12:14:22 +00:00
)
def do_filter_ranking ( self , engine_slug , queryset , search_text ) :
""" Performs the full text ranking. """
2011-08-29 18:18:27 +00:00
search_text = escape_mysql_boolean_query ( search_text )
2011-08-29 12:14:22 +00:00
return queryset . extra (
select = {
2011-08-29 18:18:27 +00:00
" watson_rank " : """
( ( MATCH ( watson_searchentry . title ) AGAINST ( % s IN BOOLEAN MODE ) ) * 3 ) +
( ( MATCH ( watson_searchentry . description ) AGAINST ( % s IN BOOLEAN MODE ) ) * 2 ) +
( ( MATCH ( watson_searchentry . content ) AGAINST ( % s IN BOOLEAN MODE ) ) * 1 )
""" ,
2011-08-29 12:14:22 +00:00
} ,
2011-08-29 18:18:27 +00:00
select_params = ( search_text , search_text , search_text , ) ,
2011-08-29 12:14:22 +00:00
order_by = ( " -watson_rank " , ) ,
)
2012-03-14 10:56:35 +00:00
def get_postgresql_version ( connection ) :
""" Returns the version number of the PostgreSQL connection. """
try :
from django . db . backends . postgresql . version import get_version # Django 1.3
except ImportError :
# Use the Django 1.4 method.
from django . db . backends . postgresql_psycopg2 . version import get_version
return get_version ( connection )
else :
# Use the Django 1.3 method.
cursor = connection . cursor ( )
major , major2 , minor = get_version ( cursor )
return major * 10000 + major2 * 100 + minor
2011-08-29 12:14:22 +00:00
2011-08-20 17:08:00 +00:00
class AdaptiveSearchBackend ( SearchBackend ) :
"""
A search backend that guesses the correct search backend based on the
DATABASES [ " default " ] settings .
"""
def __new__ ( cls ) :
""" Guess the correct search backend and initialize it. """
2012-03-14 10:56:35 +00:00
if connection . vendor == " postgresql " :
version = get_postgresql_version ( connection )
if version > 80400 :
2012-02-28 16:30:38 +00:00
return PostgresSearchBackend ( )
2012-03-14 10:56:35 +00:00
if version > 80300 :
2012-02-28 16:30:38 +00:00
return PostgresLegacySearchBackend ( )
2012-03-14 10:56:35 +00:00
if connection . vendor == " mysql " :
2011-08-29 12:14:22 +00:00
return MySQLSearchBackend ( )
2012-04-19 23:09:24 +00:00
return RegexSearchBackend ( )