Dlaczego Mongoose ma zarówno schematy, jak i modele?

94

Te dwa typy obiektów wydają się być tak blisko siebie, że posiadanie obu wydaje się zbędne. Jaki jest sens posiadania zarówno schematów, jak i modeli?

Randomblue
źródło

Odpowiedzi:

61

Często najłatwiejszym sposobem odpowiedzi na tego typu pytania jest podanie przykładu. W takim razie ktoś już to za mnie zrobił :)

Spójrz tutaj:

http://rawberg.com/blog/nodejs/mongoose-orm-nested-models/

EDYCJA: Oryginalny post (jak wspomniano w komentarzach) wydaje się już nie istnieć, więc powielam go poniżej. Jeśli kiedykolwiek wróci lub właśnie się poruszył, daj mi znać.

Daje przyzwoity opis korzystania ze schematów w modelach w mangusta i dlaczego chciałbyś to zrobić, a także pokazuje, jak przesuwać zadania za pośrednictwem modelu, podczas gdy schemat dotyczy struktury itp.

Oryginalny post:

Zacznijmy od prostego przykładu osadzania schematu wewnątrz modelu.

var TaskSchema = new Schema({
    name: String,
    priority: Number
});

TaskSchema.virtual('nameandpriority')
    .get( function () {
        return this.name + '(' + this.priority + ')';
    });

TaskSchema.method('isHighPriority', function() {
    if(this.priority === 1) {
        return true;
    } else {
        return false;
    }
}); 

var ListSchema = new Schema({
    name: String,
    tasks: [TaskSchema]
});

mongoose.model('List', ListSchema);

var List = mongoose.model('List');

var sampleList = new List({name:'Sample List'});

Utworzyłem nowy TaskSchemaobiekt z podstawowymi informacjami, które może mieć zadanie. Wirtualny atrybut Mongoose jest skonfigurowany tak, aby wygodnie łączyć nazwę i priorytet zadania. Podałem tutaj tylko metodę pobierającą, ale obsługiwane są również metody ustawiające wirtualne.

Zdefiniowałem również prostą metodę zadaniową o nazwie, isHighPriorityaby zademonstrować, jak działają metody w tej konfiguracji.

W ListSchemadefinicji zauważysz, jak klucz zadań jest skonfigurowany do przechowywania tablicy TaskSchemaobiektów. Klucz zadania stanie się instancją, DocumentArrayktóra zapewnia specjalne metody radzenia sobie z osadzonymi dokumentami Mongo.

Na razie przekazałem ListSchemaobiekt tylko do mongoose.model i zostawiłem TaskSchema wyłączony. Technicznie nie jest konieczne przekształcanie go TaskSchemaw model formalny, ponieważ nie będziemy go zapisywać w jego własnej kolekcji. Później pokażę ci, jak nic nie szkodzi, jeśli to zrobisz, i może pomóc uporządkować wszystkie modele w ten sam sposób, zwłaszcza gdy zaczynają obejmować wiele plików.

Przy Listkonfiguracji modelu dodajmy do niego kilka zadań i zapiszmy je w Mongo.

var List = mongoose.model('List');
var sampleList = new List({name:'Sample List'});

sampleList.tasks.push(
    {name:'task one', priority:1}, 
    {name:'task two', priority:5}
);

sampleList.save(function(err) {
    if (err) {
        console.log('error adding new list');
        console.log(err);
    } else {
        console.log('new list successfully saved'); 
    }
});

Atrybut zadań na instancji naszego Listmodelu ( simpleList) działa jak zwykła tablica JavaScript i możemy dodawać do niego nowe zadania za pomocą metody push. Należy zwrócić uwagę na to, że zadania są dodawane jako zwykłe obiekty JavaScript. To subtelne rozróżnienie, które może nie być od razu intuicyjne.

Możesz sprawdzić z powłoki Mongo, że nowa lista i zadania zostały zapisane w mongo.

db.lists.find()
{ "tasks" : [
    {
        "_id" : ObjectId("4dd1cbeed77909f507000002"),
        "priority" : 1,
        "name" : "task one"
    },
    {
        "_id" : ObjectId("4dd1cbeed77909f507000003"),
        "priority" : 5,
        "name" : "task two"
    }
], "_id" : ObjectId("4dd1cbeed77909f507000001"), "name" : "Sample List" }

Teraz możemy użyć ObjectIddo wyciągnięcia Sample Listi iteracji przez jego zadania.

List.findById('4dd1cbeed77909f507000001', function(err, list) {
    console.log(list.name + ' retrieved');
    list.tasks.forEach(function(task, index, array) {
        console.log(task.name);
        console.log(task.nameandpriority);
        console.log(task.isHighPriority());
    });
});

Jeśli uruchomisz ten ostatni fragment kodu, pojawi się błąd informujący, że osadzony dokument nie ma metody isHighPriority. W aktualnej wersji Mongoose nie ma bezpośredniego dostępu do metod w osadzonych schematach. Jest otwarte zgłoszenie, aby to naprawić, a po zadaniu pytania Mongoose Google Group, manimal45 opublikował pomocne obejście do teraz.

List.findById('4dd1cbeed77909f507000001', function(err, list) {
    console.log(list.name + ' retrieved');
    list.tasks.forEach(function(task, index, array) {
        console.log(task.name);
        console.log(task.nameandpriority);
        console.log(task._schema.methods.isHighPriority.apply(task));
    });
});

Jeśli uruchomisz ten kod, powinieneś zobaczyć następujące dane wyjściowe w wierszu poleceń.

Sample List retrieved
task one
task one (1)
true
task two
task two (5)
false

Mając to na uwadze, przejdźmy TaskSchemado modelu Mongoose.

mongoose.model('Task', TaskSchema);

var Task = mongoose.model('Task');

var ListSchema = new Schema({
    name: String,
    tasks: [Task.schema]
});

mongoose.model('List', ListSchema);

var List = mongoose.model('List');

TaskSchemaDefinicja jest taka sama jak wcześniej, więc zostawiłem ją. Po przekształceniu go w model nadal możemy uzyskać dostęp do jego podstawowego obiektu Schema za pomocą notacji kropkowej.

Utwórzmy nową listę i umieśćmy w niej dwie instancje modelu zadań.

var demoList = new List({name:'Demo List'});

var taskThree = new Task({name:'task three', priority:10});
var taskFour = new Task({name:'task four', priority:11});

demoList.tasks.push(taskThree.toObject(), taskFour.toObject());

demoList.save(function(err) {
    if (err) {
        console.log('error adding new list');
        console.log(err);
    } else {
        console.log('new list successfully saved'); 
    }
});

Gdy osadzamy instancje modelu zadania na liście, wzywamy toObjectje do konwersji ich danych na zwykłe obiekty JavaScript, których List.tasks DocumentArrayoczekuje. Gdy zapiszesz instancje modelu w ten sposób, twoje osadzone dokumenty będą zawierały ObjectIds.

Pełny przykład kodu jest dostępny jako streszczenie . Mam nadzieję, że te obejścia pomogą załagodzić sytuację, ponieważ Mongoose nadal się rozwija. Wciąż jestem całkiem nowy w Mongoose i MongoDB, więc zachęcam do dzielenia się lepszymi rozwiązaniami i wskazówkami w komentarzach. Miłego modelowania danych!

Adam Comerford
źródło
3
Generalnie odradza się przesyłanie odsłoniętych linków jako odpowiedzi na pytania zamieszczone w SO, ponieważ łącze może przestać działać (jak w tym przypadku). Przynajmniej skopiuj / wklej i zacytuj odpowiednie sekcje artykułów, do których prowadzą linki.
βξhrαng
1
zrobione - to wciąż było w pamięci podręcznej Google, więc stosunkowo proste
Adam Comerford
1
Dla przypomnienia,
Dakota
5
Nie próbuję padać na czyjąś paradę, ale ta odpowiedź brzmi bardziej jak samouczek: odpowiedź na pytanie, jak, ale nie dlaczego. Pomimo mniejszej liczby
pozytywnych
2
Widziałem tę odpowiedź (i zagłosowałem za nią), ta została odebrana i zaakceptowana ponad 2 lata wcześniej. Cieszę się, że można znaleźć lepszą odpowiedź, nie ma deszczu na czyjejś paradzie, a od lutego 2015 r. Był link do odpowiedzi, o której wspomniałeś w komentarzach do pytania, więc nie czułem potrzeby, aby go łączyć.
Adam Comerford,
54

Schemat to obiekt, który definiuje strukturę wszystkich dokumentów, które będą przechowywane w kolekcji MongoDB; umożliwia definiowanie typów i walidatorów dla wszystkich elementów danych.

Model to obiekt, który zapewnia łatwy dostęp do nazwanej kolekcji, umożliwiając wysyłanie zapytań do kolekcji i używanie schematu do walidacji dokumentów zapisywanych w tej kolekcji. Jest tworzony przez połączenie schematu, połączenia i nazwy kolekcji.

Pierwotnie sformułowane przez Valeri Karpov, blog MongoDB

Zeeshan Hassan Memon
źródło
5

Nie sądzę, aby zaakceptowana odpowiedź faktycznie odpowiadała na postawione pytanie. Odpowiedź nie wyjaśnia, dlaczego Mongoose zdecydował się wymagać od programisty dostarczenia zarówno schematu, jak i zmiennej modelu. Przykład frameworka, w którym wyeliminowano potrzebę deweloperaDo zdefiniowania schematu danych służy django - programista zapisuje swoje modele w pliku models.py i pozostawia to frameworkowi w celu zarządzania schematem. Pierwszym powodem, dla którego to robią, biorąc pod uwagę moje doświadczenie z django, jest łatwość użycia. Może ważniejsza jest zasada DRY (nie powtarzaj się) - nie musisz pamiętać o aktualizowaniu schematu przy zmianie modelu - django zrobi to za Ciebie! Railsy zarządzają również schematem danych za Ciebie - programista nie edytuje schematu bezpośrednio, ale zmienia go, definiując migracje, które manipulują schematem.

Jednym z powodów, dla których mogłem zrozumieć, że Mongoose oddzieliłby schemat od modelu, są przypadki, w których chciałbyś zbudować model z dwóch schematów. Taki scenariusz może wprowadzić więcej złożoności, niż warto zarządzać - jeśli masz dwa schematy zarządzane przez jeden model, dlaczego nie są one jednym schematem?

Być może pierwotne pytanie jest raczej reliktem tradycyjnego systemu relacyjnych baz danych. W świecie NoSQL / Mongo być może schemat jest nieco bardziej elastyczny niż MySQL / PostgreSQL, a zatem zmiana schematu jest bardziej powszechną praktyką.

johnklawlor
źródło
Jakby schemat kontra model nie był wystarczający. Powtarzanie siebie, napotykasz więcej duplikatów, gdy próbujesz utrzymać zgodny interfejs TypeScript , a nawet więcej podczas tworzenia schematu GraphQL.
Dan Dascalescu
0

Zrozumieć dlaczego? musisz zrozumieć, czym właściwie jest Mongoose?

Cóż, mangusta to biblioteka do modelowania danych obiektowych dla MongoDB i Node JS, zapewniająca wyższy poziom abstrakcji. Jest to więc trochę jak relacja między Express i Node, więc Express jest warstwą abstrakcji nad zwykłym Node, podczas gdy Mongoose jest warstwą abstrakcji nad zwykłym sterownikiem MongoDB.

Biblioteka modelowania danych obiektowych jest dla nas tylko sposobem na napisanie kodu JavaScript, który będzie następnie współdziałać z bazą danych. Więc moglibyśmy po prostu użyć zwykłego sterownika MongoDB, aby uzyskać dostęp do naszej bazy danych, działałoby dobrze.

Ale zamiast tego używamy Mongoose, ponieważ zapewnia nam o wiele więcej funkcjonalności po wyjęciu z pudełka, umożliwiając szybsze i prostsze tworzenie naszych aplikacji.

Tak więc niektóre funkcje Mongoose daje nam schematy do modelowania naszych danych i relacji, łatwą weryfikację danych, proste API zapytań, oprogramowanie pośredniczące i wiele więcej.

W Mongoose schemat to miejsce, w którym modelujemy nasze dane, opisujemy strukturę danych, wartości domyślne i walidację, a następnie bierzemy ten schemat i tworzymy z niego model, model jest w zasadzie opakowaniem wokół schematu, co pozwala nam faktycznie komunikować się z bazą danych w celu tworzenia, usuwania, aktualizowania i czytania dokumentów.

wprowadź opis obrazu tutaj

Stwórzmy model ze schematu.

const tourSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'A tour must have a name'],
    unique: true,
  },
  rating: {
    type: Number,
    default: 4.5,
  },
  price: {
    type: Number,
    required: [true, 'A tour must have a price'],
  },
});
//tour model
const Tour = mongoose.model('Tour', tourSchema);

Zgodnie z umową pierwsza litera nazwy modelu musi być wielka.

Stwórzmy instancję naszego modelu, który stworzyliśmy przy użyciu mongoose i schematu. również wchodź w interakcję z naszą bazą danych.

const testTour = new Tour({ // instance of our model
  name: 'The Forest Hiker',
  rating: 4.7,
  price: 497,
});
 // saving testTour document into database
testTour
  .save()
  .then((doc) => {
    console.log(doc);
  })
  .catch((err) => {
    console.log(err);
  });

Tak więc posiadanie zarówno schamy, jak i modle mangusty ułatwia nam życie.

Lord
źródło