add recommended as a sort, and default to recommended, downloads
This commit is contained in:
Родитель
983a751f68
Коммит
fa5ba91601
|
@ -79,13 +79,20 @@ This endpoint allows you to search through public add-ons.
|
|||
passed and when filtering to only return featured or
|
||||
recommended add-ons.
|
||||
rating Bayesian rating, descending.
|
||||
recommended Recommended add-ons above non-recommend add-ons. Only
|
||||
available combined with another sort - ignored on its own.
|
||||
relevance Search query relevance, descending.
|
||||
updated Last updated date, descending.
|
||||
users Average number of daily users, descending.
|
||||
============== ==========================================================
|
||||
|
||||
The default is to sort by relevance if a search query (``q``) is present,
|
||||
otherwise sort by number of weekly downloads, descending.
|
||||
The new default behavior is to sort by relevance if a search query (``q``)
|
||||
is present; otherwise place recommended add-ons first, then non recommended
|
||||
add-ons, then sorted by number of weekly downloads, descending. (``sort=recommended,downloads``).
|
||||
This is the default on AMO dev server.
|
||||
|
||||
The default on AMO production currently is to sort by relevance if a search
|
||||
query (``q``) is present; otherwise sort by number of weekly downloads, descending.
|
||||
|
||||
You can combine multiple parameters by separating them with a comma.
|
||||
For instance, to sort search results by downloads and then by creation
|
||||
|
|
|
@ -324,6 +324,8 @@ v4 API changelog
|
|||
* 2019-05-09: added ``is_recommended`` to addons API. https://github.com/mozilla/addons-server/issues/11278
|
||||
* 2019-05-16: added /reviewers/canned-responses/ endpoint. https://github.com/mozilla/addons-server/issues/11276
|
||||
* 2019-05-23: added ``is_recommended`` to addons autocomplete API also. https://github.com/mozilla/addons-server/issues/11439
|
||||
* 2019-05-23: changed the addons search API default sort when no query string is passed - now ``sort=recommended,downloads``.
|
||||
Also made ``recommended`` sort available generally to the addons search API. https://github.com/mozilla/addons-server/issues/11432
|
||||
|
||||
----------------
|
||||
v5 API changelog
|
||||
|
|
|
@ -13,6 +13,7 @@ from elasticsearch import Elasticsearch
|
|||
from unittest.mock import patch
|
||||
from rest_framework.test import APIRequestFactory
|
||||
from waffle import switch_is_active
|
||||
from waffle.testutils import override_switch
|
||||
|
||||
from olympia import amo
|
||||
from olympia.addons.models import (
|
||||
|
@ -1012,6 +1013,7 @@ class TestAddonSearchView(ESTestCase):
|
|||
|
||||
def perform_search(self, url, data=None, expected_status=200,
|
||||
expected_queries=0, **headers):
|
||||
switch_is_active('api-recommendations-priority') # just to cache it
|
||||
with self.assertNumQueries(expected_queries):
|
||||
response = self.client.get(url, data, **headers)
|
||||
assert response.status_code == expected_status, response.content
|
||||
|
@ -1834,6 +1836,43 @@ class TestAddonSearchView(ESTestCase):
|
|||
# No results, but no 500 either.
|
||||
assert data['count'] == 0
|
||||
|
||||
def test_with_recommended_addons(self):
|
||||
addon1 = addon_factory(weekly_downloads=666)
|
||||
addon2 = addon_factory(weekly_downloads=555)
|
||||
addon3 = addon_factory(weekly_downloads=444)
|
||||
addon4 = addon_factory(weekly_downloads=333)
|
||||
addon5 = addon_factory(weekly_downloads=222)
|
||||
self.refresh()
|
||||
|
||||
# Default case first - no recommended addons
|
||||
data = self.perform_search(self.url) # No query.
|
||||
|
||||
ids = [result['id'] for result in data['results']]
|
||||
assert ids == [addon1.id, addon2.id, addon3.id, addon4.id, addon5.id]
|
||||
|
||||
# Now made some of the add-ons recommended
|
||||
DiscoveryItem.objects.create(addon=addon2, recommendable=True)
|
||||
addon2.current_version.update(recommendation_approved=True)
|
||||
# del addon2.is_recommended
|
||||
DiscoveryItem.objects.create(addon=addon4, recommendable=True)
|
||||
addon4.current_version.update(recommendation_approved=True)
|
||||
# del addon4.is_recommended
|
||||
self.refresh()
|
||||
|
||||
data = self.perform_search(self.url) # No query.
|
||||
|
||||
ids = [result['id'] for result in data['results']]
|
||||
# addon2 and addon4 will be first because they're recommended
|
||||
assert ids == [addon2.id, addon4.id, addon1.id, addon3.id, addon5.id]
|
||||
|
||||
# But if the waffle is off the recommended add-ons don't have priority.
|
||||
with override_switch('api-recommendations-priority', active=False):
|
||||
assert not switch_is_active('api-recommendations-priority')
|
||||
data = self.perform_search(self.url) # No query.
|
||||
ids = [result['id'] for result in data['results']]
|
||||
assert ids == [
|
||||
addon1.id, addon2.id, addon3.id, addon4.id, addon5.id]
|
||||
|
||||
|
||||
class TestAddonAutoCompleteSearchView(ESTestCase):
|
||||
client_class = APITestClient
|
||||
|
@ -1966,6 +2005,7 @@ class TestAddonAutoCompleteSearchView(ESTestCase):
|
|||
self.refresh()
|
||||
|
||||
# page_size should be ignored, we should get 10 results.
|
||||
switch_is_active('api-recommendations-priority') # just to cache it
|
||||
data = self.perform_search(self.url, {'page_size': 1})
|
||||
assert 'count' not in data
|
||||
assert 'next' not in data
|
||||
|
|
|
@ -850,6 +850,7 @@ class SortingFilter(BaseFilterBackend):
|
|||
'name': 'name.raw',
|
||||
'random': '_score',
|
||||
'rating': '-bayesian_rating',
|
||||
'recommended': '-is_recommended',
|
||||
'relevance': '_score',
|
||||
'updated': '-last_updated',
|
||||
'users': '-average_daily_users',
|
||||
|
@ -862,11 +863,6 @@ class SortingFilter(BaseFilterBackend):
|
|||
|
||||
if sort_param is not None:
|
||||
split_sort_params = sort_param.split(',')
|
||||
try:
|
||||
order_by = [self.SORTING_PARAMS[name] for name in
|
||||
split_sort_params]
|
||||
except KeyError:
|
||||
raise serializers.ValidationError('Invalid "sort" parameter.')
|
||||
|
||||
# Random sort is a bit special.
|
||||
# First, it can't be combined with other sorts.
|
||||
|
@ -895,10 +891,24 @@ class SortingFilter(BaseFilterBackend):
|
|||
'when the "featured" or "recommended" parameter is '
|
||||
'also present, and the "q" parameter absent.')
|
||||
|
||||
# The default sort depends on the presence of a query: we sort by
|
||||
# relevance if we have a query, otherwise by downloads.
|
||||
if not order_by:
|
||||
sort_param = 'relevance' if search_query_param else 'downloads'
|
||||
order_by = [self.SORTING_PARAMS[sort_param]]
|
||||
# Having just recommended sort doesn't make any sense, so ignore it
|
||||
if sort_param == 'recommended':
|
||||
sort_param = None
|
||||
|
||||
if sort_param is None:
|
||||
# The default sort depends on the presence of a query: we sort by
|
||||
# relevance if we have a query, otherwise by recommended,downloads.
|
||||
recommended_waffle_on = switch_is_active(
|
||||
'api-recommendations-priority')
|
||||
split_sort_params = (
|
||||
['relevance'] if search_query_param else
|
||||
['recommended', 'downloads'] if recommended_waffle_on else
|
||||
['downloads'])
|
||||
|
||||
try:
|
||||
order_by = [self.SORTING_PARAMS[name] for name in
|
||||
split_sort_params]
|
||||
except KeyError:
|
||||
raise serializers.ValidationError('Invalid "sort" parameter.')
|
||||
|
||||
return qs.sort(*order_by)
|
||||
|
|
|
@ -377,16 +377,29 @@ class TestSortingFilter(FilterTestsBase):
|
|||
# queryset object.
|
||||
return {key[1:]: {'order': 'desc'}} if key.startswith('-') else key
|
||||
|
||||
@override_switch('api-recommendations-priority', active=False)
|
||||
def test_sort_default_recommendations_waffle_off(self):
|
||||
qs = self._filter(data={'q': 'something'})
|
||||
assert qs['sort'] == [self._reformat_order('_score')]
|
||||
|
||||
qs = self._filter()
|
||||
assert qs['sort'] == [
|
||||
self._reformat_order('-weekly_downloads')]
|
||||
|
||||
@override_switch('api-recommendations-priority', active=True)
|
||||
def test_sort_default(self):
|
||||
qs = self._filter(data={'q': 'something'})
|
||||
assert qs['sort'] == [self._reformat_order('_score')]
|
||||
|
||||
qs = self._filter()
|
||||
assert qs['sort'] == [self._reformat_order('-weekly_downloads')]
|
||||
assert qs['sort'] == [
|
||||
self._reformat_order('-is_recommended'),
|
||||
self._reformat_order('-weekly_downloads')]
|
||||
|
||||
def test_sort_query(self):
|
||||
SORTING_PARAMS = copy.copy(SortingFilter.SORTING_PARAMS)
|
||||
SORTING_PARAMS.pop('random') # Tested separately below.
|
||||
SORTING_PARAMS.pop('recommended') # Tested separately below.
|
||||
|
||||
for param in SORTING_PARAMS:
|
||||
qs = self._filter(data={'sort': param})
|
||||
|
@ -472,6 +485,17 @@ class TestSortingFilter(FilterTestsBase):
|
|||
{'random_score': {}}
|
||||
]
|
||||
|
||||
@override_switch('api-recommendations-priority', active=True)
|
||||
def test_sort_recommended_only(self):
|
||||
# If you try to sort by just recommended it gets ignored
|
||||
qs = self._filter(data={'q': 'something', 'sort': 'recommended'})
|
||||
assert qs['sort'] == [self._reformat_order('_score')]
|
||||
|
||||
qs = self._filter(data={'sort': 'recommended'})
|
||||
assert qs['sort'] == [
|
||||
self._reformat_order('-is_recommended'),
|
||||
self._reformat_order('-weekly_downloads')]
|
||||
|
||||
|
||||
class TestSearchParameterFilter(FilterTestsBase):
|
||||
filter_classes = [SearchParameterFilter]
|
||||
|
|
Загрузка…
Ссылка в новой задаче