Empurre itens para o array mongo via mongoose

185

Eu procurei MUITO procurando respostas, mas tenho certeza de que estou perdida pelas palavras certas para descrever o que estou procurando.

Basicamente, eu tenho uma coleção mongodb chamada 'people'. O esquema para essa coleção é o seguinte:

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

Agora, eu tenho um aplicativo expresso muito básico que se conecta ao banco de dados e cria com êxito 'pessoas' com uma matriz de amigos vazia.

Em um local secundário no aplicativo, existe um formulário para adicionar amigos. O formulário recebe firstName e lastName e, em seguida, POSTs com o campo name também para referência ao objeto de pessoas apropriado.

O que estou tendo dificuldade em criar é criar um novo objeto amigo e depois "empurrá-lo" para a matriz de amigos.

Eu sei que quando faço isso pelo console mongo, uso a função update com $ push como meu segundo argumento após o critério de pesquisa, mas não consigo encontrar a maneira apropriada de obter o mangusto para fazer isso.

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

ATUALIZAÇÃO: Então, a resposta de Adrian foi muito útil. A seguir, foi o que fiz para alcançar meu objetivo.

no meu arquivo app.js, defino uma rota temporária usando

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

onde no meu arquivo users.js eu tenho

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
fonte
Vejo que eu poderia usar as coleções.findOneAndUpdate (), mas não tenho certeza de onde implementaria isso? No meu modelo?
Neurax 10/10

Respostas:

282

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

Existem duas opções que você tem:

Atualize o modelo na memória e salve (javascript simples array.push):

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

ou

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

Eu sempre tento escolher a primeira opção sempre que possível, porque ela respeitará mais os benefícios que o mangusto oferece a você (ganchos, validação etc.).

No entanto, se você estiver gravando muitas gravações simultâneas, encontrará condições de corrida nas quais ocorrerá erros de versão desagradáveis ​​para impedir que você substitua o modelo inteiro a cada vez e perca o amigo anterior que você adicionou. Então, só vá para o primeiro quando for absolutamente necessário.

Adrian Schneider
fonte
1
Eu pensei que a segunda opção era realmente a mais segura em termos de proteção contra gravação simultânea? Você acidentalmente disse o último quando quis dizer o primeiro?
que você precisa
37
Sim, eu apenas verificado e (em novembro de 2017), que é seguro usar o mangusto updatefunção, enquanto que NÃO É seguro para encontrar um documento, modificá-lo na memória e, em seguida, chamar o .save()método no documento. Quando digo que uma operação é 'segura', significa que, mesmo que uma, duas ou 50 alterações estejam sendo aplicadas a um documento, todas serão aplicadas com êxito e o comportamento das atualizações será o esperado. A manipulação essencialmente na memória é insegura porque você pode estar trabalhando em um documento desatualizado, portanto, ao salvar, as alterações que ocorreram após a etapa de busca são perdidas.
que você precisa
2
@ Will Brickner, um fluxo de trabalho comum para isso é agrupar sua lógica em um loop de repetição: (repetível contém buscar, modificar, salvar). Se você recuperar um VersionError (exceção de bloqueio otimista), poderá tentar novamente essa operação algumas vezes e buscar uma nova cópia a cada vez. Eu ainda prefiro atualizações atômicas quando possível!
Adrian Schneider
8
@ZackOfAllTrades Também me confundiu, mas acredito que feito é a função de retorno de chamada. let done = function(err, result) { // this runs after the mongoose operation }
usuário
como obter o id do objeto amigo no retorno de chamada?
Dee Nix
41

O operador $ push acrescenta um valor especificado a uma matriz.

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

$ push adiciona o campo da matriz com o valor como elemento.

A resposta acima atende a todos os requisitos, mas eu consegui fazê-lo fazendo o seguinte

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
fonte
Foi o que achei que funcionou, então acho que esse é o mais atualizado a partir de 2019. O que é a melhor resposta que acho que está faltando de alguma forma e talvez incompleto / desatualizado.
Cheesus Toast
Quero apenas salientar que você pode ter um pequeno erro no código. Eu consegui funcionar porque coloquei a variável correta no meu projeto. Onde você tem Friend.findOneAndUpdate () acho que deve ser People.findOneAndUpdate (). Isso estaria mais de acordo com a pergunta original. Eu poderia editá-lo para você, mas eu prefiro que você apenas verifique se eu estiver errado (sou um pouco novo em nó / expresso).
Cheesus Toast
@CheesusToast Acho que, se estiver errado, você pode alterar a resposta. Eu coloquei esta resposta que estava funcionando bem para mim. mas se você encontrar esta resposta errada, poderá alterar esta resposta e ficarei feliz se você me corrigir, senhor :-).
Parth Raval
1
OK, não há problema, era apenas uma pequena edição de qualquer maneira. É meio que exigente da minha parte porque os espectadores (como eu) teriam descoberto para onde a matriz estava sendo pressionada. Eu ainda acho que este deve ser melhor resposta :)
Cheesus Toast
10

Use $pushpara atualizar o documento e inserir novo valor dentro de uma 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
}

atualizar:

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

resultado para atualização:

{
    "_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
fonte
2

Uma maneira fácil de fazer isso é usar o seguinte:

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

No meu caso, eu fiz isso

  const eventId = event.id;
  User.findByIdAndUpdate(id, { $push: { createdEvents: eventId } }).exec();
Prathamesh Mais
fonte
-3

Também encontrei esse problema. Minha correção foi criar um esquema filho. Veja abaixo um exemplo para seus modelos.

---- Modelo de pessoa

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 -> certifique-se de usar letras minúsculas para o esquema

--- Esquema filho

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

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

module.exports = mongoose.model('SingleFriend', SingleFriendSchema);
theRealSheng
fonte
Possivelmente, o motivo do voto negativo é porque isso não parece necessário. A resposta de Parth Raval é bastante suficiente.
Cheesus Toast