Skip to content

SearchQuerySet search in db only for the last given word #1817

@mkaniaa

Description

@mkaniaa
  • Tested with the latest Haystack release
  • Tested with the current Haystack master branch

Expected behaviour

Passing to the haystack search engine string with spaces should create a query to the db using all of the specified words, combining everything into a single WHERE clause. The returned result should be all records that contain all those specified words.

Actual behaviour

Haystack creates query using only the last given word in the string. Escaping spaces or using python urllib.parse.quote() function doesn't change anything.

Example:

Model Category:

class Category(model.Model):
    name = models.CharField(_("Name"), max_length=255, db_index=True)
    description = models.TextField(_("Description"), blank=True)
    image = models.ImageField(_("Image"), upload_to="categories", blank=True, null=True, max_length=255)
    slug = SlugField(_("Slug"), max_length=255, db_index=True)
    path = models.CharField(_("Path"), max_length=255)

The WHERE clause in the query created based on the request response = api_client.get(f"{url}?q=word1 word2 word3") should look like this:

WHERE ((UPPER("catalogue_category"."path"::text) LIKE UPPER(\'%word1%\') 
OR UPPER("catalogue_category"."name"::text) LIKE UPPER(\'%word1%\') 
OR UPPER("catalogue_category"."description"::text) LIKE UPPER(\'%word1%\') 
OR UPPER("catalogue_category"."slug"::text) LIKE UPPER(\'%word1%\')) 
AND 
(UPPER("catalogue_category"."path"::text) LIKE UPPER(\'%word2%\') 
OR UPPER("catalogue_category"."name"::text) LIKE UPPER(\'%word2%\') 
OR UPPER("catalogue_category"."description"::text) LIKE UPPER(\'%word2%\') 
OR UPPER("catalogue_category"."slug"::text) LIKE UPPER(\'%word2%\')) 
AND 
(UPPER("catalogue_category"."path"::text) LIKE UPPER(\'%word3%\') 
OR UPPER("catalogue_category"."name"::text) LIKE UPPER(\'%word3%\') 
OR UPPER("catalogue_category"."description"::text) LIKE UPPER(\'%word3%\') 
OR UPPER("catalogue_category"."slug"::text) LIKE UPPER(\'%word3%\')))`

but it actually look like this:

WHERE (UPPER("catalogue_category"."path"::text) LIKE UPPER(\'%word3%\') 
OR UPPER("catalogue_category"."name"::text) LIKE UPPER(\'%word3%\') 
OR UPPER("catalogue_category"."description"::text) LIKE UPPER(\'%word3%\') 
OR UPPER("catalogue_category"."slug"::text) LIKE UPPER(\'%word3%\'))

Steps to reproduce the behaviour

  1. Set a class using the SearchQuerySet from haystack to filter objects from db. Example of use:

    def filter_queryset(
        self, request: Request, queryset: "QuerySet[Model]", view: GenericViewSet
    ) -> "QuerySet[Model]":
    
        sqs = SearchQuerySet().models(queryset.model)
    
        # Get search parameters
        param = request.query_params.get(self.search_param_name)
        if not param:
            return queryset
    
        results = [i.object.pk for i in sqs.filter(content=param)]
    
        # Return results as a QuerySet
        return queryset.filter(id__in=results)
    
  2. Set the backend of SimpleSearchBackend:

      HAYSTACK_CONNECTIONS = {"default": {"ENGINE": "haystack.backends.simple_backend.SimpleEngine"}}
    
  3. Use this to filter objects by a string containing more than one word separated by spaces (string can be quoted).

  4. Check the created SQL query or results, trying to get objects that match not only the last word but all the specified words. Instead of filtering out objects that do not contain all of the specified words, the query will only filter out objects that do not contain the last word in the string.

Configuration

  • Operating system version: macOS Big Sur v11.6
  • Search engine version: n/a
  • Python version: 3.8.12
  • Django version: 3.2.2
  • Haystack version: 3.1.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions