예제 #1
0
 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),
     )
예제 #2
0
 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
예제 #3
0
 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
예제 #4
0
 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),
     )
예제 #5
0
 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
예제 #6
0
    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),
        )
예제 #7
0
    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
예제 #8
0
 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
예제 #9
0
 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),
     )
예제 #10
0
 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
예제 #11
0
    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,
        )
예제 #12
0
    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,
        )
예제 #13
0
 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
예제 #14
0
 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)