web-dev-qa-db-fra.com

Rechercher dans Double Nested Array MongoDB

J'ai cette collection dans mongodb

{
"_id" : "777",
"someKey" : "someValue",
"someArray" : [
    {
        "name" : "name1",
        "someNestedArray" : [
            {
                "name" : "value"
            },
            {
                "name" : "delete me"
            }
        ]
    }
  ]
}

Je veux trouver un document basé sur someArray.someNestedArray.name mais je ne trouve aucun lien utile tous les résultats de la recherche sur la mise à jour du tableau imbriqué j'essaye mais ne retourne rien

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

et quelque chose d'autre

comment puis-je trouver par élément dans un tableau double imbriqué mongodb?

15
user298582

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

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

C'est très bien pour une valeur de "champ unique", pour faire correspondre plusieurs champs que 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 à ce "chemin" correspondant à la valeur. Si vous aviez l'intention de "faire correspondre et filtrer" le résultat de sorte que seul l'élément correspondant ait été renvoyé, cela n'est pas possible avec la projection de l'opérateur positionnel, comme cité :

Tableaux imbriqués

L'opérateur positionnel $ ne peut pas être utilisé pour les requêtes qui traversent plusieurs tableaux, 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. Le $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é supprimé de tous les éléments .

En suivant à nouveau l'exemple d'avoir en fait 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 ] }
          ]
        }
      }
    }
  }}
])

Par conséquent, sur le tableau "externe", le $filter regarde en fait le $size du tableau "interne" après avoir été "filtré". lui-même, vous pouvez donc rejeter ces résultats lorsque l'ensemble du tableau interne correspond en fait à la note.

MongoDB plus ancien

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

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 les tableaux imbriqués pour un ou plusieurs résultats dans le document.

43
Neil Lunn

Vous pouvez également essayer quelque chose comme ci-dessous:

db.collection.aggregate(
    { $unwind: '$someArray' },
    {
        $project: {
            'filteredValue': {
                $filter: {
                  input: "$someArray.someNestedArray",
                  as: "someObj",
                  cond: { $eq: [ '$$someObj.name', 'delete me' ] }
                }
            }
        }
    }
)
0
Jitendra