diff --git a/frontends/api/src/generated/api.ts b/frontends/api/src/generated/api.ts index 5819ddc94f..8c3c2fbc29 100644 --- a/frontends/api/src/generated/api.ts +++ b/frontends/api/src/generated/api.ts @@ -9602,7 +9602,7 @@ export const LearningResourcesSearchApiAxiosParamCreator = function ( * @param {string} [q] The search text * @param {Array} [resource_content_tags] * @param {Array<'course' | 'program' | 'learning path' | 'podcast' | 'podcast episode'>} [resource_type] - * @param {'id' | '-id' | 'readable_id' | '-readable_id' | 'last_modified' | '-last_modified' | 'runs.start_date' | '-runs.start_date'} [sortby] if the parameter starts with \'-\' the sort is in descending order * `id` - id * `-id` - -id * `readable_id` - readable_id * `-readable_id` - -readable_id * `last_modified` - last_modified * `-last_modified` - -last_modified * `runs.start_date` - runs.start_date * `-runs.start_date` - -runs.start_date + * @param {'id' | '-id' | 'readable_id' | '-readable_id' | 'last_modified' | '-last_modified' | 'start_date' | '-start_date' | 'mitcoursenumber' | '-mitcoursenumber'} [sortby] if the parameter starts with \'-\' the sort is in descending order * `id` - Object ID ascending * `-id` - Object ID descending * `readable_id` - Readable ID ascending * `-readable_id` - Readable ID descending * `last_modified` - Last Modified Date ascending * `-last_modified` - Last Modified Date descending * `start_date` - Start Date ascending * `-start_date` - Start Date descending * `mitcoursenumber` - MIT course number ascending * `-mitcoursenumber` - MIT course number descending * @param {Array} [topic] * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -9668,8 +9668,10 @@ export const LearningResourcesSearchApiAxiosParamCreator = function ( | "-readable_id" | "last_modified" | "-last_modified" - | "runs.start_date" - | "-runs.start_date", + | "start_date" + | "-start_date" + | "mitcoursenumber" + | "-mitcoursenumber", topic?: Array, options: AxiosRequestConfig = {}, ): Promise => { @@ -9793,7 +9795,7 @@ export const LearningResourcesSearchApiFp = function ( * @param {string} [q] The search text * @param {Array} [resource_content_tags] * @param {Array<'course' | 'program' | 'learning path' | 'podcast' | 'podcast episode'>} [resource_type] - * @param {'id' | '-id' | 'readable_id' | '-readable_id' | 'last_modified' | '-last_modified' | 'runs.start_date' | '-runs.start_date'} [sortby] if the parameter starts with \'-\' the sort is in descending order * `id` - id * `-id` - -id * `readable_id` - readable_id * `-readable_id` - -readable_id * `last_modified` - last_modified * `-last_modified` - -last_modified * `runs.start_date` - runs.start_date * `-runs.start_date` - -runs.start_date + * @param {'id' | '-id' | 'readable_id' | '-readable_id' | 'last_modified' | '-last_modified' | 'start_date' | '-start_date' | 'mitcoursenumber' | '-mitcoursenumber'} [sortby] if the parameter starts with \'-\' the sort is in descending order * `id` - Object ID ascending * `-id` - Object ID descending * `readable_id` - Readable ID ascending * `-readable_id` - Readable ID descending * `last_modified` - Last Modified Date ascending * `-last_modified` - Last Modified Date descending * `start_date` - Start Date ascending * `-start_date` - Start Date descending * `mitcoursenumber` - MIT course number ascending * `-mitcoursenumber` - MIT course number descending * @param {Array} [topic] * @param {*} [options] Override http request option. * @throws {RequiredError} @@ -9859,8 +9861,10 @@ export const LearningResourcesSearchApiFp = function ( | "-readable_id" | "last_modified" | "-last_modified" - | "runs.start_date" - | "-runs.start_date", + | "start_date" + | "-start_date" + | "mitcoursenumber" + | "-mitcoursenumber", topic?: Array, options?: AxiosRequestConfig, ): Promise< @@ -10078,8 +10082,8 @@ export interface LearningResourcesSearchApiLearningResourcesSearchRetrieveReques > /** - * if the parameter starts with \'-\' the sort is in descending order * `id` - id * `-id` - -id * `readable_id` - readable_id * `-readable_id` - -readable_id * `last_modified` - last_modified * `-last_modified` - -last_modified * `runs.start_date` - runs.start_date * `-runs.start_date` - -runs.start_date - * @type {'id' | '-id' | 'readable_id' | '-readable_id' | 'last_modified' | '-last_modified' | 'runs.start_date' | '-runs.start_date'} + * if the parameter starts with \'-\' the sort is in descending order * `id` - Object ID ascending * `-id` - Object ID descending * `readable_id` - Readable ID ascending * `-readable_id` - Readable ID descending * `last_modified` - Last Modified Date ascending * `-last_modified` - Last Modified Date descending * `start_date` - Start Date ascending * `-start_date` - Start Date descending * `mitcoursenumber` - MIT course number ascending * `-mitcoursenumber` - MIT course number descending + * @type {'id' | '-id' | 'readable_id' | '-readable_id' | 'last_modified' | '-last_modified' | 'start_date' | '-start_date' | 'mitcoursenumber' | '-mitcoursenumber'} * @memberof LearningResourcesSearchApiLearningResourcesSearchRetrieve */ readonly sortby?: @@ -10089,8 +10093,10 @@ export interface LearningResourcesSearchApiLearningResourcesSearchRetrieveReques | "-readable_id" | "last_modified" | "-last_modified" - | "runs.start_date" - | "-runs.start_date" + | "start_date" + | "-start_date" + | "mitcoursenumber" + | "-mitcoursenumber" /** * diff --git a/learning_resources_search/api.py b/learning_resources_search/api.py index cf9762dcee..5bde392f75 100644 --- a/learning_resources_search/api.py +++ b/learning_resources_search/api.py @@ -12,6 +12,7 @@ DEPARTMENT_QUERY_FIELDS, LEARNING_RESOURCE_QUERY_FIELDS, LEARNING_RESOURCE_SEARCH_FILTERS, + LEARNING_RESOURCE_SORTBY_OPTIONS, LEARNING_RESOURCE_TYPES, RESOURCEFILE_QUERY_FIELDS, RUN_INSTRUCTORS_QUERY_FIELDS, @@ -22,6 +23,7 @@ ) LEARN_SUGGEST_FIELDS = ["title.trigram", "description.trigram"] +COURSENUM_SORT_FIELD = "course.course_numbers.sort_coursenum" def gen_content_file_id(content_file_id): @@ -62,16 +64,21 @@ def relevant_indexes(resource_types, aggregations): return map(get_default_alias_name, set(resource_types_copy)) -def generate_sort_clause(sort): +def generate_sort_clause(search_params): """ Return sort clause for the query Args: - sort (string): the sort parameter + sort (dict): the search params Returns: dict or String: either a dictionary with the sort clause for nested sort params or just sort parameter """ + sort = LEARNING_RESOURCE_SORTBY_OPTIONS.get(search_params.get("sortby"), {}).get( + "sort" + ) + + departments = search_params.get("department") if "." in sort: if sort.startswith("-"): @@ -83,7 +90,26 @@ def generate_sort_clause(sort): path = ".".join(field.split(".")[:-1]) - return {field: {"order": direction, "nested": {"path": path}}} + sort_filter = {} + if field == COURSENUM_SORT_FIELD: + if departments: + sort_filter = { + "filter": { + "bool": { + "should": [ + { + "term": { + f"{path}.department.department_id": department + } + } + for department in departments + ] + } + } + } + else: + sort_filter = {"filter": {"term": {f"{path}.primary": True}}} + return {field: {"order": direction, "nested": {"path": path, **sort_filter}}} else: return sort @@ -439,7 +465,7 @@ def execute_learn_search(search_params): search = search.extra(size=search_params.get("limit")) if search_params.get("sortby"): - sort = generate_sort_clause(search_params.get("sortby")) + sort = generate_sort_clause(search_params) search = search.sort(sort) diff --git a/learning_resources_search/api_test.py b/learning_resources_search/api_test.py index 4c1d45a9e4..c32a16ee2d 100644 --- a/learning_resources_search/api_test.py +++ b/learning_resources_search/api_test.py @@ -33,22 +33,70 @@ def test_relevant_indexes(resourse_types, aggregations, result): @pytest.mark.parametrize( - ("sort_param", "result"), + ("sort_param", "departments", "result"), [ - ("prices", "prices"), - ("-prices", "-prices"), + ("id", None, "id"), + ("-id", ["7"], "-id"), ( - "runs.start_date", + "start_date", + ["5"], {"runs.start_date": {"order": "asc", "nested": {"path": "runs"}}}, ), ( - "-runs.start_date", + "-start_date", + None, {"runs.start_date": {"order": "desc", "nested": {"path": "runs"}}}, ), + ( + "mitcoursenumber", + None, + { + "course.course_numbers.sort_coursenum": { + "order": "asc", + "nested": { + "path": "course.course_numbers", + "filter": {"term": {"course.course_numbers.primary": True}}, + }, + } + }, + ), + ( + "mitcoursenumber", + ["7", "5"], + { + "course.course_numbers.sort_coursenum": { + "order": "asc", + "nested": { + "path": "course.course_numbers", + "filter": { + "bool": { + "should": [ + { + "term": { + "course.course_numbers.department.department_id": ( + "7" + ) + } + }, + { + "term": { + "course.course_numbers.department.department_id": ( + "5" + ) + } + }, + ] + } + }, + }, + } + }, + ), ], ) -def test_generate_sort_clause(sort_param, result): - assert generate_sort_clause(sort_param) == result +def test_generate_sort_clause(sort_param, departments, result): + params = {"sortby": sort_param, "department": departments} + assert generate_sort_clause(params) == result def test_generate_learning_resources_text_clause(): @@ -93,7 +141,9 @@ def test_generate_learning_resources_text_clause(): "query": { "multi_match": { "query": "math", - "fields": ["departments.name"], + "fields": [ + "departments.department_id" + ], } }, } @@ -208,7 +258,7 @@ def test_generate_learning_resources_text_clause(): "query": { "multi_match": { "query": "math", - "fields": ["departments.name"], + "fields": ["departments.department_id"], } }, } @@ -323,7 +373,9 @@ def test_generate_learning_resources_text_clause(): "query": { "query_string": { "query": '"math"', - "fields": ["departments.name"], + "fields": [ + "departments.department_id" + ], } }, } @@ -441,7 +493,7 @@ def test_generate_learning_resources_text_clause(): "query": { "query_string": { "query": '"math"', - "fields": ["departments.name"], + "fields": ["departments.department_id"], } }, } @@ -545,7 +597,9 @@ def test_generate_content_file_text_clause(): "query": { "multi_match": { "query": "math", - "fields": ["departments.name"], + "fields": [ + "departments.department_id" + ], } }, } @@ -574,7 +628,7 @@ def test_generate_content_file_text_clause(): "query": { "multi_match": { "query": "math", - "fields": ["departments.name"], + "fields": ["departments.department_id"], } }, } @@ -607,7 +661,9 @@ def test_generate_content_file_text_clause(): "query": { "query_string": { "query": '"math"', - "fields": ["departments.name"], + "fields": [ + "departments.department_id" + ], } }, } @@ -636,7 +692,7 @@ def test_generate_content_file_text_clause(): "query": { "query_string": { "query": '"math"', - "fields": ["departments.name"], + "fields": ["departments.department_id"], } }, } @@ -804,7 +860,7 @@ def test_execute_learn_search(opensearch): "resource_type": ["course"], "limit": 1, "offset": 1, - "sortby": "prices", + "sortby": "-readable_id", } query = { @@ -857,7 +913,7 @@ def test_execute_learn_search(opensearch): "multi_match": { "query": "math", "fields": [ - "departments.name" + "departments.department_id" ], } }, @@ -979,7 +1035,7 @@ def test_execute_learn_search(opensearch): "query": { "multi_match": { "query": "math", - "fields": ["departments.name"], + "fields": ["departments.department_id"], } }, } @@ -1083,7 +1139,7 @@ def test_execute_learn_search(opensearch): ] } }, - "sort": ["prices"], + "sort": [{"readable_id": {"order": "desc"}}], "from": 1, "size": 1, "suggest": { diff --git a/learning_resources_search/constants.py b/learning_resources_search/constants.py index bc3970638b..0f18d7de92 100644 --- a/learning_resources_search/constants.py +++ b/learning_resources_search/constants.py @@ -58,7 +58,7 @@ class IndexestoUpdate(Enum): SEARCH_NESTED_FILTERS = { "topic": "topics.name", "level": "runs.level", - "department": "departments.name", + "department": "departments.department_id", } ENGLISH_TEXT_FIELD = { @@ -214,7 +214,7 @@ class IndexestoUpdate(Enum): ] TOPICS_QUERY_FIELDS = ["topics.name"] -DEPARTMENT_QUERY_FIELDS = ["departments.name"] +DEPARTMENT_QUERY_FIELDS = ["departments.department_id"] COURSE_QUERY_FIELDS = [ "course.course_numbers.value", @@ -251,3 +251,46 @@ class IndexestoUpdate(Enum): "course.course_numbers.primary", "resource_relations", ] + +LEARNING_RESOURCE_SORTBY_OPTIONS = { + "id": { + "title": "Object ID ascending", + "sort": "id", + }, + "-id": { + "title": "Object ID descending", + "sort": "-id", + }, + "readable_id": { + "title": "Readable ID ascending", + "sort": "readable_id", + }, + "-readable_id": { + "title": "Readable ID descending", + "sort": "-readable_id", + }, + "last_modified": { + "title": "Last Modified Date ascending", + "sort": "last_modified", + }, + "-last_modified": { + "title": "Last Modified Date descending", + "sort": "-last_modified", + }, + "start_date": { + "title": "Start Date ascending", + "sort": "runs.start_date", + }, + "-start_date": { + "title": "Start Date descending", + "sort": "-runs.start_date", + }, + "mitcoursenumber": { + "title": "MIT course number ascending", + "sort": "course.course_numbers.sort_coursenum", + }, + "-mitcoursenumber": { + "title": "MIT course number descending", + "sort": "-course.course_numbers.sort_coursenum", + }, +} diff --git a/learning_resources_search/serializers.py b/learning_resources_search/serializers.py index aab63258e7..a7153d73a8 100644 --- a/learning_resources_search/serializers.py +++ b/learning_resources_search/serializers.py @@ -17,6 +17,7 @@ from learning_resources_search.api import gen_content_file_id from learning_resources_search.constants import ( CONTENT_FILE_TYPE, + LEARNING_RESOURCE_SORTBY_OPTIONS, LEARNING_RESOURCE_TYPES, ) @@ -128,17 +129,6 @@ def to_representation(self, data): return data.split(",") -LEARNING_RESOURCE_SORTBY_OPTIONS = [ - "id", - "-id", - "readable_id", - "-readable_id", - "last_modified", - "-last_modified", - "runs.start_date", - "-runs.start_date", -] - CONTENT_FILE_SORTBY_OPTIONS = [ "id", "-id", @@ -171,7 +161,10 @@ class SearchRequestSerializer(serializers.Serializer): class LearningResourcesSearchRequestSerializer(SearchRequestSerializer): sortby = serializers.ChoiceField( required=False, - choices=LEARNING_RESOURCE_SORTBY_OPTIONS, + choices=[ + (key, LEARNING_RESOURCE_SORTBY_OPTIONS[key]["title"]) + for key in LEARNING_RESOURCE_SORTBY_OPTIONS + ], help_text="if the parameter starts with '-' the sort is in descending order", ) resource_type = StringArrayField( diff --git a/learning_resources_search/serializers_test.py b/learning_resources_search/serializers_test.py index 9d51a9c7db..09a8c04cde 100644 --- a/learning_resources_search/serializers_test.py +++ b/learning_resources_search/serializers_test.py @@ -193,13 +193,13 @@ def test_learning_resources_search_request_serializer(): "q": "text", "offset": 1, "limit": 1, - "sortby": "-runs.start_date", + "sortby": "-start_date", "professional": "true", "certification": "Certificates", "offered_by": "xpro,ocw", "platform": "xpro,edx,ocw", "topic": "Math", - "department": "mathematics,chemistry", + "department": "18,5", "level": "Undergraduate", "resource_content_tags": "Lecture Videos", "aggregations": "resource_type,platform,level", @@ -210,14 +210,14 @@ def test_learning_resources_search_request_serializer(): "q": "text", "offset": 1, "limit": 1, - "sortby": "-runs.start_date", + "sortby": "-start_date", "resource_type": ["course", "program"], "professional": ["true"], "certification": ["Certificates"], "offered_by": ["xpro", "ocw"], "platform": ["xpro", "edx", "ocw"], "topic": ["Math"], - "department": ["mathematics", "chemistry"], + "department": ["18", "5"], "level": ["Undergraduate"], "resource_content_tags": ["Lecture Videos"], "aggregations": ["resource_type", "platform", "level"], diff --git a/openapi.yaml b/openapi.yaml index e33522f21a..36f0f3ad6b 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2240,21 +2240,25 @@ paths: - -readable_id - last_modified - -last_modified - - runs.start_date - - -runs.start_date + - start_date + - -start_date + - mitcoursenumber + - -mitcoursenumber type: string minLength: 1 description: |- if the parameter starts with '-' the sort is in descending order - * `id` - id - * `-id` - -id - * `readable_id` - readable_id - * `-readable_id` - -readable_id - * `last_modified` - last_modified - * `-last_modified` - -last_modified - * `runs.start_date` - runs.start_date - * `-runs.start_date` - -runs.start_date + * `id` - Object ID ascending + * `-id` - Object ID descending + * `readable_id` - Readable ID ascending + * `-readable_id` - Readable ID descending + * `last_modified` - Last Modified Date ascending + * `-last_modified` - Last Modified Date descending + * `start_date` - Start Date ascending + * `-start_date` - Start Date descending + * `mitcoursenumber` - MIT course number ascending + * `-mitcoursenumber` - MIT course number descending - in: query name: topic schema: