Sails.js:模型本身的一对多关系

我正在尝试使用Sails.js和MongoDB来构build一个支持多级类别的简单应用程序。 一个类别可以有许多子类别。 所以在我的Category模型中,我在自己的内部build立了一对多关系类别

 module.exports = { adapter: 'mongo', attributes: { categoryTitle: { type: 'string', required: true }, //owner or parent of the one-to-many relationship parentCat: { model: 'category' }, //sub categories subCategories: { collection: 'category', via: 'parentCat' }, }}; 

这些类别使用​​AngularJS中的ng-repeat指令显示在select下拉列表中,例如

 <select class="selector3" id="categoryParent" ng-model="category.parent"> <option>Choose a category or none</option> <option ng-repeat="cat in categories" value="{{cat.categoryTitle}}">{{cat.categoryTitle}}</option> </select> 

AngularJS的CategoryController中 ,我使用$scope.category.parent捕获所选类别的值(作为父类别)。 这一步工作,因为我能够通过console.log($scope.category.parent);看到选定的值console.log($scope.category.parent);

但是,当我将新类别与其父类别一起保存到MongoDB时, 即使其他字段已正确保存父类别也为null ,如Mongoterminal所示。 我想这个问题与我为保存Sails中的新类而写的'create'API有关 。 你能发现在下面的代码中可能是错的吗?

 create: function(req, res, next) { var params = req.allParams(); // set parent category if exists if (params.parentCat) { var parentCategory = Category.findOne({categoryTitle : params.parentCat}) .exec(function(err, category) { if (err) { return null; //not found } return category; //found, return the category }); params.parentCat = parentCategory; //set the parent category in params } //insert the new category to MongoDB Category.create(params, function(err, category) { if (err) { console.log('category addition failed'); return next(err); } console.log('successfully added the category: ' + category.categoryTitle); res.redirect('/category'); }); } //create 

你在这里混淆了一些概念。

首先

 var parentCategory = Category.findOne({categoryTitle : params.parentCat}) 

请注意,waterline方法findOne不会从数据库中返回文档。 所以,上面的陈述不会达到你打算达到的目的。 重要的是您在exec函数中提供的callback函数。 像这样想:

Node.js事件循环将只执行Category.findOne,并继续前进到下一个语句,而不用等待findOne的完成。 这里的下一个陈述是:

 params.parentCat = parentCategory 

请注意,从MongoDB中获取尚未完成。 即使完成,findOne也不会像上面所说的那样将引用返回给数据库中的文档。 所以,这不是在Node.js中完成的事情。 相反,如果你想访问数据库对象,它存在于exec中的callback函数的类variables中。

现在第二个问题是下面的陈述

 return category; 

如你所知,将会使你完全脱离创build动作。 另外,这里需要注意的一点是,在这个声明和底部的redirect语句之间将会出现竞争。 这是为什么

Node.js事件循环执行它遇到的每个语句。 如果语句是一个阻塞语句(如var x = <some computation without callback function> ),那么它将停留在该语句处并等待其完成。 否则,如果语句与callback函数(如Category.findOne({}).exec(function (err, category){}) )的callback函数是asynchronous的Category.findOne({}).exec(function (err, category){})则事件循环将移至下一个语句,callbackCategory.findOne({}).exec(function (err, category){})将作为当findOne完成时。

现在,在这种情况下,findOne将被执行,并且事件循环将会继续

 params.parentCat = parentCategory; 

这不是一个asynchronous的声明,但它瞬间完成,事件循环移动到

 Category.create() 

现在,你可以把它想象为findOne和create将在mongoDB中并行执行。 一旦他们的数据库操作完成,callback函数将被执行。 无论首先执行哪个callback,它的return语句都会被执行。 所以,这里的行为是不可预测的。 return category可能执行或res.redirect('/category'); 威力。

我已经重写了上面提到的所有要点的创build函数

 create: function(req, res, next) { var params = req.params.all(); // set parent category if exists if (params.parentCat) { Category.findOne({categoryTitle : params.parentCat}).exec(function (err, parentCategory) { if (err) { return res.json(500, 'Sever error'); //not found } else{ params.parentCat = parentCategory.id; //set the parent category in params //insert the new category to MongoDB Category.create(params, function (err, category) { if (err) { console.log('category addition failed'); return next(err); } console.log('successfully added the category: ' + category.categoryTitle); res.redirect('/category'); }); } }); } else{ return res.json(500,'Server error. Parent category required'); } }