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) pk = model._meta.pk 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", "watson_searchentry.search_tsv @@ to_tsquery('{search_config}', %s)".format( search_config = self.search_config ), "watson_searchentry.{ref_name} = {table_name}.{pk_name}".format( ref_name = ref_name, table_name = connection.ops.quote_name(model._meta.db_table), pk_name = connection.ops.quote_name(pk.db_column or pk.attname), ), "watson_searchentry.content_type_id = %s" ), params = (engine_slug, self.escape_postgres_query(search_text), content_type.id), )
def _create_model_filter(self, models): """Creates a filter for the given model/queryset list.""" filters = Q() for model in models: filter = Q() # Process querysets. if isinstance(model, QuerySet): sub_queryset = model model = model.model queryset = sub_queryset.values_list("pk", flat=True) if has_int_pk(model): filter &= Q( object_id_int__in = queryset, ) else: live_ids = list(queryset) if live_ids: filter &= Q( object_id__in = live_ids, ) else: # HACK: There is a bug in Django (https://code.djangoproject.com/ticket/15145) that messes up __in queries when the iterable is empty. # This bit of nonsense ensures that this aspect of the query will be impossible to fulfill. filter &= Q( content_type = ContentType.objects.get_for_model(model).id + 1, ) # Add the model to the filter. content_type = ContentType.objects.get_for_model(model) filter &= Q( content_type = content_type, ) # Combine with the other filters. filters |= filter return filters
def _create_model_filter(self, models, backend): """Creates a filter for the given model/queryset list.""" from django.contrib.contenttypes.models import ContentType from watson.models import has_int_pk filters = Q() for model in models: filter = Q() # Process querysets. if isinstance(model, QuerySet): sub_queryset = model model = model.model queryset = sub_queryset.values_list("pk", flat=True) if has_int_pk(model): filter &= Q(object_id_int__in=queryset, ) else: queryset = queryset.annotate(watson_pk_str=RawSQL( backend.do_string_cast( connections[queryset.db], model._meta.pk.db_column or model._meta.pk.attname, ), ()), ).values_list("watson_pk_str", flat=True) filter &= Q(object_id__in=queryset, ) # Add the model to the filter. content_type = ContentType.objects.get_for_model(model) filter &= Q(content_type=content_type, ) # Combine with the other filters. filters |= filter return filters
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) pk = model._meta.pk 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", "MATCH (watson_searchentry.title, watson_searchentry.description, watson_searchentry.content) AGAINST (%s IN BOOLEAN MODE)", "watson_searchentry.{ref_name} = {table_name}.{pk_name}". format( ref_name=ref_name, table_name=connection.ops.quote_name(model._meta.db_table), pk_name=connection.ops.quote_name(pk.db_column or pk.attname), ), "watson_searchentry.content_type_id = %s", ), params=(engine_slug, self._format_query(search_text), content_type.id), )
def _create_model_filter(self, models): """Creates a filter for the given model/queryset list.""" from django.contrib.contenttypes.models import ContentType from watson.models import has_int_pk filters = Q() for model in models: filter = Q() # Process querysets. if isinstance(model, QuerySet): sub_queryset = model model = model.model queryset = sub_queryset.values_list("pk", flat=True) if has_int_pk(model): filter &= Q(object_id_int__in=queryset, ) else: live_ids = list(queryset) if live_ids: filter &= Q(object_id__in=live_ids, ) else: # HACK: There is a bug in Django # (https://code.djangoproject.com/ticket/15145) # that messes up __in queries when the iterable is empty. # This bit of nonsense ensures that this aspect of the query # will be impossible to fulfill. filter &= Q( content_type=ContentType.objects.get_for_model( model).id + 1, ) # Add the model to the filter. content_type = ContentType.objects.get_for_model(model) filter &= Q(content_type=content_type, ) # Combine with the other filters. filters |= filter return filters
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) connection = connections[queryset.db] pk = model._meta.pk if has_int_pk(model): ref_name = "object_id_int" ref_name_typecast = "" else: ref_name = "object_id" # Cast to text to make join work with uuid columns ref_name_typecast = "::text" return queryset.extra( tables=("watson_searchentry",), where=( "watson_searchentry.engine_slug = %s", "watson_searchentry.search_tsv @@ to_tsquery('{search_config}', %s)".format( search_config=self.search_config ), "watson_searchentry.{ref_name} = {table_name}.{pk_name}{ref_name_typecast}".format( ref_name=ref_name, table_name=connection.ops.quote_name(model._meta.db_table), pk_name=connection.ops.quote_name(pk.db_column or pk.attname), ref_name_typecast=ref_name_typecast ), "watson_searchentry.content_type_id = %s" ), params=(engine_slug, self.escape_postgres_query(search_text), content_type.id), )
def _get_entries_for_obj(self, obj): """Returns a queryset of entries associate with the given obj.""" from django.contrib.contenttypes.models import ContentType model = obj.__class__ content_type = ContentType.objects.get_for_model(model) object_id = force_text(obj.pk) # Get the basic list of search entries. search_entries = SearchEntry.objects.filter( content_type = content_type, engine_slug = self._engine_slug, ) if has_int_pk(model): # Do a fast indexed lookup. object_id_int = int(obj.pk) search_entries = search_entries.filter( object_id_int = object_id_int, ) else: # Alas, have to do a slow unindexed lookup. object_id_int = None search_entries = search_entries.filter( object_id = object_id, ) return object_id_int, search_entries
def _create_model_filter(self, models, backend): """Creates a filter for the given model/queryset list.""" from django.contrib.contenttypes.models import ContentType from watson.models import has_int_pk filters = Q() for model in models: filter = Q() # Process querysets. if isinstance(model, QuerySet): sub_queryset = model model = model.model queryset = sub_queryset.values_list("pk", flat=True) if has_int_pk(model): filter &= Q( object_id_int__in=queryset, ) else: queryset = queryset.annotate( watson_pk_str=RawSQL(backend.do_string_cast( connections[queryset.db], model._meta.pk.db_column or model._meta.pk.attname, ), ()), ).values_list("watson_pk_str", flat=True) filter &= Q( object_id__in=queryset, ) # Add the model to the filter. content_type = ContentType.objects.get_for_model(model) filter &= Q( content_type=content_type, ) # Combine with the other filters. filters |= filter return filters
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) connection = connections[queryset.db] pk = model._meta.pk 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", "MATCH (watson_searchentry.title, watson_searchentry.description, watson_searchentry.content) " "AGAINST (%s IN BOOLEAN MODE)", "watson_searchentry.{ref_name} = {table_name}.{pk_name}".format( ref_name=ref_name, table_name=connection.ops.quote_name(model._meta.db_table), pk_name=connection.ops.quote_name(pk.db_column or pk.attname), ), "watson_searchentry.content_type_id = %s", ), params=(engine_slug, self._format_query(search_text), content_type.id), )
def _create_model_filter(self, models): """Creates a filter for the given model/queryset list.""" from django.contrib.contenttypes.models import ContentType from watson.models import has_int_pk filters = Q() for model in models: filter = Q() # Process querysets. if isinstance(model, QuerySet): sub_queryset = model model = model.model queryset = sub_queryset.values_list("pk", flat=True) if has_int_pk(model): filter &= Q( object_id_int__in=queryset, ) else: filter &= Q( object_id__in=queryset, ) # Add the model to the filter. content_type = ContentType.objects.get_for_model(model) filter &= Q( content_type=content_type, ) # Combine with the other filters. filters |= filter return filters
def do_filter(self, engine_slug, queryset, search_text): """Filters the given queryset according the the search logic for this backend.""" model = queryset.model connection = connections[queryset.db] 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 = [""" ({db_table}.{engine_slug} = %s) """, """ ({db_table}.{content_type_id} = %s) """] word_kwargs = { "db_table": db_table, "model_db_table": model_db_table, "engine_slug": connection.ops.quote_name("engine_slug"), "title": connection.ops.quote_name("title"), "description": connection.ops.quote_name("description"), "content": connection.ops.quote_name("content"), "content_type_id": connection.ops.quote_name("content_type_id"), "object_id": connection.ops.quote_name("object_id"), "object_id_int": connection.ops.quote_name("object_id_int"), "id": id, "iregex_operator": connection.operators["iregex"], } 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. for word in search_text.split(): regex = regex_from_word(word) word_query.append( """ ({db_table}.{title} {iregex_operator} OR {db_table}.{description} {iregex_operator} OR {db_table}.{content} {iregex_operator}) """ ) word_args.extend((regex, regex, regex)) # Compile the query. full_word_query = " AND ".join(word_query).format(**word_kwargs) return queryset.extra( tables=(db_table,), where=(full_word_query,), params=word_args, )
def _create_model_filter(self, models): """Creates a filter for the given model/queryset list.""" from django.contrib.contenttypes.models import ContentType from watson.models import has_int_pk filters = Q() for model in models: filter = Q() # Process querysets. if isinstance(model, QuerySet): sub_queryset = model model = model.model queryset = sub_queryset.values_list("pk", flat=True) if has_int_pk(model): filter &= Q(object_id_int__in=queryset, ) else: filter &= Q(object_id__in=queryset, ) # Add the model to the filter. content_type = ContentType.objects.get_for_model(model) filter &= Q(content_type=content_type, ) # Combine with the other filters. filters |= filter return filters
def register(self, model, adapter_cls=SearchAdapter, **field_overrides): """ Registers the given model with this search engine. If the given model is already registered with this search engine, a RegistrationError will be raised. """ # Add in custom live filters. if isinstance(model, QuerySet): live_queryset = model model = model.model field_overrides["get_live_queryset"] = lambda self_: live_queryset.all() # Check for existing registration. if self.is_registered(model): raise RegistrationError("{model!r} is already registered with this search engine".format( model = model, )) # Perform any customization. if field_overrides: adapter_cls = type("Custom" + adapter_cls.__name__, (adapter_cls,), field_overrides) # Perform the registration. adapter_obj = adapter_cls(model) self._registered_models[model] = adapter_obj # Add in a generic relation, if not exists. if not hasattr(model, "searchentry_set"): if has_int_pk(model): object_id_field = "object_id_int" else: object_id_field = "object_id" generic_relation = generic.GenericRelation( SearchEntry, object_id_field = object_id_field, ) model.searchentry_set = generic_relation generic_relation.contribute_to_class(model, "searchentry_set") # Connect to the signalling framework. post_save.connect(self._post_save_receiver, model) pre_delete.connect(self._pre_delete_receiver, model)