MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

correspondance du cadre d'agrégation mongodb par documents imbriqués

Interroger cette structure pour les résultats que vous voulez n'est pas possible sans connaître tous les forms possibles noms au préalable et en les utilisant dans la requête. Ce serait très salissant de toute façon. Cela dit, lisez la suite pendant que j'explique comment cela peut être fait.

Il y a un problème avec la structure de ces documents qui va vous empêcher de faire une analyse de requête raisonnable. Dans l'état actuel des choses, vous devez connaître tous les champs de nom de formulaire possibles afin de filtrer quoi que ce soit.

Votre structure actuelle comporte des formulaires contenant un sous-document, dont chaque clé contient un autre sous-document avec une seule propriété, status . Ceci est difficile à traverser car vos forms L'élément a une structure arbitraire pour chaque document que vous créez. Cela signifie que le modèle pour descendre au status informations dont vous souhaitez comparer les modifications pour chaque document de votre collection.

Voici ce que j'entends par chemin. Pour obtenir le statut de n'importe quel élément, vous devez procéder comme suit

Avec le deuxième élément changeant tout le temps. Il n'y a aucun moyen à joker quelque chose comme ça car le nom est considéré comme explicite.

Cela peut avoir été considéré comme un moyen simple d'implémenter la sérialisation des données de vos formulaires mais je vois un plus flexible alternative. Ce dont vous avez besoin est une structure de document que vous pouvez parcourir dans un modèle standard. C'est toujours quelque chose à considérer dans la conception. Prenez ce qui suit :

{
    "_id" : "Tvq444454j",
    "name": "Jim",
    "forms": [
        {
             "name": "Jorney",
             "status":"closed"          
        },
        {
            "name": "Women",
            "status":"void"            
        },
        {
            "name": "Child",
            "status":"closed"           
        },
        {
            "name": "Farm",
            "status":"closed"            
        }  
    ]
}

Ainsi la structure du document est modifiée pour rendre les forms élément d'un tableau, et plutôt que de placer le champ d'état sous une clé qui nomme le "champ de formulaire", nous avons chaque membre du tableau sous la forme d'un sous-document contenant le "champ de formulaire" name et le status . Ainsi, l'identifiant et le statut sont toujours appariés, mais simplement représentés sous la forme d'un sous-document maintenant. Cela modifie surtout le chemin d'accès à ces clés, comme maintenant pour les deux le nom du champ et son statut que nous pouvons faire

Qu'est-ce que cela signifie que vous pouvez interroger pour trouver les noms de tous les champs du form ou tous les status champs du form , ou même tous les documents avec un certain name champ et certains status . C'est beaucoup mieux que ce qui pourrait être fait avec la structure d'origine.

Maintenant, dans votre cas particulier, vous souhaitez obtenir uniquement les documents où tous les champs ne sont pas void . Maintenant, il n'y a aucun moyen dans une seule requête de le faire car il n'y a pas d'opérateur pour comparer tous les éléments d'un tableau de cette manière et voir s'ils sont identiques. Mais vous pouvez adopter deux approches :

La première, et probablement moins efficace, consiste à interroger tous les documents qui contiennent un élément dans forms qui a un status de "vide". Avec les ID de document résultants, vous pouvez émettre une autre requête qui renvoie les documents qui ne le font pas avoir les identifiants qui ont été spécifiés.

db.forms.find({ "forms.status": "void" },{ _id: 1})

db.forms.find({ _id: $not: { $in: [<Object1>,<Object2>,<Object3>,... ] } })

Compte tenu de la taille du résultat, cela peut ne pas être possible et n'est généralement pas une bonne idée car l'opérateur d'exclusion $not force essentiellement une analyse complète de la collection, vous ne pouvez donc pas utiliser d'index.

Une autre approche consiste à utiliser le pipeline d'agrégation comme suit :

db.forms.aggregate([
    { "$unwind": "$forms" },
    { "$group": { "_id": "$_id", "status": { "$addToSet": "$forms.status" }}},
    { "$unwind": "$status" },
    { "$sort": { "_id": 1, "status": -1 }},
    { "$group": { "_id": "$_id", "status": { "$first": "$status"}}},
    { "$match":{ "status": "closed" }}
])

Bien sûr, cela ne renverra que le _id pour les documents correspondants, mais vous pouvez émettre une requête avec $in et renvoyer l'ensemble des documents correspondants. C'est mieux que l'opérateur d'exclusion utilisé auparavant, et maintenant nous pouvons utiliser un index pour éviter les analyses complètes de la collection.

En guise d'approche finale et pour le meilleur compte tenu des performances, vous pouvez à nouveau modifier le document afin qu'au niveau supérieur, vous gardiez le "statut" indiquant si un champ des formulaires est "annulé" ou "fermé". Ainsi, au niveau supérieur, la valeur ne serait fermée que si tous les éléments étaient "fermés" et "vide" si quelque chose était vide, et ainsi de suite.

Ce dernier signifierait un autre changement programmatique, et tous les changements apportés aux forms les éléments devraient également mettre à jour ce champ pour conserver le "statut". C'est cependant le moyen le plus efficace de trouver les documents dont vous avez besoin et cela peut valoir la peine d'être considéré.

MODIFIER :

En plus de changer le document pour qu'il ait un statut principal, le formulaire de requête le plus rapide sur la structure révisée est en fait :

db.forms.find({ "forms": { "$not": { "$elemMatch": { "status": "void" } } } })