【问题标题】:Deep associations in sails mongo using populate method?使用填充方法在sails mongo中进行深度关联?
【发布时间】:2015-10-12 07:29:59
【问题描述】:

我是sails.js 的新手,我正在使用"sails.js with Mongodb"。 我在使用我的风帆应用程序中的填充时遇到了深度关联问题。

我有这样的关系:

 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"
    }
  ]
}

如何使用填充进行深层嵌套关联?或者还有其他方法可以实现吗?

请任何人都可以帮助我实现这一目标。

提前致谢。

【问题讨论】:

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


    【解决方案1】:

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

    如果您阅读有关此问题的 cmets,您会看到基于 this gistan opened pull request


    您也可以手动完成,但是您想要检索很多信息,并且异步代码很快就会变得非常混乱

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

    使用你觉得更舒服的那些。 this answer 中有一个使用 Promise 和 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);
            }
        });
    }
    

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

    【讨论】:

    • 谢谢亚历克西斯..手动我怎么能做到这一点..你能告诉我一段代码吗?它对我有很大帮助。
    • 有很多选择,我现在没有太多时间,但我试图为您提供有关如何存档的更多信息。
    • 我将文章属性从分类重命名为文章
    • 亚历克西斯。我在类别控制器操作中添加了一些额外的代码。控制台中的一切都很好。我的意思是我在控制台中得到了预期的结果,但是当我在邮递员中检查时,缺少一篇文章,即总共有 2 篇文章,在控制台中我得到 2 篇文章,但在邮递员中我只得到一篇文章。它没有显示第二篇文章。请检查类别控制器中的 searchCategory 操作。
    • 即使我在控制台中收到此错误http.js:690 throw new Error('Can\'t set headers after they are sent.'); ^ Error: Can't set headers after they are sent.
    【解决方案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 获取更多文档。

    【讨论】:

      猜你喜欢
      • 2016-08-09
      • 2014-04-11
      • 2017-04-08
      • 2018-12-12
      • 2017-09-15
      • 2022-01-21
      • 2015-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多