From 3bf9d7990dde286213cc7c78756d2a755b01d553 Mon Sep 17 00:00:00 2001 From: Darrel O'Pry Date: Wed, 8 Mar 2023 14:22:24 -0500 Subject: [PATCH] feat: add search operator and fields arguments allow for more specific search queries by specifying the search_operator and search_fields. --- grapple/types/structures.py | 28 ++++++++++++++++++++++ grapple/utils.py | 46 +++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/grapple/types/structures.py b/grapple/types/structures.py index 693f961f..18f6cc55 100644 --- a/grapple/types/structures.py +++ b/grapple/types/structures.py @@ -30,6 +30,8 @@ class QuerySetList(graphene.List): * ``limit`` * ``offset`` * ``search_query`` + * ``search_operator`` + * ``search_fields`` * ``order`` :param enable_limit: Enable limit argument. @@ -91,6 +93,19 @@ def __init__(self, of_type, *args, **kwargs): graphene.String, description=_("Filter the results using Wagtail's search."), ) + kwargs["search_operator"] = graphene.Argument( + graphene.Enum("SearchOperatorEnum", ["and", "or"]), + description=_( + "Specify search operator (and/or), see: https://docs.wagtail.org/en/stable/topics/search/searching.html#search-operator" + ), + default_value="and", + ) + kwargs["search_field"] = graphene.Argument( + graphene.List(graphene.String), + description=_( + "A list of fields to search in. see: https://docs.wagtail.org/en/stable/topics/search/searching.html#specifying-the-fields-to-search" + ), + ) if "id" not in kwargs: kwargs["id"] = graphene.Argument(graphene.ID, description=_("Filter by ID")) @@ -198,6 +213,19 @@ def PaginatedQuerySet(of_type, type_class, **kwargs): kwargs["search_query"] = graphene.Argument( graphene.String, description=_("Filter the results using Wagtail's search.") ) + kwargs["search_operator"] = graphene.Argument( + graphene.Enum("SearchOperatorEnum", ["and", "or"]), + description=_( + "Specify search operator (and/or), see: https://docs.wagtail.org/en/stable/topics/search/searching.html#search-operator" + ), + default_value="and", + ) + kwargs["search_field"] = graphene.Argument( + graphene.List(graphene.String), + description=_( + "A list of fields to search in. see: https://docs.wagtail.org/en/stable/topics/search/searching.html#specifying-the-fields-to-search" + ), + ) if "id" not in kwargs: kwargs["id"] = graphene.Argument(graphene.ID, description=_("Filter by ID")) diff --git a/grapple/utils.py b/grapple/utils.py index db252fbf..5e711b37 100644 --- a/grapple/utils.py +++ b/grapple/utils.py @@ -4,6 +4,7 @@ from wagtail.models import Site from wagtail.search.index import class_is_indexed from wagtail.search.models import Query +from wagtail.search.utils import parse_query_string from .settings import grapple_settings from .types.structures import BasePaginatedType, PaginationType @@ -62,6 +63,8 @@ def resolve_queryset( id=None, order=None, collection=None, + search_operator="and", + search_fields=None, **kwargs, ): """ @@ -83,6 +86,11 @@ def resolve_queryset( :type order: str :param collection: Use Wagtail's collection id to filter images or documents :type collection: int + :param search_operator: The operator to use when combining search terms. + Defaults to "and". + :type search_operator: "and" | "or" + :param search_fields: A list of fields to search. Defaults to all fields. + :type search_fields: list """ if id is not None: @@ -112,7 +120,14 @@ def resolve_queryset( query = Query.get(search_query) query.add_hit() - qs = qs.search(search_query, order_by_relevance=order_by_relevance) + filters, parsed_query = parse_query_string(search_query, search_operator) + + qs = qs.search( + parsed_query, + order_by_relevance=order_by_relevance, + operator=search_operator, + fields=search_fields, + ) if connection.vendor != "sqlite": qs = qs.annotate_score("search_score") @@ -153,7 +168,16 @@ def get_paginated_result(qs, page, per_page): def resolve_paginated_queryset( - qs, info, page=None, per_page=None, search_query=None, id=None, order=None, **kwargs + qs, + info, + page=None, + per_page=None, + id=None, + order=None, + search_query=None, + search_operator="and", + search_fields=None, + **kwargs, ): """ Add page, per_page and search capabilities to the query. This contains @@ -167,11 +191,16 @@ def resolve_paginated_queryset( :type id: int :param per_page: The maximum number of items to include on a page. :type per_page: int + :param order: Order the query set using the Django QuerySet order_by format. + :type order: str :param search_query: Using Wagtail search, exclude objects that do not match the search query. :type search_query: str - :param order: Order the query set using the Django QuerySet order_by format. - :type order: str + :param search_operator: The operator to use when combining search terms. + Defaults to "and". + :type search_operator: "and" | "or" + :param search_fields: A list of fields to search. Defaults to all fields. + :type search_fields: list """ page = int(page or 1) per_page = min( @@ -199,7 +228,14 @@ def resolve_paginated_queryset( query = Query.get(search_query) query.add_hit() - qs = qs.search(search_query, order_by_relevance=order_by_relevance) + filters, parsed_query = parse_query_string(search_query, search_operator) + + qs = qs.search( + parsed_query, + order_by_relevance=order_by_relevance, + operator=search_operator, + fields=search_fields, + ) if connection.vendor != "sqlite": qs = qs.annotate_score("search_score")