使用populate方法在Sails中如何深度关联MongoDB?

4
我是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..!!'});
            }
        }
    });
}

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个回答

3

目前尚不支持,但似乎Waterline团队正在研究

如果您阅读此问题的评论,您将看到基于this gist一个开放的拉取请求


您也可以手动完成,但是如果您想检索大量信息,则异步代码可能会变得非常混乱。有许多工具可帮助您保持代码的可读性: 使用您感觉更舒适的工具。在此答案中使用了promises和lodash的示例。我不会假设您要使用哪些工具,但如果您后来仍然被阻止,可以更新问题

Category 只有一个关系,而 Article 有很多个。在您的情况下,我认为您应该不使用 populate() 加载分类。然后您可以循环遍历每个分类的文章 ids,使用 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);
        }
    });
}

一旦您理解了异步执行流程的工作原理,您应该使用我上面提到的工具来改进代码。


谢谢Alexis。手动怎么实现这个功能?你能给我展示一下代码吗?这会对我很有帮助。 - Anil Kumar
有很多选项,而且我现在没有太多时间,但我尽力为您提供更多关于如何实现此目标的信息。 - Alexis N-o
我将“类别”属性从“文章”改名为“文章”。 - Anil Kumar
Alexis,我在类别控制器操作中添加了一些额外的代码。在控制台中一切都正常。我的意思是,我在控制台中得到了预期的结果,但是当我在Postman中检查时,缺少一篇文章,即总共有2篇文章,在控制台中我得到了2篇文章,但是在Postman中我只得到了一篇文章。它没有显示第二篇文章。你能否请检查类别控制器中的searchCategory操作。 - Anil Kumar
即使我在控制台中也遇到了这个错误 http.js:690 throw new Error('Can\'t set headers after they are sent.'); ^ Error: Can't set headers after they are sent. - Anil Kumar
我更新了响应并删除了无用的评论。 - Alexis N-o

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 获取更多关于该文档的信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接