Sequelize: Поиск по ассоциации
Как использовать Sequelize для поиска всех людей, у которых столбец в связи соответствует определенному условию?
Пример: нужно найти все Книги, авторы которых имеют фамилию 'Hitchcock'. Схема книги содержит отношение hasOne с таблицей Авторов.
Редактирование: Я понимаю, как это можно сделать с помощью сырого SQL-запроса, но ищу другой подход.
3 ответ(ов)
Вот рабочий пример того, как использовать Sequelize для получения всех Books
у Author
с определенной фамилией. Код выглядит более сложным, чем на самом деле, поскольку я определяю модели, создаю связи между ними, синхронизирую с базой данных (чтобы создать их таблицы), а затем создаю тестовые данные в этих новых таблицах. Ищите findAll
в середине кода, чтобы увидеть конкретно то, что вам нужно.
module.exports = function(sequelize, DataTypes) {
var Author = sequelize.define('Author', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING
},
lastName: {
type: DataTypes.STRING
}
});
var Book = sequelize.define('Book', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
title: {
type: DataTypes.STRING
}
});
var firstAuthor;
var secondAuthor;
Author.hasMany(Book);
Book.belongsTo(Author);
Author.sync({ force: true })
.then(function() {
return Book.sync({ force: true });
})
.then(function() {
return Author.create({firstName: 'Test', lastName: 'Testerson'});
})
.then(function(author1) {
firstAuthor = author1;
return Author.create({firstName: 'The Invisible', lastName: 'Hand'});
})
.then(function(author2) {
secondAuthor = author2;
return Book.create({AuthorId: firstAuthor.id, title: 'A simple book'});
})
.then(function() {
return Book.create({AuthorId: firstAuthor.id, title: 'Another book'});
})
.then(function() {
return Book.create({AuthorId: secondAuthor.id, title: 'Some other book'});
})
.then(function() {
// Это именно то, что вам нужно.
return Book.findAll({
where: {
'Authors.lastName': 'Testerson'
},
include: [
{ model: Author, as: Author.tableName }
]
});
})
.then(function(books) {
console.log('Найдено ' + books.length + ' книг у Test Testerson');
});
}
Обратите внимание на то, что в запросе findAll
используется where
для фильтрации книг по фамилии автора. Также вспомогательная модель Author
включается в выборку с помощью include
, чтобы получить всю необходимую информацию о авторах.
В последней версии Sequelize (5.9.0) метод, предложенный @c.hill, больше не работает. Теперь вам нужно использовать следующий код:
return Book.findAll({
where: {
'$Authors.lastName$': 'Testerson'
},
include: [
{model: Author, as: Author.tableName}
]
});
Обратите внимание, что в данном случае важно использовать синтаксис с $
для доступа к полям связанных моделей в условии where
, а также убедитесь, что название ассоциации указано правильно. Если у вас возникнут дополнительные вопросы по реализации, не стесняйтесь спрашивать!
Для документации!
Проверьте раздел об жадной выборке (eager loading) по следующей ссылке:
https://sequelize.org/master/manual/eager-loading.html
Что касается вышеуказанных ответов, нужную информацию можно найти в документации под заголовком
Сложные условия WHERE на верхнем уровне
Из документации:
Чтобы получить верхнеуровневые условия WHERE, которые касаются вложенных колонок, Sequelize предоставляет способ ссылаться на вложенные колонки с помощью синтаксиса: '\(nested.column\)'.
Это может быть, например, использовано для того, чтобы переместить условия WHERE из включенной модели из условия ON в верхнеуровневое условие WHERE.
User.findAll({
where: {
'$Instruments.size$': { [Op.ne]: 'small' }
},
include: [{
model: Tool,
as: 'Instruments'
}]
});
Сгенерированный SQL:
SELECT
`user`.`id`,
`user`.`name`,
`Instruments`.`id` AS `Instruments.id`,
`Instruments`.`name` AS `Instruments.name`,
`Instruments`.`size` AS `Instruments.size`,
`Instruments`.`userId` AS `Instruments.userId`
FROM `users` AS `user`
LEFT OUTER JOIN `tools` AS `Instruments` ON
`user`.`id` = `Instruments`.`userId`
WHERE `Instruments`.`size` != 'small';
Для лучшего понимания всех различий между внутренними условиями where (используемыми внутри include) с опцией required и без нее, а также верхнеуровневым условием where с использованием синтаксиса \(nested.column\), ниже приведены четыре примера:
// Внутреннее условие where, с `required: true` по умолчанию
await User.findAll({
include: {
model: Tool,
as: 'Instruments',
where: {
size: { [Op.ne]: 'small' }
}
}
});
// Внутреннее условие where, `required: false`
await User.findAll({
include: {
model: Tool,
as: 'Instruments',
where: {
size: { [Op.ne]: 'small' }
},
required: false
}
});
// Верхнеуровневое условие where, с `required: false` по умолчанию
await User.findAll({
where: {
'$Instruments.size$': { [Op.ne]: 'small' }
},
include: {
model: Tool,
as: 'Instruments'
}
});
// Верхнеуровневое условие where, `required: true`
await User.findAll({
where: {
'$Instruments.size$': { [Op.ne]: 'small' }
},
include: {
model: Tool,
as: 'Instruments',
required: true
}
});
Сгенерированные SQL-запросы в порядке:
-- Внутреннее условие where, с `required: true`
SELECT [...] FROM `users` AS `user`
INNER JOIN `tools` AS `Instruments` ON
`user`.`id` = `Instruments`.`userId`
AND `Instruments`.`size` != 'small';
-- Внутреннее условие where, `required: false`
SELECT [...] FROM `users` AS `user`
LEFT OUTER JOIN `tools` AS `Instruments` ON
`user`.`id` = `Instruments`.`userId`
AND `Instruments`.`size` != 'small';
-- Верхнеуровневое условие where, с `required: false`
SELECT [...] FROM `users` AS `user`
LEFT OUTER JOIN `tools` AS `Instruments` ON
`user`.`id` = `Instruments`.`userId`
WHERE `Instruments`.`size` != 'small';
-- Верхнеуровневое условие where, `required: true`
SELECT [...] FROM `users` AS `user`
INNER JOIN `tools` AS `Instruments` ON
`user`.`id` = `Instruments`.`userId`
WHERE `Instruments`.`size` != 'small';
Это дает нам хорошее представление о том, как выполняются соединения (join)!
Как задать длину Sequelize.STRING?
Автоинкрементный ID с Sequelize в MySQL
Как узнать версию установленного npm пакета?
Как задать переменные окружения из файла package.json?
Самый быстрый способ скопировать файл в Node.js