2016-08-25 2 views
2

Мне нужно получить объект, а также получить отношения и вложенные отношения.Sails/Waterline: Как получить отношения внутри отношения?

Итак, у меня есть три модели ниже:

модели Пользователь:

module.exports = { 

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

Pet модели:

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(....) I получить u ser и связанных с ними домашних животных. Как я могу также получить связанные вакцины с каждый домашним животным? Я не нашел ссылок об этом в официальной документации.

ответ

1

Я также столкнулся с этой проблемой, и, насколько я знаю, вложенные запросы ассоциации еще не встроены в паруса (начиная с этого поста).

Вы можете использовать обещания для обработки вложенной вами совокупности, но это может стать довольно волосатым, если вы заполняете множество уровней.

Что-то вроде:

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

Thanks @Kevin! Хорошо, я буду реализовывать это, как вы указали. Когда они, наконец, добавят эту функциональность в фреймворк, я обновлю. –

1

Хотя ответ Кевина Ле верен, он может стать немного грязным, потому что вы выполняете асинхронные функции внутри цикла. Конечно, это работает, но предположим, что вы хотите вернуть пользователя со всеми домашними животными и вакцинами, как только закончите - как вы это делаете?

Существует несколько способов решения этой проблемы. Один из них - использовать async library, который предлагает кучу функций утилиты для работы с асинхронным кодом. Библиотека уже включена в паруса, и вы можете использовать ее по всему миру по умолчанию.

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); 

    }); 
+0

Спасибо @Simon, я тоже попробую этот подход –