Insertar elementos en la matriz de mongo a través de mangosta

185

He buscado SO mucho buscando la respuesta, pero estoy seguro de que no tengo las palabras correctas para describir lo que busco.

Básicamente tengo una colección mongodb llamada 'people'. El esquema para esa colección es el siguiente:

people: {
         name: String, 
         friends: [{firstName: String, lastName: String}]
        }

Ahora, tengo una aplicación express muy básica que se conecta a la base de datos y crea con éxito 'personas' con una matriz de amigos vacía.

En un lugar secundario de la aplicación, hay un formulario para agregar amigos. El formulario toma firstName y lastName y luego POSTs con el campo de nombre también para referencia al objeto de personas adecuado.

Lo que me cuesta trabajo es crear un nuevo objeto amigo y luego "empujarlo" a la matriz de amigos.

Sé que cuando hago esto a través de la consola de mongo, uso la función de actualización con $ push como mi segundo argumento después de los criterios de búsqueda, pero parece que no puedo encontrar la forma adecuada de obtener mangosta para hacer esto.

db.people.update({name: "John"}, {$push: {friends: {firstName: "Harry", lastName: "Potter"}}});

ACTUALIZACIÓN: Entonces, la respuesta de Adrian fue muy útil. Lo siguiente es lo que hice para lograr mi objetivo.

en mi archivo app.js, configuro una ruta temporal usando

app.get('/addfriend', users.addFriend);

donde en mi archivo users.js tengo

exports.addFriend = function (req, res, next)
{
var friend = {"firstName": req.body.fName, "lastName": req.body.lName};
Users.findOneAndUpdate({name: req.user.name}, {$push: {friends: friend}});
};
Neurax
fuente
Veo que potencialmente podría usar collections.findOneAndUpdate (), pero no estoy seguro de dónde implementaría eso. En mi modelo?
Neurax

Respuestas:

282

Asumiendo, var friend = { firstName: 'Harry', lastName: 'Potter' };

Hay dos opciones que tiene:

Actualice el modelo en memoria y guarde (javascript array.push simple):

person.friends.push(friend);
person.save(done);

o

PersonModel.update(
    { _id: person._id }, 
    { $push: { friends: friend } },
    done
);

Siempre trato de elegir la primera opción cuando es posible, porque respetará más de los beneficios que la mangosta le brinda (ganchos, validación, etc.).

Sin embargo, si está haciendo muchas escrituras concurrentes, llegará a condiciones de carrera donde terminará con errores de versión desagradables para evitar que reemplace el modelo completo cada vez y pierda al amigo anterior que agregó. Así que solo ve al primero cuando sea absolutamente necesario.

Adrian Schneider
fuente
1
¿Pensé que la segunda opción era en realidad la más segura en términos de protección contra escritura concurrente? ¿Dijiste accidentalmente esto último cuando querías decir anterior?
Will Brickner el
37
Sí, acabo de comprobar y (en noviembre de 2017), que ES seguro de usar la mangosta updatefunción, mientras que NO ES seguro de encontrar un documento, modificarlo en la memoria, y luego llamar al .save()método en el documento. Cuando digo que una operación es "segura", significa que incluso si se aplican uno, dos o 50 cambios a un documento, todos se aplicarán con éxito y el comportamiento de las actualizaciones será el esperado. Esencialmente, la manipulación en la memoria no es segura porque podría estar trabajando en un documento desactualizado, por lo que cuando guarda, los cambios que ocurrieron después del paso de recuperación se pierden.
Will Brickner
2
@Will Brickner, un flujo de trabajo común para eso es envolver su lógica en un bucle de reintento: (recuperable contiene buscar, modificar, guardar). Si recupera un VersionError (excepción de bloqueo optimista), puede volver a intentar esa operación varias veces y obtener una nueva copia cada vez. Sin embargo, todavía prefiero las actualizaciones atómicas cuando sea posible.
Adrian Schneider
8
@ZackOfAllTrades también me confundió, pero creo que la función de devolución de llamada está hecha. let done = function(err, result) { // this runs after the mongoose operation }
usuario
¿Cómo obtener la identificación del objeto amigo en la devolución de llamada?
Dee Nix
41

El operador $ push agrega un valor especificado a una matriz.

{ $push: { <field1>: <value1>, ... } }

$ push agrega el campo de matriz con el valor como elemento.

La respuesta anterior cumple con todos los requisitos, pero lo hice funcionar haciendo lo siguiente

var objFriends = { fname:"fname",lname:"lname",surname:"surname" };
Friend.findOneAndUpdate(
   { _id: req.body.id }, 
   { $push: { friends: objFriends  } },
  function (error, success) {
        if (error) {
            console.log(error);
        } else {
            console.log(success);
        }
    });
)
Parth Raval
fuente
Este es el que he encontrado que funciona, así que creo que este es el más actualizado a partir de 2019. Creo que la mejor respuesta es la que falta de alguna manera y tal vez incompleta / desactualizada.
Cheesus Toast
¿Puedo señalar que puede tener un pequeño error en el código? Lo puse a trabajar porque puse la variable correcta para mi proyecto. Donde tenga Friend.findOneAndUpdate () Creo que debería ser People.findOneAndUpdate (). Eso sería más acorde con la pregunta original. Podría editarlo para usted, pero preferiría que lo verifique dos veces en caso de que esté equivocado (soy un poco nuevo en node / express).
Cheesus Toast
@CheesusToast Creo que si me equivoco, puedes cambiar la respuesta. He puesto esta respuesta que estaba funcionando bien para mí. pero si encuentra esta respuesta incorrecta, puede cambiar esta respuesta y me alegrará si me corrige, señor :-).
Parth Raval
1
OK, no hay problema, de todos modos solo fue una pequeña edición. Es un poco quisquilloso de mi parte porque los espectadores (como yo) habrían averiguado dónde se estaba empujando la matriz de todos modos. Todavía creo que esta debería ser la mejor respuesta :)
Cheesus Toast
10

Use $pushpara actualizar el documento e insertar un nuevo valor dentro de una matriz.

encontrar:

db.getCollection('noti').find({})

resultado para encontrar:

{
    "_id" : ObjectId("5bc061f05a4c0511a9252e88"),
    "count" : 1.0,
    "color" : "green",
    "icon" : "circle",
    "graph" : [ 
        {
            "date" : ISODate("2018-10-24T08:55:13.331Z"),
            "count" : 2.0
        }
    ],
    "name" : "online visitor",
    "read" : false,
    "date" : ISODate("2018-10-12T08:57:20.853Z"),
    "__v" : 0.0
}

actualizar:

db.getCollection('noti').findOneAndUpdate(
   { _id: ObjectId("5bc061f05a4c0511a9252e88") }, 
   { $push: { 
             graph: {
               "date" : ISODate("2018-10-24T08:55:13.331Z"),
               "count" : 3.0
               }  
           } 
   })

resultado para la actualización:

{
    "_id" : ObjectId("5bc061f05a4c0511a9252e88"),
    "count" : 1.0,
    "color" : "green",
    "icon" : "circle",
    "graph" : [ 
        {
            "date" : ISODate("2018-10-24T08:55:13.331Z"),
            "count" : 2.0
        }, 
        {
            "date" : ISODate("2018-10-24T08:55:13.331Z"),
            "count" : 3.0
        }
    ],
    "name" : "online visitor",
    "read" : false,
    "date" : ISODate("2018-10-12T08:57:20.853Z"),
    "__v" : 0.0
}
KARTHIKEYAN.A
fuente
2

Una manera fácil de hacerlo es usar lo siguiente:

var John = people.findOne({name: "John"});
John.friends.push({firstName: "Harry", lastName: "Potter"});
John.save();
Felipe Toledo
fuente
0

En mi caso, hice esto

  const eventId = event.id;
  User.findByIdAndUpdate(id, { $push: { createdEvents: eventId } }).exec();
Prathamesh Más
fuente
-3

Me encontré con este problema también. Mi solución fue crear un esquema hijo. Vea a continuación un ejemplo para sus modelos.

---- Modelo de persona

const mongoose = require('mongoose');
const SingleFriend = require('./SingleFriend');
const Schema   = mongoose.Schema;

const productSchema = new Schema({
  friends    : [SingleFriend.schema]
});

module.exports = mongoose.model('Person', personSchema);

*** Importante: SingleFriend.schema -> asegúrese de usar minúsculas para el esquema

--- esquema infantil

const mongoose = require('mongoose');
const Schema   = mongoose.Schema;

const SingleFriendSchema = new Schema({
  Name: String
});

module.exports = mongoose.model('SingleFriend', SingleFriendSchema);
theRealSheng
fuente
Posiblemente, la razón del voto negativo es porque esto no parece necesario. La respuesta de Parth Raval es suficiente.
Cheesus Toast