猫鼬:如何建模外键/反向关系?

Bry*_*ace 5 mongoose mongodb node.js

我使用Mongoose来建模PersonTransaction集合,其中每个Transaction都会引用两个不同的Person实例:

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]
});
Run Code Online (Sandbox Code Playgroud)

我希望每个Person人都拥有所有Transactions 的集合,无论是它们to还是from值.到目前为止,这是我能够弄清楚如何做到这一点的最好方法:

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();
});
Run Code Online (Sandbox Code Playgroud)

这似乎过分了.有没有更好的方法来做到这一点,还是我试图像关系数据库那样使用MongoDB?我应该直接查询集合,而不是拥有Transaction与每个Person实例关联的s Translation集合吗?

谢谢.

ale*_*lex 7

在设计MongoDB架构时,您必须更多地考虑要在数据库上执行的查询.

尝试复制数据以提高速度并引用它以保证完整性.那是什么意思?
那么,例如当你查询一个事务时,我猜你不需要第一次没有所有的用户细节吗?(在交易中显示信息时,您是否需要用户的电子邮件,位置?)
我认为您可能只需要用户ID和用户名,因此您应该执行以下操作:

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
});
Run Code Online (Sandbox Code Playgroud)

因此,对于显示交易详细信息的页面(一个用于事务,另外两个用于用户名的查询),您不需要执行3次查询,而是只有一个查询.
这只是一个示例,您可以为User模式应用相同的逻辑,具体取决于您要实现的目标.

无论如何我不认为你的中间件是好的,因为你没有检查那里的错误(无论如何,你总是打电话给下一个).这就是我编写中间件的方式(没有测试,但这个想法很重要):

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); 
      }
    );
  });
});
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我使用Step库进行流控制,我只使用一个查询而不是两个(当搜索"to"和"from"时).