使用填充方法在 sails mongo 中建立深度关联?
Deep associations in sails mongo using populate method?
我是 sails.js 的新手,我正在使用 "sails.js with Mongodb"。
我在我的 sails 应用程序中使用 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..!!'});
}
}
});
}
邮递员请求:
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"
}
]
}
如何使用填充进行深层嵌套关联?或者还有其他方法可以实现吗?
请谁能帮我实现这个目标。
提前致谢。
目前不支持,但似乎 the waterline team is working on it。
如果你看了关于这个问题的评论,你会看到有an opened pull request based on this gist。
您也可以手动执行此操作,但是您想要检索很多信息并且 异步代码很快就会变得非常混乱。
有很多工具可以帮助您保持代码的可读性:
- 命名回调函数
promises
(已经 available in Waterline)
- async
- lodash
使用你觉得更舒服的那些。 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);
}
});
}
了解异步执行流程的工作原理后,您应该使用我上面提到的工具改进代码。
是的,还有另一种方法。
确保您需要以下 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..!!'});
}
});
}
前往 https://www.npmjs.com/package/nested-pop 获取更多文档。
我是 sails.js 的新手,我正在使用 "sails.js with Mongodb"。 我在我的 sails 应用程序中使用 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..!!'});
}
}
});
}
邮递员请求:
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"
}
]
}
如何使用填充进行深层嵌套关联?或者还有其他方法可以实现吗?
请谁能帮我实现这个目标。
提前致谢。
目前不支持,但似乎 the waterline team is working on it。
如果你看了关于这个问题的评论,你会看到有an opened pull request based on this gist。
您也可以手动执行此操作,但是您想要检索很多信息并且 异步代码很快就会变得非常混乱。
有很多工具可以帮助您保持代码的可读性:
- 命名回调函数
promises
(已经 available in Waterline)- async
- lodash
使用你觉得更舒服的那些。 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);
}
});
}
了解异步执行流程的工作原理后,您应该使用我上面提到的工具改进代码。
是的,还有另一种方法。
确保您需要以下 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..!!'});
}
});
}
前往 https://www.npmjs.com/package/nested-pop 获取更多文档。