Résumé:

Pour une liste déroulante, je dois calculer les valeurs distinctes d'une clé spécifique stockée dans un JSONField dans une table de la base de données Postgres. Pire scénario : la table contient 1 à 10 millions d'entrées.

Contexte:

Je développe une configuration dans laquelle j'ai plusieurs déploiements (un pour chaque client). Chaque déploiement contient un backend et plusieurs clients. Les journaux sont publiés en continu des clients vers le backend. Ces journaux contiendront un champ log_meta, qui comprend une clé nommée origin, qui décrit d'où provient l'entrée de journal. D'un client à l'autre, la valeur de origin peut varier et je ne souhaite pas imposer un ensemble restreint de valeurs pour origin, mais en général, elles désignent l'environnement dans lequel le client s'exécute ; "DEV" et "PRODUCTION" sont des valeurs potentielles pour origin. En pratique, il peut n'y avoir que 1 à 2 valeurs distinctes pour origin dans un seul déploiement. On peut s'attendre à ce que le nombre de journaux se situe entre 1 et 10 millions.

from jsonfield import JSONField
from django.db import models

class Log(models.Model)
    # Other fields
    log_json = JSONField(default=list)
    log_meta: JSONField(default=dict) # Will contain a key named origin

Dans une "interface d'administrateur", je souhaite soutenir que l'administrateur peut filtrer (via une liste déroulante) pour ne voir que les journaux provenant d'une origine spécifique. Pour ce faire, je dois extraire les valeurs distinctes du champ origin.

Comment puis-je calculer cet ensemble de valeurs distinctes dans Django, en tenant compte du fait que le nombre de journaux peut dans certains cas être compris entre 1 et 10 millions ?

Ce que j'ai déjà essayé :

  • Rien, car je ne sais pas comment faire.

Informations supplémentaires :

  • Le backend est écrit en Django, en utilisant Postgres comme base de données.
  • S'il n'est pas possible de calculer les valeurs à la volée, mon alternative est de construire l'ensemble de valeurs distinctes en continu au fur et à mesure que les journaux arrivent. Je considère cela comme une deuxième option, car cela introduit un état supplémentaire ; si possible, je préfère simplement calculer/déduire l'ensemble à la place.
0
Albertsen 13 févr. 2020 à 11:34

1 réponse

Meilleure réponse

Le QuerySet pour effectuer la sélection requise :

Log.objects.filter(
    # some filtering if required
    log_meta__origin__isnull=False
).order_by().values_list('log_meta__origin').distinct()

order_by() est d'effacer toute commande déjà présente sur QuerySet pour nous permettre d'appeler distinct() plus tard.


Son « efficacité » est une question complètement différente et subjective.

PostgreSQL doit parcourir tous les enregistrements pour effectuer cette sélection.

Une possibilité consiste à ajouter une indexation sur ce seul champ de JSON (comme dans ce question SO)

Comme ce type de sélection semble ne pas avoir besoin d'être effectué fréquemment (ce qui signifie que les origines distinctes sont assez stables, vous pouvez par exemple mettre en cache la liste des valeurs distinctes et la mettre à jour périodiquement) - utilisez PostgreSQL Vues matérialisées et les mettre à jour périodiquement / à la demande (ou simplement stocker la liste dans le cache (Redis) au lieu de Materialized vues).

1
Oleg Russkin 13 févr. 2020 à 09:59