Wepchnij elementy do tablicy mongo za pośrednictwem mongoose


185

Przeszukałem TAK trochę, szukając odpowiedzi, ale jestem pewien, że brakuje mi odpowiednich słów, aby opisać, czego szukam.

Zasadniczo mam kolekcję mongodb o nazwie „people”. Schemat tej kolekcji jest następujący:

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

Teraz mam bardzo podstawową aplikację ekspresową, która łączy się z bazą danych i pomyślnie tworzy „ludzi” z pustą tablicą znajomych.

W drugorzędnym miejscu w aplikacji znajduje się formularz do dodawania znajomych. Formularz przyjmuje nazwy firstName i lastName, a następnie POST z polem nazwy również w celu odniesienia do odpowiedniego obiektu osób.

Trudno mi jest stworzyć nowy obiekt przyjaciela, a następnie „wypchnąć” go do tablicy znajomych.

Wiem, że kiedy robię to za pomocą konsoli mongo, używam funkcji aktualizacji z $ push jako drugim argumentem po kryteriach wyszukiwania, ale nie mogę znaleźć odpowiedniego sposobu, aby zmusić mongoose do zrobienia tego.

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

AKTUALIZACJA: Więc odpowiedź Adriana była bardzo pomocna. Oto, co zrobiłem, aby osiągnąć swój cel.

w moim pliku app.js ustawiłem trasę tymczasową za pomocą

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

gdzie w moim pliku users.js mam

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}});
};

Widzę, że mógłbym potencjalnie użyć collections.findOneAndUpdate (), ale nie jestem pewien, gdzie bym to zaimplementował? W moim modelu?
Neurax

Odpowiedzi:


282

Zarozumiały, var friend = { firstName: 'Harry', lastName: 'Potter' };

Masz do wyboru dwie opcje:

Zaktualizuj model w pamięci i zapisz (zwykły javascript array.push):

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

lub

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

Zawsze staram się wybierać pierwszą opcję, jeśli to możliwe, ponieważ będzie ona szanować więcej korzyści, jakie daje mangusta (haczyki, walidacja itp.).

Jeśli jednak wykonujesz wiele równoczesnych zapisów, napotkasz warunki wyścigu, w których skończysz z paskudnymi błędami wersji, aby powstrzymać cię przed zamianą całego modelu za każdym razem i utratą poprzedniego dodanego znajomego. Więc idź do tego pierwszego tylko wtedy, gdy jest to absolutnie konieczne.


1
Myślałem, że druga opcja jest w rzeczywistości bezpieczniejsza pod względem równoczesnej ochrony przed zapisem? Czy przypadkowo powiedziałeś drugie, kiedy chciałeś powiedzieć poprzednie?
Will Brickner

37
Tak, właśnie sprawdziłem i (w listopadzie 2017), to JEST bezpiecznie korzystać z Mongoose updatefunkcję, a to NIE JEST bezpieczne znaleźć dokument, modyfikować je w pamięci, a następnie wywołać .save()metodę na dokumencie. Kiedy mówię, że operacja jest „bezpieczna”, oznacza to, że nawet jeśli jedna, dwie lub 50 zmian jest wprowadzanych do dokumentu, wszystkie zostaną pomyślnie zastosowane, a aktualizacje będą działać zgodnie z oczekiwaniami. Zasadniczo manipulacja w pamięci jest niebezpieczna, ponieważ możesz pracować nad nieaktualnym dokumentem, więc po zapisaniu zmiany, które nastąpiły po kroku pobierania, zostaną utracone.
Will Brickner

2
@Will Brickner Typowy przepływ pracy w tym celu polega na zawinięciu logiki w pętlę ponawiania: (ponawianie obejmuje pobieranie, modyfikowanie, zapisywanie). Jeśli otrzymasz VersionError (wyjątek optymistycznej blokady), możesz powtórzyć tę operację kilka razy i za każdym razem pobierać nową kopię. Jednak nadal wolę aktualizacje atomowe, jeśli to możliwe!
Adrian Schneider

8
@ZackOfAllTrades też mnie zdezorientował, ale uważam, że to funkcja wywołania zwrotnego. let done = function(err, result) { // this runs after the mongoose operation }
użytkownik

jak uzyskać identyfikator obiektu znajomego w wywołaniu zwrotnym?
Dee Nix

41

Operator $ push dołącza określoną wartość do tablicy.

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

$ push dodaje pole tablicy z wartością jako elementem.

Powyższa odpowiedź spełnia wszystkie wymagania, ale udało mi się to, wykonując następujące czynności

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);
        }
    });
)

To jest ta, która działa, więc myślę, że jest najbardziej aktualna na rok 2019. Ta, która moim zdaniem jest najlepszą odpowiedzią, w jakiś sposób brakuje i może być niekompletna / nieaktualna.
Toast Cheesus

Mogę tylko zaznaczyć, że możesz mieć niewielki błąd w kodzie. Mam to do pracy, ponieważ umieściłem poprawną zmienną dla mojego projektu. Gdzie masz Friend.findOneAndUpdate () Myślę, że powinno to być People.findOneAndUpdate (). Byłoby to bardziej zgodne z pierwotnym pytaniem. Mógłbym to dla Ciebie edytować, ale wolałbym, abyś dokładnie to sprawdził na wypadek, gdyby się mylił (jestem trochę nowy w node / express).
Toast Cheesus

@CheesusToast Myślę, że jeśli się mylę, możesz zmienić odpowiedź. ułożyłem tę odpowiedź, która działała dobrze dla mnie. ale jeśli uznasz, że ta odpowiedź jest błędna, możesz zmienić tę odpowiedź i będę zadowolony, jeśli mnie poprawisz, sir :-).
Parth Raval

1
OK, nie ma problemu, i tak była to tylko mała edycja. To trochę wybredne z mojej strony, ponieważ widzowie (tacy jak ja) i tak zorientowaliby się, gdzie tablica została przesunięta. Nadal uważam, że to powinna być najlepsza odpowiedź :)
Toast Cheesus

9

Służy $pushdo aktualizowania dokumentu i wstawiania nowej wartości do tablicy.

odnaleźć:

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

wynik dla znalezienia:

{
    "_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
}

aktualizacja:

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

wynik do aktualizacji:

{
    "_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
}

2

Prostym sposobem na to jest użycie:

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

0

W moim przypadku to zrobiłem

  const eventId = event.id;
  User.findByIdAndUpdate(id, { $push: { createdEvents: eventId } }).exec();

-3

Natknąłem się również na ten problem. Moją poprawką było utworzenie schematu potomnego. Zobacz poniżej przykład dla swoich modeli.

---- Model osoby

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);

*** Ważne: SingleFriend.schema -> upewnij się, że w schemacie używasz małych liter

--- Schemat podrzędny

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

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

module.exports = mongoose.model('SingleFriend', SingleFriendSchema);

Prawdopodobnie przyczyną negatywnego głosu jest to, że nie wydaje się to konieczne. Odpowiedź Parth Raval jest wystarczająca.
Toast Cheesus
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.