帆/水线:如何检索关系内的关系?

时间:2016-08-25 17:12:37

标签: node.js sails.js waterline

我需要检索一个对象,并获得关系和嵌套关系。

所以,我有以下三个模型:

用户模型:

module.exports = {

  attributes: {
    name: {
      type: 'string'
    },
    pets: {
      collection: 'pet',
      via: 'owner',
    }
}

宠物模型:

module.exports = {
  attributes: {
    name: {
      type: 'string'
    },
    owner: {
      model: 'user'
    },
    vaccines: {
      collection: 'vaccine',
      via: 'pet',
    }
}

疫苗模型:

module.exports = {
  attributes: {
    name: {
      type: 'string'
    },
    pet: {
      model: 'pet'
    }
}

致电User.findOne(name: 'everton').populate('pets').exec(....)我会收到用户及相关的宠物。我怎样才能获得每只宠物的相关疫苗?我没有在官方文档中找到关于此的参考文献。

2 个答案:

答案 0 :(得分:1)

我也遇到过这个问题,据我所知,嵌套关联查询还没有构建到sails中(截至本文)。

您可以使用promises为您处理嵌套群体,但如果填充多个级别,这可能会变得非常繁琐。

类似的东西:

User.findOne(name: 'everton')
  .populate('pets')
  .then(function(user) {
    user.pets.forEach(function (pet) {
      //load pet's vaccines
    });
  });

这是一个关于sails.js的广泛讨论的话题,实际上是一个开放的拉取请求,它增加了这个功能的大部分内容。查看https://github.com/balderdashy/waterline/pull/1052

答案 1 :(得分:1)

虽然Kevin Le的答案是正确的,但它可能会有点混乱,因为你在循环中执行异步函数。当然它可以工作,但是假设你想要在完成后将所有宠物和疫苗都归还用户 - 你是如何做到的?

有几种方法可以解决这个问题。一种是使用提供一堆util函数的async library来处理异步代码。该库已包含在sails中,您可以默认全局使用它。

 User.findOneByName('TestUser')
   .populate('pets')
   .then(function (user) {

     var pets = user.pets;

     // async.each() will perform a for each loop and execute
     // a fallback after the last iteration is finished
     async.each(pets, function (pet, cb) {

       Vaccine.find({pet: pet.id})
         .then(function(vaccines){

           // I didn't find a way to reuse the attribute name
           pet.connectedVaccines = vaccines;

           cb();
         })

     }, function(){
       // this callback will be executed once all vaccines are received 
       return res.json(user);
     });
   });

还有另一种解决蓝鸟承诺问题的方法,它也是帆的一部分。它可能比前一个更有效,因为它只用一个数据库请求就可以获取所有疫苗。另一方面,它更难阅读...

User.findOneByName('TestUser')
  .populate('pets')
  .then(function (user) {

    var pets = user.pets,
        petsIds = [];

    // to avoid looping over the async function 
    // all pet ids get collected...
    pets.forEach(function(pet){
      petsIds.push(pet.id);
    });

    // ... to get all vaccines with one db call 
    var vaccines = Vaccine.find({pet: petsIds})
      .then(function(vaccines){
        return vaccines;
      });

    // with bluebird this array...
    return [user, vaccines];
  })

  //... will be passed here as soon as the vaccines are finished loading
  .spread(function(user, vaccines){

    // for the same output as before the vaccines get attached to 
    // the according pet object
    user.pets.forEach(function(pet){

      // as seen above the attribute name can't get used 
      // to store the data
      pet.connectedVaccines = vaccines.filter(function(vaccine){
        return vaccine.pet == pet.id;
      });
    });

    // then the user with all nested data can get returned
    return res.json(user);

  });