4 votes

La requête récursive de Mongodb ne fonctionne pas comme prévu avec $graphLookup

Dans mes documents, j'ai le _id, un nom de société et un sponsor (qui identifie le document parent, par le _id).

Par exemple, j'ai ce premier enregistrement qui n'a pas de sponsor (parent)

_id:607536219910ef23e80e0bbe
companyname:"Société principale"
sponsor:"

Ensuite Company 1, où la Société principale est le parent:

  _id:607e16760a9d2c16e06bc252
    companyname:"Société 1"
    sponsor:"607536219910ef23e80e0bbe"

Et Company 2, où la Société 1 est le parent:

_id:607e187b0a9d2c16e06bc253
companyname:"Société 2"
sponsor:"607e16760a9d2c16e06bc252"

Et Company 3, où la Société 2 est le parent:

_id:607e1f470a9d2c16e06bc254
companyname:"Société 3"
sponsor:"607e187b0a9d2c16e06bc253"

Je fais un $match pour ramener les enregistrements enfants pour la société principale

{
  sponsor: '607536219910ef23e80e0bbe'
}

Et ensuite, je $addFields userid, qui est un _Id converti en chaîne. Ceci est pour correspondre plus tard avec le sponsor:

{"userid": { "$toString": "$_id" }}

Maintenant, lorsque je graphLookup, j'obtiens la société fille (Société 2) pour la Société principale, mais je n'obtiens pas la Société 3 en tant que fille de la Société 2. Je n'obtiens que la Société 1 et la Société 2:

description de l'image ici

Voici mon graphLookup

{
  from: 'pls',
  startWith: "$userid",
  connectFromField: 'userid',
  connectToField: 'sponsor',
  as: 'downline',
  maxDepth: 100,
  restrictSearchWithMatch: {}
}

Toute aide sera appréciée.

MISE À JOUR:

Comme l'a dit Turivishal ci-dessous, la requête fonctionne, mais voici les résultats attendus:

[{
        "_id": "607536219910ef23e80e0bbe",
        "companyname": "Société principale",
        "downline": [{
            "_id": "607e16760a9d2c16e06bc252",
            "companyname": "Société 1",
            "sponsor": "607536219910ef23e80e0bbe",
            "downline": [{
                "_id": "607e187b0a9d2c16e06bc253",
                "companyname": "Société 2",
                "sponsor": "607e16760a9d2c16e06bc252",
                "downline": [{
                    "_id": "607e1f470a9d2c16e06bc254",
                    "companyname": "Société 3",
                    "sponsor": "607e187b0a9d2c16e06bc253"
                }]
            }]
        }],
        "sponsor": "",
        "userId": "607536219910ef23e80e0bbe"
    }

SOLUTION PAR TURIVISHAL:

Comme solution Turivishal, voici le pipeline final qui fournit une vue hiérarchique/arborescente parfaite de la requête récurrente et fonctionne parfaitement avec les contrôles Angular Treeview. Merci beaucoup Turivishal. Je pense que vous devriez poster une réponse pour que je puisse l'accepter et qu'elle puisse être utile pour d'autres.

Sa solution est assez similaire à celle qu'il a proposée, mais bien meilleure. J'ai fini par créer un nouveau champ appelé PLID qui duplique le champ _id, et cela fonctionne incroyablement bien. Je laisse les administrateurs décider s'ils estiment que cette question devrait être fermée, car encore une fois, la solution de Turivishal est basée sur cette question, mais plus claire à mon avis. Voici son travail:

[
  {
    '$match': {
      'sponsor': '0'
    }
  }, {
    '$graphLookup': {
      'from': 'pls', 
      'startWith': '$plid', 
      'connectFromField': 'plid', 
      'connectToField': 'sponsor', 
      'depthField': 'level', 
      'as': 'children'
    }
  }, {
    '$unwind': {
      'path': '$children', 
      'preserveNullAndEmptyArrays': true
    }
  }, {
    '$sort': {
      'children.level': -1
    }
  }, {
    '$group': {
      '_id': '$plid', 
      'sponsor': {
        '$first': '$sponsor'
      }, 
      'companyname': {
        '$first': '$companyname'
      }, 
      'children': {
        '$push': '$children'
      }
    }
  }, {
    '$addFields': {
      'children': {
        '$reduce': {
          'input': '$children', 
          'initialValue': {
            'level': -1, 
            'presentChild': [], 
            'prevChild': []
          }, 
          'in': {
            '$let': {
              'vars': {
                'prev': {
                  '$cond': [
                    {
                      '$eq': [
                        '$$value.level', '$$this.level'
                      ]
                    }, '$$value.prevChild', '$$value.presentChild'
                  ]
                }, 
                'current': {
                  '$cond': [
                    {
                      '$eq': [
                        '$$value.level', '$$this.level'
                      ]
                    }, '$$value.presentChild', []
                  ]
                }
              }, 
              'in': {
                'level': '$$this.level', 
                'prevChild': '$$prev', 
                'presentChild': {
                  '$concatArrays': [
                    '$$current', [
                      {
                        '$mergeObjects': [
                          '$$this', {
                            'children': {
                              '$filter': {
                                'input': '$$prev', 
                                'as': 'e', 
                                'cond': {
                                  '$eq': [
                                    '$$e.sponsor', '$$this.plid'
                                  ]
                                }
                              }
                            }
                          }
                        ]
                      }
                    ]
                  ]
                }
              }
            }
          }
        }
      }
    }
  }, {
    '$addFields': {
      'children': '$children.presentChild'
    }
  }
]

2voto

turivishal Points 21485

Vous pouvez utiliser $graphLookup et d'autres opérateurs de tableau utiles,

  • $match filtre les enregistrements dont le champ sponsor est ""
  • $graphLookup pour obtenir les enregistrements enfants et le nombre de profondeur dans le champ level
  • $unwind décompose le tableau downline et permet de ne pas supprimer les enfants vides
  • $sort par champ de niveau de profondeur level par ordre décroissant
  • $group par champ id et reconstruit le tableau downline
  • $addFields trouve maintenant les enfants de niveau imbriqué et les attribue à leur niveau,
    • $reduce pour parcourir le tableau downline.
    • initialiser le champ par défaut level avec une valeur par défaut de -1, presentChild est [], prevChild est [] pour les conditions
    • $let pour initialiser les champs:
      • prev selon la condition si les deux champs level sont égaux, retourner prevChild sinon retourner presentChild
      • current selon la condition si les deux champs level sont égaux, retourner presentChild sinon []
    • in pour renvoyer le champ level et le champ prevChild des champs initialisés
      • presentChild $filter downline à partir du tableau prev et retourner, fusionner les objets actuels avec le tableau downline en utilisant $mergeObjects et concaténer avec le tableau current de let en utilisant $concatArrays
  • $addFields pour ne retourner que le tableau presentChild car nous avons seulement besoin de ce tableau traité

    db.collection.aggregate([ { $match: { sponsor: "" } }, { $graphLookup: { from: "collection", startWith: "$_id", connectFromField: "_id", connectToField: "sponsor", depthField: "level", as: "downline" } }, { $unwind: { path: "$downline", preserveNullAndEmptyArrays: true } }, { $sort: { "downline.level": -1 } }, { $group: { _id: "$_id", sponsor: { $first: "$sponsor" }, companyname: { $first: "$companyname" }, downline: { $push: "$downline" } } }, { $addFields: { downline: { $reduce: { input: "$downline", initialValue: { level: -1, presentChild: [], prevChild: [] }, in: { $let: { vars: { prev: { $cond: [{ $eq: ["$$value.level", "$$this.level"] }, "$$value.prevChild", "$$value.presentChild"] }, current: { $cond: [{ $eq: ["$$value.level", "$$this.level"] }, "$$value.presentChild", []] } }, in: { level: "$$this.level", prevChild: "$$prev", presentChild: { $concatArrays: [ "$$current", [ { $mergeObjects: [ "$$this", { downline: { $filter: { input: "$$prev", as: "e", cond: { $eq: ["$$e.sponsor", "$$this._id"] } } } } ] } ] ] } } } } } } } }, { $addFields: { downline: "$downline.presentChild" } } ])

Terrain de jeu

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X