🐛(api) return best matching identity only

Use best matching identity to order results.
This commit is contained in:
Samuel Paccoud - DINUM
2024-01-31 07:33:05 +01:00
committed by Marie
parent 3aba9a4419
commit a4ac5304d7
2 changed files with 37 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
"""API endpoints""" """API endpoints"""
from django.contrib.postgres.search import TrigramSimilarity from django.contrib.postgres.search import TrigramSimilarity
from django.db.models import Func, OuterRef, Q, Subquery, Value from django.db.models import Func, Max, OuterRef, Q, Subquery, Value
from rest_framework import ( from rest_framework import (
decorators, decorators,
@@ -202,9 +202,11 @@ class UserViewSet(
# Search by case-insensitive and accent-insensitive trigram similarity # Search by case-insensitive and accent-insensitive trigram similarity
if query := self.request.GET.get("q", ""): if query := self.request.GET.get("q", ""):
similarity = TrigramSimilarity( similarity = Max(
Func("identities__email", function="unaccent"), TrigramSimilarity(
Func(Value(query), function="unaccent"), Func("identities__email", function="unaccent"),
Func(Value(query), function="unaccent"),
)
) )
queryset = ( queryset = (
queryset.annotate(similarity=similarity) queryset.annotate(similarity=similarity)

View File

@@ -100,10 +100,9 @@ def test_api_users_authenticated_list_by_email():
assert user_ids == [str(nicole.user.id), str(frank.user.id)] assert user_ids == [str(nicole.user.id), str(frank.user.id)]
def test_api_users_authenticated_list_multiplie_identities_user(): def test_api_users_authenticated_list_multiple_identities_single_user():
""" """
Authenticated users should be able to search users with a case-insensitive and User with multiple identities should appear only once in results.
partial query on the email.
""" """
user = factories.UserFactory(email="tester@ministry.fr") user = factories.UserFactory(email="tester@ministry.fr")
factories.IdentityFactory(user=user, email=user.email) factories.IdentityFactory(user=user, email=user.email)
@@ -122,8 +121,36 @@ def test_api_users_authenticated_list_multiplie_identities_user():
assert response.status_code == HTTP_200_OK assert response.status_code == HTTP_200_OK
# A single user is returned, despite similarity matching both emails # A single user is returned, despite similarity matching both emails
assert response.json()["count"] == 1 assert response.json()["count"] == 1
assert response.json()["results"][0]["id"] == str(dave.id)
def test_api_users_authenticated_list_multiple_identities_best_result():
"""
User with multiple identities should be ranked
on their best matching identities.
"""
user = factories.UserFactory(email="tester@ministry.fr")
factories.IdentityFactory(user=user, email=user.email)
jwt_token = OIDCToken.for_user(user)
dave = factories.UserFactory()
davina = factories.UserFactory()
prudence = factories.UserFactory()
factories.IdentityFactory(user=dave, email="david.bowman@work.com")
factories.IdentityFactory(user=dave, email="babibou@ehehe.com")
factories.IdentityFactory(user=davina, email="davina.bowan@work.com")
factories.IdentityFactory(user=prudence, email="prudence.crandall@work.com")
# Full query should work
response = APIClient().get(
"/api/v1.0/users/?q=david.bowman@work.com",
HTTP_AUTHORIZATION=f"Bearer {jwt_token}",
)
assert response.status_code == HTTP_200_OK
assert response.json()["count"] == 3
user_ids = [user["id"] for user in response.json()["results"]] user_ids = [user["id"] for user in response.json()["results"]]
assert user_ids[0] == str(dave.id) assert user_ids == [str(dave.id), str(davina.id), str(prudence.id)]
def test_api_users_authenticated_list_uppercase_content(): def test_api_users_authenticated_list_uppercase_content():