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

Rechercher dans un tableau double imbriqué MongoDB

Dans le sens le plus simple, cela suit simplement la forme de base de la "notation par points" utilisée par MongoDB. Cela fonctionnera quel que soit le membre du tableau dans lequel se trouve le membre du tableau interne, tant qu'il correspond à une valeur :

db.mycollection.find({
    "someArray.someNestedArray.name": "value"
})

C'est bien pour une valeur "champ unique", pour faire correspondre plusieurs champs, vous utiliseriez $elemMatch :

db.mycollection.find({
    "someArray": { 
        "$elemMatch": {
            "name": "name1",
            "someNestedArray": {
                "$elemMatch": {
                    "name": "value",
                    "otherField": 1
                }
            }
        }
    }
})

Cela correspond au document qui contiendrait quelque chose avec un champ a à ce "chemin" correspondant à la valeur. Si vous aviez l'intention de "faire correspondre et filtrer" le résultat afin que seul l'élément correspondant soit renvoyé, cela n'est pas possible avec la projection de l'opérateur positionnel, comme indiqué :

Tableaux imbriqués

L'opérateur positionnel $ ne peut pas être utilisé pour les requêtes qui traversent plus d'un tableau, telles que les requêtes qui traversent des tableaux imbriqués dans d'autres tableaux, car le remplacement de l'espace réservé $ est une valeur unique

MongoDB moderne

Nous pouvons le faire en appliquant $filter et $map ici. La $map est vraiment nécessaire car le tableau "interne" peut changer à la suite du "filtrage", et le tableau "externe" ne correspond bien sûr pas aux conditions lorsque le "interne" a été dépouillé de tous les éléments.

Encore une fois, en suivant l'exemple d'avoir plusieurs propriétés à faire correspondre dans chaque tableau :

db.mycollection.aggregate([
  { "$match": {
    "someArray": {
      "$elemMatch": {
         "name": "name1",
         "someNestedArray": {
           "$elemMatch": {
             "name": "value",
             "otherField": 1
           }
         }
       }
    }
  }},
  { "$addFields": {
    "someArray": {
      "$filter": {
        "input": {
          "$map": {
            "input": "$someArray",
            "as": "sa",
            "in": {
              "name": "$$sa.name",
              "someNestedArray": {
                "$filter": {
                  "input": "$$sa.someNestedArray",
                  "as": "sn",
                  "cond": {
                    "$and": [
                      { "$eq": [ "$$sn.name", "value" ] },
                      { "$eq": [ "$$sn.otherField", 1 ] }
                    ]
                  }
                }
              }             
            }
          },
        },
        "as": "sa",
        "cond": {
          "$and": [
            { "$eq": [ "$$sa.name", "name1" ] },
            { "$gt": [ { "$size": "$$sa.someNestedArray" }, 0 ] }
          ]
        }
      }
    }
  }}
])

Donc sur le tableau "externe" le $filter regarde réellement le $size du tableau "interne" après qu'il ait été "filtré" lui-même, de sorte que vous pouvez rejeter ces résultats lorsque l'ensemble du tableau interne correspond en fait à la notation.

Ancienne MongoDB

Afin de "projeter" uniquement l'élément correspondant, vous avez besoin du .aggregate() méthode :

db.mycollection.aggregate([
    // Match possible documents
    { "$match": {
        "someArray.someNestedArray.name": "value"
    }},

    // Unwind each array
    { "$unwind": "$someArray" },
    { "$unwind": "$someArray.someNestedArray" },

    // Filter just the matching elements
    { "$match": {
        "someArray.someNestedArray.name": "value"
    }},

    // Group to inner array
    { "$group": {
        "_id": { 
            "_id": "$_id", 
            "name": "$someArray.name"
        },
        "someKey": { "$first": "$someKey" },
        "someNestedArray": { "$push": "$someArray.someNestedArray" }
    }},

    // Group to outer array
    { "$group": {
        "_id": "$_id._id",
        "someKey": { "$first": "$someKey" },
        "someArray": { "$push": {
            "name": "$_id.name",
            "someNestedArray": "$someNestedArray"
        }}
    }} 
])

Cela vous permet de "filtrer" les correspondances dans des tableaux imbriqués pour un ou plusieurs résultats dans le document.