我是sails.js的新手,我正在使用“sails.js与Mongodb”。 我在我的风帆应用程序中使用populate时遇到深层关联问题。
我有这样的关系:
Category has many to many relationship with Article.
City has one to many relationship with Areas.
Article has one to one relationship with City and Areas.
Category.js
module.exports = {
schema: true,
attributes: {
//add referecnce to other article
Articles: {
collection: 'Article',
via:'ref_category_id'
},
category_name: {
type:'string',
required: true,
unique: true },
}
};
Article.js
module.exports = {
schema: true,
attributes: {
//adding reference to category
ref_category_id: {
collection:'Category',
via:'Articles'
},
//adding reference to city
ref_city_id: {
model:'City'
},
//add a reference to area
ref_area_id: {
model:'Areas'
},
//adding reference to tags
tags: {
collection: 'Tag',
via:'articles'
},
title:{ type:'string',required:true},
blurb: { type: 'string'},
description:{ type:'string'}
}
};
City.js
module.exports = {
schema: true,
attributes: {
Areas: {
collection: 'Areas',
via:'ref_city_id'
},
ref_article_id:{
model:'Article'
},
city_name: { type:'string',
required:true,
unique:true }
}
};
Areas.js
module.exports = {
schema: true,
attributes: {
area_name: { type:'string',required:true},
latitude: { type:'float'},
longitude: { type:'float'},
ref_city_id: {
model: 'City'
},
ref_article_id:{
model:'Article'
}
}
};
Tag.js
module.exports = {
schema:false,
attributes: {
//adding reference to article
articles: {
collection: 'Article',
via:'tags'
},
tag_name:{type:'string',
required:true,
unique:true
}
}
};
CategoryController.js
searchCategory: function(req, res, next) {
var category_name = req.param('category_name');
Category.find({category_name:{'like': '%'+category_name+'%'}}).populate('Articles').exec(function(err, category)
{
if(err)
{
return res.json({'status':486,'status_message':'Server Error'});
}
else
{
if(category.length > 0)
{
var c = parseInt(category[0].Articles.length,10);
console.log(c);
var i = parseInt('0',10);
for (i=0; i<c; i++)
{
console.log('i value in loop = ' + i);
Article.find({id:category[0].Article[i].id}).populateAll().exec(function(err,article_info) {
if(err)
{
return res.send(err);
}
else
{
console.log(article_info);
console.log('-------------------------------------------');
res.json(article_info);
console.log(' I value = ' + i);
}
});
}
//console.log(category);
//return res.json({'status':479,'status_message':'Success..!!','category_info':category});
}
else
{
return res.json({'status':489,'status_message':'failure..!! No categories found..!!'});
}
}
});
}
POSTMAN请求:
http://localhost:1337/category/searchCategory
{"category_name":"travel"}
这是我的json回复: 在这里,我只获得与类别映射的文章。但我想显示映射到文章的城市,区域和标签的值。
{
"status": 479,
"status_message": "Success..!!",
"category_info": [
{
"Articles": [
{
"ref_city_id": "55a766d0a29811e875cb96a1",
"ref_area_id": "55a78b69578393e0049dec43",
"title": "title",
"blurb": "blurb",
"description": "Description",
"createdAt": "2015-07-16T12:36:36.778Z",
"updatedAt": "2015-07-16T12:48:20.609Z",
"id": "55a7a55439ace79e0512269d"
},
],
"category_name": "Cooking ",
"id": "55a6b26aee9b41de747547bb",
"createdAt": "2015-07-15T19:20:10.670Z",
"updatedAt": "2015-07-15T19:20:10.670Z"
}
]
}
如何使用populate进行深层嵌套关联?或者有其他方法可以达到这个目的吗?
请有人帮助我实现这一目标。
提前致谢。
答案 0 :(得分:3)
目前不支持,但似乎是the waterline team is working on it。
如果您阅读有关此问题的评论,则会看到an opened pull request基于this gist。
您也可以手动执行此操作,但是您想要检索许多信息,而异步代码可能很快就会非常混乱。
有许多工具可以帮助您保持代码的可读性:
promises
(已available in Waterline)使用您感觉更舒适的。在this answer中有一个使用promises和lodash的例子。我不会假设您要使用哪些工具,但如果您以后仍然被阻止,则可以更新问题。
Category
只有一个关系,Article
有很多。我认为在您的情况下,您应该在不使用populate()
的情况下加载类别。然后,您可以循环抛出每个类别的文章ID,使用populate()
(或populateAll()
)加载每篇文章,并使用结果覆盖category.Articles
。
修改即可。为避免Can\'t set headers after they are sent.
错误,您可以使用计数器确保仅在执行了所有异步函数时发送响应。您目前正在发送两次回复。
var articles = [];
var cpt = 0;
for (i=0; i<c; i++) {
Article.find({id:category[0].Article[i].id}).populate('ref_category_id').populate('tags').populate('ref_city_id').populate('ref_area_id').exec(function(err,article_info) {
// Simplified callback function
cpt++; // Increment every time the callback function is executed
articles.push(article_info);
if (cpt === c) { // We should have "c" executions of the callback function before sending the result in the response
// This is working if `category` is an instance of `Category`,
// but in your controller, `category` contains an array of `Category`.
// Do you want to return a single Category or should you rename `category` into `categories` ?
category.Articles = articles;
res.json(category);
}
});
}
一旦了解了异步执行流程的工作原理,就应该使用我上面提到的工具改进代码。
答案 1 :(得分:2)
是的,还有另一种方法可以做到这一点。
确保您需要以下npm模块。
var nestedPop = require('nested-pop');
searchCategory: function(req, res, next) {
var category_name = req.param('category_name');
Category.find({category_name:{'like': '%'+category_name+'%'}})
.populate('Articles')
.exec(function(err, category) {
if(err) return res.json({'status':486,'status_message':'Server Error'});
if(category.length > 0) {
var c = parseInt(category[0].Articles.length,10);
console.log(c);
var i = parseInt('0',10);
for (var i = 0; i < c; i++) {
console.log('i value in loop = ' + i);
Article.find({id:category[0].Article[i].id})
.populateAll()
.exec(function(err, article_info) {
if(err) return res.send(err);
return nestedPop(article_info, {
ref_city_id: {
as: 'City',
populate: [
'things',
'you',
'want',
'to',
'populate',
'for',
'city'
],
},
ref_area_id: {
as: 'Area', // or Areas (whatever the model name is)
populate: [
'things',
'you',
'want',
'to',
'populate',
'for',
'area'
]
}
}).then(function(article_info) {
console.log(article_info);
console.log('------------------------------------------');
res.json(article_info);
console.log(' I value = ' + i);
});
});
}
} else {
return res.json({'status':489,'status_message':'failure..!! No categories found..!!'});
}
});
}