2011-10-30 17 views
5

Używam Mongoose modelować Person i Transaction kolekcji, gdzie każdy Transaction będą miały odniesienie do dwóch różnych Person przypadkach:Mongoose: Jak modelować klucz obcy/odwrotną relację?

var TransactionSchema = new Schema({ 
    , amount   : { type: Number, required: true } 
    , from   : { type: ObjectId, required: true } 
    , to    : { type: ObjectId, required: true } 
    , date   : Date 
}); 

var PersonSchema = new Schema({ 
    name   : { type: String, required: true } 
    , transactions : [ObjectId] 
}); 

Chciałbym każdy Person mieć kolekcję wszystkich Transaction s, że są one albo wartość to lub from dla. Jak dotąd, jest to najlepszy sposób udało mi się dowiedzieć, jak to zrobić:

TransactionSchema.pre('save', function(next, done) { 
    var transaction = this; 

    Person.findById(this.to, function (err, person) { 
     person.transactions.push(transaction); 
     person.save(); 
    }); 

    Person.findById(this.from, function (err, person) { 
     person.transactions.push(transaction); 
     person.save(); 
    }); 

    next(); 
}); 

Wydaje się nadmierne. Czy istnieje lepszy sposób na to, czy próbuję użyć MongoDB zbyt wiele jak relacyjnej bazy danych? Zamiast kolekcjonowania Transaction s skojarzonych z każdą instancją Person, czy powinienem zapytać bezpośrednio o kolekcję Translation?

Dziękuję.

Odpowiedz

7

Musisz przemyśleć więcej na temat zapytań, które zamierzasz wykonać w bazie danych podczas projektowania schematu MongoDB.

Spróbuj zduplikować dane dla prędkości i odnieść je do integralności. Co to znaczy?
Cóż, na przykład, gdy wysyłasz zapytanie do Transakcji, domyślam się, że nie potrzebujesz wszystkich danych użytkownika od pierwszego razu nie? (Musisz e-mail użytkownika, lokalizacja przy wyświetlaniu informacji o transakcji?)
myślę, że po prostu prawdopodobnie potrzebować identyfikator użytkownika i nazwę użytkownika, więc należy zrobić coś takiego:

var TransactionSchema = new Schema({ 
    , amount   : { type: Number, required: true } 
    , from   : { 
    user_id: { 
     type: ObjectId 
    , required: true 
    } 
    , username: { 
     type: String 
    , required: true 
    } 
    } 
    , to    : { 
    user_id: { 
     type: ObjectId 
    , required: true 
    } 
    , username: { 
     type: String 
    , required: true 
    } 
    } 
    , date   : Date 
}); 

Więc zamiast wykonania 3 zapytań o stronę wyświetlającą szczegóły transakcji (jedną dla transakcji i 2 dodatkowe zapytania dla nazw użytkowników), będziesz miał tylko jedną.
To tylko przykład, można zastosować tę samą logikę dla schematu użytkownika, w zależności od tego, co próbujesz osiągnąć.

W każdym razie nie uważam, że twoje oprogramowanie pośrednie jest w porządku, ponieważ nie sprawdzasz tam błędów (zawsze dzwonisz dalej bez względu na wszystko). W ten sposób chciałbym napisać middleware (nie testowałem, ale idea jest ważna):

TransactionSchema.pre('save', function(next, done) { 
    var transaction = this; 

    Person.where('_id').in([this.to, this.from]).run(function (err, people) { 
    if (people.length != 2) { next(new Error("To or from doesn't exist")); return; } 
    Step(
     function save_to() { 
     people[0].transactions.push(transaction); 
     people[0].save(this); 
     }, 
     function save_from(err) { 
     if (err) { next(err); return; } 
     people[1].transactions.push(transaction); 
     people[1].save(this); 
     }, 
     function callback(err) { 
     next(err); 
     } 
    ); 
    }); 
}); 

W powyższym kodzie używam Step bibliotekę dla sterowania przepływem, a ja tylko za pomocą jednego zapytania zamiast dwóch (podczas wyszukiwania "do" i "od").

+0

muchos! po prostu chcę, abym przeczytał –

+0

Czy lepiej to zrobić w pre-save lub po save. Co się stanie, jeśli transakcja nie zostanie zapisana z powodu błędów sprawdzania poprawności lub jakiegokolwiek innego błędu. Czy nie dodałeś osobiście manekina? Jeśli zrobisz to w post-save, nie będziesz musiał ponownie pytać o transakcję – raju

Powiązane problemy