web-dev-qa-db-fra.com

Mise à jour des tableaux imbriqués dans mongodb

J'ai un document dans mongodb avec un tableau d'objets imbriqués profonds à 2 niveaux que je dois mettre à jour, quelque chose comme ceci:

{
    id: 1,
    items: [
        {
            id: 2,
            blocks: [
                {
                    id: 3
                    txt: 'hello'
                }
            ]
        }
    ] 
}

S'il n'y avait qu'un seul tableau profond, je pourrais utiliser l'opérateur positionnel pour mettre à jour les objets, mais pour le deuxième niveau, la seule option que j'ai trouvée consiste à utiliser l'opérateur positionnel avec l'index des objets imbriqués, comme ceci:

db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}})

Cette approche fonctionne mais elle me semble dangereuse car je construis un service Web et le numéro d'index devrait provenir du client qui peut envoyer disons 100000 comme index et cela forcera mongodb à créer un tableau avec 100000 index avec une valeur nulle.

Existe-t-il d'autres moyens de mettre à jour ces objets imbriqués où je peux me référer à l'ID de l'objet au lieu de sa position ou peut-être des moyens de vérifier si l'index fourni est hors limites avant de l'utiliser dans la requête?

45
src091

Voici la grande question, avez-vous besoin de tirer parti des opérations "addToSet" et "Push" de Mongo? Si vous prévoyez vraiment de modifier uniquement les éléments individuels du tableau, vous devez probablement créer ces tableaux en tant qu'objets.

Voici comment je structurerais ceci:

{
    id: 1,
    items: 
        { 
          "2" : { "blocks" : { "3" : { txt : 'hello' } } },
          "5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } }
        }
}

Cela transforme essentiellement tout en objets JSON au lieu de tableaux. Vous perdez la possibilité d'utiliser $Push Et $addToSet Mais je pense que cela facilite tout. Par exemple, votre requête ressemblerait à ceci:

db.objects.update({'items.2': {$exists:true} }, {'$set': {'items.2.blocks.0.txt': 'hi'}})

Vous remarquerez également que j'ai vidé les "ID". Lorsque vous imbriquez des choses comme celle-ci, vous pouvez généralement remplacer "ID" en utilisant simplement ce numéro comme index. Le concept "ID" est désormais implicite.

Cette fonctionnalité a été ajoutée en 3.6 avec mises à jour expressives .

db.objects.update( {id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] } )

27
Gates VP

Les identifiants que vous utilisez sont des nombres linéaires et ils doivent provenir de quelque part comme un champ supplémentaire tel que 'max_idx' ou quelque chose de similaire. Cela signifie une recherche de l'ID, puis une mise à jour. UUID/ObjectId peut être utilisé pour les identifiants, ce qui garantit que vous pouvez également utiliser Distributed CRUD.

3
Seraj