From 9aa685fc96e5529b664a9cb714078ed9c223ed30 Mon Sep 17 00:00:00 2001 From: charles Date: Tue, 10 Feb 2026 15:45:28 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(backend)=20handle=20sub-document=20fi?= =?UTF-8?q?lter=20and=20remove=20pagination?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I am adding the path params to the search endpoint. It represents the path to the parent. This allows searching in subdocs. I am also removing the pagination from the idnexer. We also want to remove db access. All necessary information are indexed in FInd. This leads to removing the pagination handled by the queryset. --- src/backend/core/api/serializers.py | 5 +- src/backend/core/api/viewsets.py | 56 ++++++++------------ src/backend/core/services/search_indexers.py | 13 +++-- 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/backend/core/api/serializers.py b/src/backend/core/api/serializers.py index a9bdd1b0..392c760f 100644 --- a/src/backend/core/api/serializers.py +++ b/src/backend/core/api/serializers.py @@ -1005,7 +1005,4 @@ class SearchDocumentSerializer(serializers.Serializer): """Serializer for fulltext search requests through Find application""" q = serializers.CharField(required=True, allow_blank=False, trim_whitespace=True) - page_size = serializers.IntegerField( - required=False, min_value=1, max_value=50, default=20 - ) - page = serializers.IntegerField(required=False, min_value=1, default=1) + path = serializers.CharField(required=False, allow_blank=False) diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 5d9bd360..916fce31 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -1402,7 +1402,7 @@ class DocumentViewSet( Returns a queryset filtered by the content of the document title """ # As the 'list' view we get a prefiltered queryset (deleted docs are excluded) - queryset = self.get_queryset() + queryset = models.Document.objects.all() filterset = DocumentFilter({"title": text}, queryset=queryset) if not filterset.is_valid(): @@ -1417,58 +1417,48 @@ class DocumentViewSet( }, ) - def _search_fulltext(self, indexer, request, params): + @staticmethod + def _search_with_indexer(indexer, request, params): """ - Returns a queryset from the results the fulltext search of Find + Returns a list of documents matching the query (q) according to the configured indexer. """ - access_token = request.session.get("oidc_access_token") - user = request.user - text = params.validated_data["q"] queryset = models.Document.objects.all() - # Retrieve the documents ids from Find. results = indexer.search( - text=text, - token=access_token, - visited=get_visited_document_ids_of(queryset, user), + q=params.validated_data["q"], + token=request.session.get("oidc_access_token"), + path=( + params.validated_data["path"] + if "path" in params.validated_data + else None + ), + visited=get_visited_document_ids_of(queryset, request.user), ) - docs_by_uuid = {str(d.pk): d for d in queryset.filter(pk__in=results)} - ordered_docs = [docs_by_uuid[id] for id in results] - - page = self.paginate_queryset(ordered_docs) - - serializer = self.get_serializer( - page if page else ordered_docs, - many=True, - context={ - "request": request, - }, + return drf_response.Response( + { + "count": len(results), + "next": None, + "previous": None, + "results": results, + } ) - return self.get_paginated_response(serializer.data) - @drf.decorators.action(detail=False, methods=["get"], url_path="search") @method_decorator(refresh_oidc_access_token) def search(self, request, *args, **kwargs): """ - Returns a DRF response containing the filtered, annotated and ordered document list. + Returns an ordered list of documents best matching the search query parameter 'q'. - Applies filtering based on request parameter 'q' from `SearchDocumentSerializer`. - Depending of the configuration it can be: - - A fulltext search through the opensearch indexation app "find" if the backend is - enabled (see SEARCH_INDEXER_CLASS) - - A filtering by the model field 'title'. - - The ordering is always by the most recent first. + It depends on a search configurable Search Indexer. If no Search Indexer is configured or if it + is not reachable, the function falls back to a basic title search. """ params = serializers.SearchDocumentSerializer(data=request.query_params) params.is_valid(raise_exception=True) indexer = get_document_indexer() - if indexer: - return self._search_fulltext(indexer, request, params=params) + return self._search_with_indexer(indexer, request, params=params) # The indexer is not configured, we fallback on a simple icontains filter by the # model field 'title'. diff --git a/src/backend/core/services/search_indexers.py b/src/backend/core/services/search_indexers.py index a4bb9eec..28efd890 100644 --- a/src/backend/core/services/search_indexers.py +++ b/src/backend/core/services/search_indexers.py @@ -185,7 +185,7 @@ class BaseDocumentIndexer(ABC): """ # pylint: disable-next=too-many-arguments,too-many-positional-arguments - def search(self, text, token, visited=(), nb_results=None): + def search(self, q, token, visited=(), nb_results=None, path=None): """ Search for documents in Find app. Ensure the same default ordering as "Docs" list : -updated_at @@ -193,7 +193,7 @@ class BaseDocumentIndexer(ABC): Returns ids of the documents Args: - text (str): Text search content. + q (str): Text search content. token (str): OIDC Authentication token. visited (list, optional): List of ids of active public documents with LinkTrace @@ -201,21 +201,24 @@ class BaseDocumentIndexer(ABC): nb_results (int, optional): The number of results to return. Defaults to 50 if not specified. + path (str, optional): + The path to filter documents. """ nb_results = nb_results or self.search_limit - response = self.search_query( + results = self.search_query( data={ - "q": text, + "q": q, "visited": visited, "services": ["docs"], "nb_results": nb_results, "order_by": "updated_at", "order_direction": "desc", + "path": path, }, token=token, ) - return [d["_id"] for d in response] + return results @abstractmethod def search_query(self, data, token) -> dict: