Tengo los siguientes esquemas para el documento Carpeta:

var permissionSchema = new Schema({
    role: { type: String },
    create_folders: { type: Boolean },
    create_contents: { type: Boolean }
});

var folderSchema = new Schema({
    name: { type: string },
    permissions: [ permissionSchema ]
});

Así, para cada Página puede tener muchos permisos. En mi CMS hay un panel donde puedo ver todas las carpetas y sus permisos. El administrador puede editar un único permiso y guardarlo.

Fácilmente podría salvar a toda la Carpeta documento con su conjunto de permisos, donde sólo uno de permiso fue modificado. Pero no quiero guardar todos los documentos (el verdadero esquema tiene mucho más campos) así que hice esto:

savePermission: function (folderId, permission, callback) {
    Folder.findOne({ _id: folderId }, function (err, data) {
        var perm = _.findWhere(data.permissions, { _id: permission._id });                

        _.extend(perm, permission);

        data.markModified("permissions");
        data.save(callback);
    });
}

pero el problema es que perm es siempre indefinido! Traté de «estáticamente» obtener el permiso de esta manera:

var perm = data.permissions[0];

y funciona muy bien, así que el problema es que se ponen de manifiesto de la biblioteca no es capaz de consultar los permisos de la matriz. Así que supongo que hay un mejor (y workgin) manera de conseguir el subdocumento de un documento recuperado.

Alguna idea?

P. S.: he resuelto de comprobar cada elemento en los datos.permiso de matriz de uso de un ciclo «for» y la comprobación de los datos.permisos[i]._id == permiso._id, pero me gustaría más inteligente solución, sé que hay uno!

3 Comentarios

  1. 94

    Así como nota, el defecto en la mangosta es que cuando se «incorporan» los datos en una matriz como la que usted puede obtener un _id valor para cada matriz de entrada como parte de su propio sub-propiedades del documento. En realidad se puede utilizar este valor para determinar el índice del elemento que desea actualizar. La MongoDB manera de hacer esto es la de posición $ operador variable, que contiene la «coincidente» posición en la matriz:

    Folder.findOneAndUpdate(
        { "_id": folderId, "permissions._id": permission._id },
        { 
            "$set": {
                "permissions.$": permission
            }
        },
        function(err,doc) {
    
        }
    );

    Que .findOneAndUpdate() método devolverá el documento modificado o de lo contrario puedes usar .update() como un método si usted no necesita el documento devuelto. Las partes principales son «coincidentes» el elemento de la matriz de actualización y de «identificación» que coinciden con el de posición $ como se mencionó anteriormente.

    Luego, por supuesto, usted está utilizando el $de operador a fin de que sólo los elementos que se especifican en realidad son enviados «a través de la red» para el servidor. Usted puede tomar esto con «la notación de punto» y sólo tiene que especificar los elementos que usted realmente desea actualizar. Como en:

    Folder.findOneAndUpdate(
        { "_id": folderId, "permissions._id": permission._id },
        { 
            "$set": {
                "permissions.$.role": permission.role
            }
        },
        function(err,doc) {
    
        }
    );

    Así que esta es la flexibilidad que MongoDB ofrece, donde se puede ser muy «selectiva» en cómo realmente actualización de un documento.

    Lo que esto hace, sin embargo, es de «bypass» cualquier lógica podría haber construido en su «mangosta» del esquema, tales como la «validación» o «pre-guardar ganchos». Esto es debido a la «óptima» es un MongoDB «característica» y cómo está diseñado. Mangosta en sí trata de ser una «conveniencia» contenedor a través de esta lógica. Pero si usted está preparado para tomar control de ti mismo, a continuación, las actualizaciones pueden ser realizados de la manera más óptima.

    Así que donde sea posible hacerlo, mantener sus datos «incrustado» y no el uso que se hace referencia modelos. Permite la atómica actualización de «padre» e «hijo» elementos simples actualizaciones de donde usted no necesita preocuparse acerca de la concurrencia. Probablemente es una de las razones por las que debería de haber seleccionado MongoDB en el primer lugar.

    • ehi! gran código, gracias! Sin embargo, si tuviera que consultar un documento, cambiar un campo y guardarlo againg, sería aceptar para mí, yo no quería guardar un objeto en su totalidad procedentes de la Interfaz de Usuario. Pero ser capaz de consulta de un documento y guardar uno de los campos (incluso un campo de sólo un subdocumento) es muy potente. Realmente tengo que cavar en el operador de$.
    • Cuando estoy usando esto para actualizar toda la subdoc (en lugar de un solo campo) esta parece ser la eliminación de la _id propiedad de la subdocumento que MongoDB dio por ser parte de una matriz. Esto es probablemente porque he quitado el _id propiedad del cartero solicitud de cuerpo, así que tal vez sólo necesita para asegurarse de que está en el cuerpo de solicitud? Parece raro que MongoDB es dejarme completamente sobrescribir el objeto entero, incluyendo la eliminación de la _id de la propiedad. No es que los padres de los objetos (efectivamente un PARCHE de solicitud en lugar de PONER). Cualquier solución?
  2. 14

    Con el fin de validar los subdocumentos a la hora de actualizar la Mangosta, usted tiene que «cargar» como un objeto de Esquema y, a continuación, la Mangosta se disparan automáticamente la validación y ganchos.

    const userSchema = new mongoose.Schema({
      //...
      addresses: [addressSchema],
    });

    Si usted tiene una matriz de subdocumentos, usted puede obtener la deseada con el id() método proporcionado por la Mangosta. A continuación, puede actualizar sus campos de forma individual, o si desea actualizar varios campos a la vez, a continuación, utilizar la set() método.

    User.findById(userId)
      .then((user) => {
        const address = user.addresses.id(addressId); //returns a matching subdocument
        address.set(req.body); //updates the address while keeping its schema       
        //address.zipCode = req.body.zipCode; //individual fields can be set directly
    
        return user.save(); //saves document with subdocuments and triggers validation
      })
      .then((user) => {
        res.send({ user });
      })
      .catch(e => res.status(400).send(e));

    Tenga en cuenta que usted realmente no necesita la userId para encontrar el documento de Usuario, usted puede obtener mediante la búsqueda de uno que tiene una dirección subdocumento que coincide con addressId de la siguiente manera:

    User.findOne({
      'addresses._id': addressId,
    })
    //.then() ... the same as the example above

    Recordar que en MongoDB el subdocumento se guarda sólo cuando el padre se guarda el documento.

    Leer más sobre el tema en la la documentación oficial.

  3. 6

    Si usted no desea que la recogida selectiva, sólo integrar el permissionSchema en el folderSchema.

    var folderSchema = new Schema({
        name: { type: string },
        permissions: [ {
            role: { type: String },
            create_folders: { type: Boolean },
            create_contents: { type: Boolean }
        } ]
    });

    Si necesita separar colecciones, este es el mejor enfoque:

    Usted podría tener un Permiso de modelo:

    var mongoose = require('mongoose');
    var PermissionSchema = new Schema({
      role: { type: String },
      create_folders: { type: Boolean },
      create_contents: { type: Boolean }
    });
    
    module.exports = mongoose.model('Permission', PermissionSchema);

    Y una Carpeta con el modelo de referencia para el documento de permiso.
    Puede hacer referencia a otro esquema como este:

    var mongoose = require('mongoose');
    var FolderSchema = new Schema({
      name: { type: string },
      permissions: [ { type: mongoose.Schema.Types.ObjectId, ref: 'Permission' } ]
    });
    
    module.exports = mongoose.model('Folder', FolderSchema);

    Y, a continuación, llamar a Folder.findOne().populate('permissions') para pedir la mangosta para rellenar el campo de permisos.

    Ahora, el siguiente:

    savePermission: function (folderId, permission, callback) {
        Folder.findOne({ _id: folderId }).populate('permissions').exec(function (err, data) {
            var perm = _.findWhere(data.permissions, { _id: permission._id });                
    
            _.extend(perm, permission);
    
            data.markModified("permissions");
            data.save(callback);
        });
    }

    La perm campo no será indefinido (si el permiso._id es, en realidad, en el conjunto de permisos), ya que ha sido poblada por Mangosta.

    • pero si lo hice como que, debo hacer una recogida separada de los permisos?
    • Sí, que es como la mangosta considerar que la permissionSchema. Cada esquema se asigna a un mongodb colección.
    • Uhm.. pero no estoy seguro de que quiero un sistema de recogida selectiva. ¿Cambia algo? O es sólo una lógica de la materia?
    • Voy a tratar de detalle de ambos enfoques en mi respuesta.

Dejar respuesta

Please enter your comment!
Please enter your name here