From b3529540074fc5782232fd56b399e9f26ac622ab Mon Sep 17 00:00:00 2001 From: Jonathan Payne Date: Fri, 27 Mar 2026 12:53:13 -0400 Subject: [PATCH] Add swagger documentation for $match endpoint Add @swagger_auto_schema decorator to MetadataToConceptsListView.post() with full query parameter definitions, request body schema, and response codes. Add 8 new $match-specific parameter definitions to swagger_parameters.py. Co-Authored-By: Claude Opus 4.6 (1M context) --- core/common/swagger_parameters.py | 34 ++++++++++++++++++++++ core/concepts/views.py | 48 ++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/core/common/swagger_parameters.py b/core/common/swagger_parameters.py index 3db0538c..eea78a1b 100644 --- a/core/common/swagger_parameters.py +++ b/core/common/swagger_parameters.py @@ -202,3 +202,37 @@ 'start_date', openapi.IN_QUERY, type=openapi.TYPE_STRING, format='YYYY-MM-DD', required=False, description='filter by start date of tasks' ) + +# $match params +match_semantic_param = openapi.Parameter( + 'semantic', openapi.IN_QUERY, type=openapi.TYPE_BOOLEAN, default=False, + description='Use semantic (LM-based) matching algorithm' +) +match_best_match_param = openapi.Parameter( + 'bestMatch', openapi.IN_QUERY, type=openapi.TYPE_BOOLEAN, default=False, + description='Apply minimum score threshold, filtering out low-quality matches' +) +match_num_candidates_param = openapi.Parameter( + 'numCandidates', openapi.IN_QUERY, type=openapi.TYPE_INTEGER, default=3000, + description='Number of approximate nearest neighbor candidates (semantic only). Max: 3000' +) +match_k_nearest_param = openapi.Parameter( + 'kNearest', openapi.IN_QUERY, type=openapi.TYPE_INTEGER, default=100, + description='Number of nearest neighbors to return from vector search (semantic only). Max: 100' +) +match_brief_param = openapi.Parameter( + 'brief', openapi.IN_QUERY, type=openapi.TYPE_BOOLEAN, default=False, + description='Return minimal concept fields in results' +) +match_encoder_model_param = openapi.Parameter( + 'encoder_model', openapi.IN_QUERY, type=openapi.TYPE_STRING, + description='Custom encoder model name for semantic vector search' +) +match_reranker_param = openapi.Parameter( + 'reranker', openapi.IN_QUERY, type=openapi.TYPE_BOOLEAN, default=False, + description='Enable cross-encoder reranking of results (semantic only)' +) +match_offset_param = openapi.Parameter( + 'offset', openapi.IN_QUERY, type=openapi.TYPE_INTEGER, default=0, + description='Number of results to skip per row' +) diff --git a/core/concepts/views.py b/core/concepts/views.py index 07a8bdd6..c544d574 100644 --- a/core/concepts/views.py +++ b/core/concepts/views.py @@ -4,6 +4,7 @@ from django.conf import settings from django.db.models import F from django.http import Http404 +from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from pydash import get, compact from rest_framework import status @@ -28,7 +29,9 @@ compress_header, include_source_versions_param, include_collection_versions_param, cascade_method_param, cascade_map_types_param, cascade_exclude_map_types_param, cascade_hierarchy_param, cascade_mappings_param, cascade_levels_param, cascade_direction_param, cascade_view_hierarchy, return_map_types_param, - omit_if_exists_in_param, equivalency_map_types_param, search_from_latest_repo_header) + omit_if_exists_in_param, equivalency_map_types_param, search_from_latest_repo_header, + match_semantic_param, match_best_match_param, match_num_candidates_param, match_k_nearest_param, + match_brief_param, match_encoder_model_param, match_reranker_param, match_offset_param) from core.common.tasks import delete_concept, make_hierarchy from core.common.throttling import ThrottleUtil from core.common.utils import (to_parent_uri_from_kwargs, generate_temp_version, get_truthy_values, to_int, @@ -933,6 +936,49 @@ def get_repo_params(is_semantic, target_repo_params, target_repo_url): raise Http400(f'Unable to resolve "target_repo_url": "{target_repo_url}"') return repo_params + @swagger_auto_schema( + operation_description='Find matching concepts across repositories using structured input data.', + operation_summary='$match - Find matching concepts', + manual_parameters=[ + verbose_param, include_retired_param, limit_param, page_param, match_offset_param, + match_semantic_param, match_best_match_param, match_num_candidates_param, + match_k_nearest_param, match_brief_param, match_encoder_model_param, match_reranker_param, + ], + request_body=openapi.Schema( + type=openapi.TYPE_OBJECT, + required=['rows'], + properties={ + 'target_repo_url': openapi.Schema( + type=openapi.TYPE_STRING, + description='Repository URL to match against. Either target_repo_url or target_repo is required.' + ), + 'target_repo': openapi.Schema( + type=openapi.TYPE_OBJECT, + description='Alternative to target_repo_url. Object with owner, source, source_version, ' + 'owner_type fields.' + ), + 'rows': openapi.Schema( + type=openapi.TYPE_ARRAY, + items=openapi.Schema(type=openapi.TYPE_OBJECT), + description='List of concept-like key-value pairs to match.' + ), + 'map_config': openapi.Schema( + type=openapi.TYPE_ARRAY, + items=openapi.Schema(type=openapi.TYPE_OBJECT), + description='Optional list configuring mapping logic per row.' + ), + 'filter': openapi.Schema( + type=openapi.TYPE_OBJECT, + description='Filtering criteria including locale and faceted filters.' + ), + } + ), + responses={ + 200: 'List of matched results per input row', + 400: 'Missing required parameters (rows, target_repo_url/target_repo)', + 403: 'User not approved for $match or on waitlist', + } + ) def post(self, request, **kwargs): # pylint: disable=unused-argument user = self.request.user if user.is_mapper_waitlisted or not user.is_mapper_approved: