使用填充方法在sails mongo中进行深层关联?

时间:2015-07-22 12:15:24

标签: node.js mongodb sails.js populate sails-mongo

我是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进行深层嵌套关联?或者有其他方法可以达到这个目的吗?

请有人帮助我实现这一目标。

提前致谢。

2 个答案:

答案 0 :(得分:3)

目前不支持,但似乎是the waterline team is working on it

如果您阅读有关此问题的评论,则会看到an opened pull request基于this gist

您也可以手动执行此操作,但是您想要检索许多信息,而异步代码可能很快就会非常混乱

有许多工具可以帮助您保持代码的可读性:

使用您感觉更舒适的。在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..!!'});
    }
  });
}

转到https://www.npmjs.com/package/nested-pop了解更多文档。