2
votes

mongo agrégat basé sur les conditions pour filtrer le document pour la gestion des versions

Je travaille sur la gestion des versions, nous avons des documents basés sur les UUIDs et les jobUuids , et les jobUuids sont les documents associés à l'utilisateur actuellement en activité. J'ai quelques requêtes agrégées sur ces collections que je dois mettre à jour en fonction des UUID du travail,

Les résultats récupérés par la requête agrégée doivent être tels que,

  • si le document jobUuid utilisateur actuel n'existe pas alors le document maître avec jobUuid: "default" sera renvoyé (le document sans jobUuid),
  • si l'uuid du travail existe, seul le document est renvoyé.

J'ai un $match utilisé pour obtenir ces documents en fonction de certaines conditions, à partir de ces documents, je dois filtrer les documents en fonction des conditions ci-dessus, et un exemple est montré ci-dessous,

Les données ressemblent à ceci:

[
  {
    "uuid": "5cdb5a10-4f9b-4886-98c1-31d9889dd943",
    "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12",
    "name": "adam"
  },
  {
    "uuid": "b745baff-312b-4d53-9438-ae28358539dc",
    "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12",
    "name": "eve"
  },
  {
    "uuid": "26cba689-7eb6-4a9e-a04e-24ede0309e50",
    "name": "john",
    "jobUuid": "default",
  }
]

Les résultats pour "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12" doivent être:

[
  {
    "uuid": "5cdb5a10-4f9b-4886-98c1-31d9889dd943",
    "name": "adam",
    "jobUuid": "default",
  },
  {
    "uuid": "5cdb5a10-4f9b-4886-98c1-31d9889dd943",
    "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12",
    "name": "adam"
  },
  {
    "uuid": "b745baff-312b-4d53-9438-ae28358539dc",
    "name": "eve",
    "jobUuid": "default",
  },
  {
    "uuid": "b745baff-312b-4d53-9438-ae28358539dc",
    "jobUuid": "d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12",
    "name": "eve"
  },
  {
    "uuid": "26cba689-7eb6-4a9e-a04e-24ede0309e50",
    "name": "john",
    "jobUuid": "default",
  }
]

Sur la base des conditions mentionnées ci-dessus, est-il possible de filtrer le document dans la requête agrégée pour extraire le document d'un uuid de travail spécifique?

Edit 1: J'ai eu la solution suivante, qui fonctionne bien, je veux une meilleure solution, éliminant toutes ces conditions imbriquées.

Edit 2: Mise à jour des données avec les UUID réels et je viens d'inclure uniquement le name dans un autre champ, nous avons n nombre de champs qui ne sont pas pertinents à inclure ici mais nécessaires à la fin.


0 commentaires

3 Réponses :


0
votes

J'ai pu résoudre ce problème avec la requête agrégée suivante,

  • Nous jobUuid abord les résultats correspondant uniquement au jobUuid fourni par l'utilisateur ou au "default" dans la section match.

  • Ensuite, les résultats sont regroupés en fonction de l'uuid, en utilisant une phase de groupes et nous comptons également les résultats.

  • En utilisant les conditions de replaceRoot nous replaceRoot abord la longueur du document groupé,

  • Si la longueur du document groupé est supérieure ou égale à 2, nous filtrons le document qui correspond au jobUuid fourni.

  • S'il est inférieur ou égal à 1, nous vérifions s'il correspond au jobUuid default et le retournons.

La requête est ci-dessous:

[
    {
      $match: {
        $or: [{ jobUuid:1 },{ jobUuid: 'default'}]
      }
    },
    {
      $group: {
        _id: '$uuid',
        count: {
          $sum: 1
        },
        docs: {
          $push: '$$ROOT'
        }
      }
    },
    {
      $replaceRoot: {
        newRoot: {
          $cond: {
            if: {
              $gte: [
                '$count',
                2
              ]
            },
            then: {
              $arrayElemAt: [
                {
                  $filter: {
                    input: '$docs',
                    as: 'item',
                    cond: {
                      $ne: [
                        '$$item.jobUuid',
                        'default'
                      ]
                    }
                  }
                },
                0
              ]
            },
            else: {
              $arrayElemAt: [
                {
                  $filter: {
                    input: '$docs',
                    as: 'item',
                    cond: {
                      $eq: [
                        '$$item.jobUuid',
                        'default'
                      ]
                    }
                  }
                },
                0
              ]
            }
          }
        }
      }
    }
  ]


0 commentaires

1
votes

Voici ce que je ferais:

  1. match stage avec $ in plutôt qu'un $ or (pour plus de lisibilité)
  2. phase de groupe avec _id sur $ uuid, comme vous l'avez fait, mais au lieu de pousser toutes les données dans un tableau, soyez plus sélectif. _id stocke déjà $ uuid, donc aucune raison de le capturer à nouveau. Le nom doit toujours être le même pour chaque $ uuid, donc ne prenez que la première instance. Sur la base de la correspondance, il n'y a que deux possibilités pour jobUuid, mais cela supposera que ce sera soit "default" ou autre chose, et qu'il peut y avoir plus d'une occurrence du jobUuid non "default". En utilisant "$ addToSet" au lieu de pousser vers un tableau au cas où il y aurait plusieurs occurrences du même jobUuid pour un utilisateur, aussi, avant d'ajouter à l'ensemble, utilisez un conditionnel pour ajouter uniquement des jobUuids non "par défaut", en utilisant $$ REMOVE pour éviter d'insérer un null lorsque le jobUuid est "default".
  3. Enfin, "$ project" pour nettoyer les choses. Si l'élément 0 du tableau jobUuids n'existe pas (est nul), il n'y a pas d'autre possibilité pour cet utilisateur que pour le jobUuid d'être "default", donc utilisez "$ ifNull" pour tester et définir "default" comme il convient. Il peut y avoir plus d'un jobUuid ici, selon si cela est autorisé dans votre base de données / application, à vous de décider comment gérer cela (prendre le plus élevé, prendre le plus bas, etc.).

Testé sur: https://mongoplayground.net/p/e76cVJf0F3o

[{
    "$match": {
        "jobUuid": {
            "$in": [
                "1",
                "default"
            ]
        }
    }
},
{
    "$group": {
        "_id": "$uuid",
        "name": {
            "$first": "$name"
        },
        "jobUuids": {
            "$addToSet": {
                "$cond": {
                    "if": {
                        "$ne": [
                            "$jobUuid",
                            "default"
                        ]
                    },
                    "then": "$jobUuid",
                    "else": "$$REMOVE"
                }
            }
        }
    }
},
{
    "$project": {
        "_id": 0,
        "uuid": "$_id",
        "name": 1,
        "jobUuid": {
            "$ifNull": [{
                    "$arrayElemAt": [
                        "$jobUuids",
                        0
                    ]
                },
                "default"
            ]
        }
    }
}]


1 commentaires

Merci pour votre solution, ici je n'ai pas mentionné que j'ai n nombres de champs dans la réponse. Le problème avec cette solution est que je dois inclure tous les champs dans la $group ce qui est vraiment gênant.



3
votes

Mise à jour basée sur un commentaire:

mais les UUID sont des chaînes alphanumériques, comme indiqué ci-dessus, cela a-t-il un effet sur ces tri, et comme nous n'utilisons pas de conditions pour obtenir les résultats, je crains que cela ne pose des problèmes.

Vous pouvez utiliser un champ supplémentaire pour faire correspondre l'ordre de tri afin qu'il soit du même ordre que les valeurs de l'expression in. Assurez-vous de fournir les valeurs par défaut comme dernière valeur.

[
  {"$match":{"jobUuid":{"$in":[1,"default"]}}},
  {"$sort":{"uuid":1, "jobUuid":1}},
  {
    "$group": {
      "_id": "$uuid",
      "doc":{"$first":"$$ROOT"}
    }
  },
  {"$replaceRoot":{"newRoot":"$doc"}}
]

exemple ici - https://mongoplayground.net/p/wXiE9i18qxf

Original

Vous pouvez utiliser la requête ci-dessous. La requête choisira le document non par défaut s'il existe pour uuid ou bien choisir le document par défaut comme seul document.

[
  {"$match":{"jobUuid":{"$in":["d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12","default"]}}},
  {"$addFields":{ "order":{"$indexOfArray":[["d275781f-ed7f-4ce4-8f7e-a82e0e9c8f12","default"], "$jobUuid"]}}},
  {"$sort":{"uuid":1, "order":1}},
  {
    "$group": {
      "_id": "$uuid",
      "doc":{"$first":"$$ROOT"}
    }
  },
  {"$project":{"doc.order":0}},
  {"$replaceRoot":{"newRoot":"$doc"}}
]

exemple ici - https://mongoplayground.net/p/KrL-1s8WCpw


6 commentaires

Merci pour votre solution, ici nous trions en fonction de l'uuid et du jobUuid, auparavant c'était des entiers, mais les UUID sont des chaînes alphanumériques, comme indiqué ci-dessus, cela a-t-il un effet sur ces tri, et puisque nous n'utilisons pas de conditions pour obtenir les résultats, je crains que cela ne cause des problèmes.


Np - Réponse mise à jour pour inclure un champ supplémentaire pour correspondre à l'ordre de tri afin qu'il soit du même ordre que les valeurs de l'expression in.


Je suis désolé, la deuxième étape de la réponse mise à jour n'a pas de sens pour moi, j'ai l'impression que nous pouvons simplement la remplacer par 0 ou 1 en fonction de la façon dont nous fournissons le tableau uuid du travail. Avons-nous vraiment besoin de cette étape?


Pas de soucis - sans savoir quel document correspond à quel jobUuid vous ne pourrez pas attribuer 1 ou 0, non? C'est une vérification par rapport aux valeurs d'entrée pour affecter l'ordre - peut-être que vous pouvez le simplifier en utilisant l'opérateur cond car il n'y a que deux entrées. Si jobUUid est défini par défaut, attribuez 1 sinon 0


Je pense que l'opérateur conditionnel est le bon choix car il n'a pas besoin de dépendre de l'expression d'entrée pour attribuer l'ordre. La deuxième étape peut être {"$addFields":{ "order":{"$cond":[{"$eq":["default","$jobUuid"]}, 1, 0]}}} place. Je crois que c'est ce à quoi vous faites référence.


Cela semble bon, j'attendrai un certain temps pour voir d'autres solutions avant d'accepter, merci pour votre temps!