メインコンテンツにスキップ
バージョン: v6 - 安定版

モデルクエリ - 基本

Sequelizeは、データベースからデータをクエリするのに役立つ様々なメソッドを提供します。

重要な注意点: Sequelizeで本番環境に対応したクエリを実行するには、トランザクションガイドも必ずお読みください。トランザクションは、データの整合性を確保し、その他の利点を提供するために重要です。

このガイドでは、標準的なCRUDクエリの作成方法を示します。

単純なINSERTクエリ

まず、簡単な例を見てみましょう

// Create a new user
const jane = await User.create({ firstName: 'Jane', lastName: 'Doe' });
console.log("Jane's auto-generated ID:", jane.id);

Model.create()メソッドは、Model.build()で保存されていないインスタンスを構築し、instance.save()でインスタンスを保存するためのショートカットです。

また、createメソッドで設定できる属性を定義することも可能です。これは、ユーザーが入力できるフォームに基づいてデータベースエントリを作成する場合に特に役立ちます。たとえば、Userモデルでユーザー名のみを設定でき、管理者フラグ(つまり、isAdmin)は設定できないように制限できます。

const user = await User.create(
{
username: 'alice123',
isAdmin: true,
},
{ fields: ['username'] },
);
// let's assume the default of isAdmin is false
console.log(user.username); // 'alice123'
console.log(user.isAdmin); // false

単純なSELECTクエリ

findAllメソッドを使用すると、テーブル全体をデータベースから読み取ることができます。

// Find all users
const users = await User.findAll();
console.log(users.every(user => user instanceof User)); // true
console.log('All users:', JSON.stringify(users, null, 2));
SELECT * FROM ...

SELECTクエリの属性の指定

一部の属性のみを選択するには、attributesオプションを使用できます。

Model.findAll({
attributes: ['foo', 'bar'],
});
SELECT foo, bar FROM ...

属性はネストされた配列を使用して名前を変更できます。

Model.findAll({
attributes: ['foo', ['bar', 'baz'], 'qux'],
});
SELECT foo, bar AS baz, qux FROM ...

sequelize.fnを使用すると、集計を行うことができます。

Model.findAll({
attributes: ['foo', [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'], 'bar'],
});
SELECT foo, COUNT(hats) AS n_hats, bar FROM ...

集計関数を使用する場合は、モデルからアクセスできるようにエイリアスを付ける必要があります。上記の例では、instance.n_hatsで帽子の数を取得できます。

集計を追加するだけの場合は、モデルのすべての属性をリストするのが面倒な場合があります。

// This is a tiresome way of getting the number of hats (along with every column)
Model.findAll({
attributes: [
'id',
'foo',
'bar',
'baz',
'qux',
'hats', // We had to list all attributes...
[sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'], // To add the aggregation...
],
});

// This is shorter, and less error prone because it still works if you add / remove attributes from your model later
Model.findAll({
attributes: {
include: [[sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats']],
},
});
SELECT id, foo, bar, baz, qux, hats, COUNT(hats) AS n_hats FROM ...

同様に、選択した属性をいくつか削除することも可能です。

Model.findAll({
attributes: { exclude: ['baz'] },
});
-- Assuming all columns are 'id', 'foo', 'bar', 'baz' and 'qux'
SELECT id, foo, bar, qux FROM ...

WHERE句の適用

whereオプションは、クエリをフィルタリングするために使用されます。where句に使用できる多くの演算子があり、Opのシンボルとして利用できます。

基本

Post.findAll({
where: {
authorId: 2,
},
});
// SELECT * FROM post WHERE authorId = 2;

Opからの演算子が明示的に渡されていないため、Sequelizeはデフォルトで等価比較を想定していることに注意してください。上記のコードは以下と同等です。

const { Op } = require('sequelize');
Post.findAll({
where: {
authorId: {
[Op.eq]: 2,
},
},
});
// SELECT * FROM post WHERE authorId = 2;

複数のチェックを渡すことができます。

Post.findAll({
where: {
authorId: 12,
status: 'active',
},
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';

最初の例でSequelizeがOp.eq演算子を推論したように、ここではSequelizeは呼び出し元が2つのチェックに対してANDを求めていると推論しました。上記のコードは以下と同等です。

const { Op } = require('sequelize');
Post.findAll({
where: {
[Op.and]: [{ authorId: 12 }, { status: 'active' }],
},
});
// SELECT * FROM post WHERE authorId = 12 AND status = 'active';

ORは同様の方法で簡単に行うことができます。

const { Op } = require('sequelize');
Post.findAll({
where: {
[Op.or]: [{ authorId: 12 }, { authorId: 13 }],
},
});
// SELECT * FROM post WHERE authorId = 12 OR authorId = 13;

上記は同じフィールドを含むORだったため、Sequelizeでは、より読みやすく、同じ動作を生成するわずかに異なる構造を使用できます。

const { Op } = require('sequelize');
Post.destroy({
where: {
authorId: {
[Op.or]: [12, 13],
},
},
});
// DELETE FROM post WHERE authorId = 12 OR authorId = 13;

演算子

Sequelizeはいくつかの演算子を提供します。

const { Op } = require("sequelize");
Post.findAll({
where: {
[Op.and]: [{ a: 5 }, { b: 6 }], // (a = 5) AND (b = 6)
[Op.or]: [{ a: 5 }, { b: 6 }], // (a = 5) OR (b = 6)
someAttribute: {
// Basics
[Op.eq]: 3, // = 3
[Op.ne]: 20, // != 20
[Op.is]: null, // IS NULL
[Op.not]: true, // IS NOT TRUE
[Op.or]: [5, 6], // (someAttribute = 5) OR (someAttribute = 6)

// Using dialect specific column identifiers (PG in the following example):
[Op.col]: 'user.organization_id', // = "user"."organization_id"

// Number comparisons
[Op.gt]: 6, // > 6
[Op.gte]: 6, // >= 6
[Op.lt]: 10, // < 10
[Op.lte]: 10, // <= 10
[Op.between]: [6, 10], // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15

// Other operators

[Op.all]: sequelize.literal('SELECT 1'), // > ALL (SELECT 1)

[Op.in]: [1, 2], // IN [1, 2]
[Op.notIn]: [1, 2], // NOT IN [1, 2]

[Op.like]: '%hat', // LIKE '%hat'
[Op.notLike]: '%hat', // NOT LIKE '%hat'
[Op.startsWith]: 'hat', // LIKE 'hat%'
[Op.endsWith]: 'hat', // LIKE '%hat'
[Op.substring]: 'hat', // LIKE '%hat%'
[Op.iLike]: '%hat', // ILIKE '%hat' (case insensitive) (PG only)
[Op.notILike]: '%hat', // NOT ILIKE '%hat' (PG only)
[Op.regexp]: '^[h|a|t]', // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
[Op.notRegexp]: '^[h|a|t]', // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
[Op.iRegexp]: '^[h|a|t]', // ~* '^[h|a|t]' (PG only)
[Op.notIRegexp]: '^[h|a|t]', // !~* '^[h|a|t]' (PG only)

[Op.any]: [2, 3], // ANY (ARRAY[2, 3]::INTEGER[]) (PG only)
[Op.match]: Sequelize.fn('to_tsquery', 'fat & rat') // match text search for strings 'fat' and 'rat' (PG only)

// In Postgres, Op.like/Op.iLike/Op.notLike can be combined to Op.any:
[Op.like]: { [Op.any]: ['cat', 'hat'] } // LIKE ANY (ARRAY['cat', 'hat'])

// There are more postgres-only range operators, see below
}
}
});

Op.inのショートハンド構文

配列を直接whereオプションに渡すと、暗黙的にIN演算子が使用されます。

Post.findAll({
where: {
id: [1, 2, 3], // Same as using `id: { [Op.in]: [1,2,3] }`
},
});
// SELECT ... FROM "posts" AS "post" WHERE "post"."id" IN (1, 2, 3);

演算子による論理的な組み合わせ

演算子Op.andOp.orOp.notを使用すると、任意の複雑なネストされた論理比較を作成できます。

Op.andOp.orの例

const { Op } = require("sequelize");

Foo.findAll({
where: {
rank: {
[Op.or]: {
[Op.lt]: 1000,
[Op.eq]: null
}
},
// rank < 1000 OR rank IS NULL

{
createdAt: {
[Op.lt]: new Date(),
[Op.gt]: new Date(new Date() - 24 * 60 * 60 * 1000)
}
},
// createdAt < [timestamp] AND createdAt > [timestamp]

{
[Op.or]: [
{
title: {
[Op.like]: 'Boat%'
}
},
{
description: {
[Op.like]: '%boat%'
}
}
]
}
// title LIKE 'Boat%' OR description LIKE '%boat%'
}
});

Op.notの例

Project.findAll({
where: {
name: 'Some Project',
[Op.not]: [
{ id: [1, 2, 3] },
{
description: {
[Op.like]: 'Hello%',
},
},
],
},
});

上記は次を生成します。

SELECT *
FROM `Projects`
WHERE (
`Projects`.`name` = 'Some Project'
AND NOT (
`Projects`.`id` IN (1,2,3)
AND
`Projects`.`description` LIKE 'Hello%'
)
)

関数(列だけでなく)を使用した高度なクエリ

WHERE char_length("content") = 7のようなものを取得したい場合はどうすればよいでしょうか?

Post.findAll({
where: sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7),
});
// SELECT ... FROM "posts" AS "post" WHERE char_length("content") = 7

sequelize.fnsequelize.colメソッドの使用法に注意してください。これらは、SQL関数呼び出しとテーブル列をそれぞれ指定するために使用する必要があります。Sequelizeはこの状況を異なる方法で処理する必要があるため(たとえば、他のシンボルエスケープアプローチを使用するなど)、単純な文字列(char_length(content)など)を渡すのではなく、これらのメソッドを使用する必要があります。

さらに複雑なものが必要な場合はどうすればよいでしょうか?

Post.findAll({
where: {
[Op.or]: [
sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7),
{
content: {
[Op.like]: 'Hello%',
},
},
{
[Op.and]: [
{ status: 'draft' },
sequelize.where(sequelize.fn('char_length', sequelize.col('content')), {
[Op.gt]: 10,
}),
],
},
],
},
});

上記は次のSQLを生成します。

SELECT
...
FROM "posts" AS "post"
WHERE (
char_length("content") = 7
OR
"post"."content" LIKE 'Hello%'
OR (
"post"."status" = 'draft'
AND
char_length("content") > 10
)
)

Postgresのみの範囲演算子

範囲型は、サポートされているすべての演算子でクエリできます。

提供された範囲値は、境界の包含/除外を定義できることにも注意してください。

[Op.contains]: 2,            // @> '2'::integer  (PG range contains element operator)
[Op.contains]: [1, 2], // @> [1, 2) (PG range contains range operator)
[Op.contained]: [1, 2], // <@ [1, 2) (PG range is contained by operator)
[Op.overlap]: [1, 2], // && [1, 2) (PG range overlap (have points in common) operator)
[Op.adjacent]: [1, 2], // -|- [1, 2) (PG range is adjacent to operator)
[Op.strictLeft]: [1, 2], // << [1, 2) (PG range strictly left of operator)
[Op.strictRight]: [1, 2], // >> [1, 2) (PG range strictly right of operator)
[Op.noExtendRight]: [1, 2], // &< [1, 2) (PG range does not extend to the right of operator)
[Op.noExtendLeft]: [1, 2], // &> [1, 2) (PG range does not extend to the left of operator)

非推奨:演算子のエイリアス

Sequelize v4では、シンボルを使用する代わりに、文字列を指定して演算子を参照することができました。これは現在非推奨であり、強く推奨されず、次のメジャーバージョンで削除される可能性があります。本当に必要な場合は、SequelizeコンストラクターでoperatorAliasesオプションを渡すことができます。

例:

const { Sequelize, Op } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:', {
operatorsAliases: {
$gt: Op.gt,
},
});

// Now we can use `$gt` instead of `[Op.gt]` in where clauses:
Foo.findAll({
where: {
$gt: 6, // Works like using [Op.gt]
},
});

単純なUPDATEクエリ

UPDATEクエリも、上記に示した読み取りクエリと同様に、whereオプションを受け入れます。

// Change everyone without a last name to "Doe"
await User.update(
{ lastName: 'Doe' },
{
where: {
lastName: null,
},
},
);

単純なDELETEクエリ

DELETEクエリも、上記に示した読み取りクエリと同様に、whereオプションを受け入れます。

// Delete everyone named "Jane"
await User.destroy({
where: {
firstName: 'Jane',
},
});

すべてを破棄するには、TRUNCATE SQLを使用できます。

// Truncate the table
await User.destroy({
truncate: true,
});

一括作成

Sequelizeは、一度のクエリで複数のレコードを一度に作成できるように、Model.bulkCreateメソッドを提供します。

Model.bulkCreateの使用法は、単一のオブジェクトではなくオブジェクトの配列を受け取るという点で、Model.createと非常によく似ています。

const captains = await Captain.bulkCreate([{ name: 'Jack Sparrow' }, { name: 'Davy Jones' }]);
console.log(captains.length); // 2
console.log(captains[0] instanceof Captain); // true
console.log(captains[0].name); // 'Jack Sparrow'
console.log(captains[0].id); // 1 // (or another auto-generated value)

ただし、デフォルトでは、bulkCreateは作成される各オブジェクトに対してバリデーションを実行しません(createは実行します)。bulkCreateにもこれらのバリデーションを実行させるには、validate: trueオプションを渡す必要があります。これにより、パフォーマンスが低下します。使用例

const Foo = sequelize.define('foo', {
name: {
type: DataTypes.TEXT,
validate: {
len: [4, 6],
},
},
});

// This will not throw an error, both instances will be created
await Foo.bulkCreate([{ name: 'abc123' }, { name: 'name too long' }]);

// This will throw an error, nothing will be created
await Foo.bulkCreate([{ name: 'abc123' }, { name: 'name too long' }], {
validate: true,
});

ユーザーから直接値を受け入れている場合は、実際に挿入する列を制限すると便利です。これをサポートするために、bulkCreate()は、考慮する必要があるフィールドを定義する配列であるfieldsオプションを受け入れます(残りは無視されます)。

await User.bulkCreate([{ username: 'foo' }, { username: 'bar', admin: true }], {
fields: ['username'],
});
// Neither foo nor bar are admins.

順序付けとグループ化

Sequelizeは、ORDER BYおよびGROUP BYを操作するためのorderおよびgroupオプションを提供します。

順序付け

orderオプションは、クエリを順序付ける項目の配列またはsequelizeメソッドを受け取ります。これらの項目は、[column, direction]の形式の配列自体です。列は正しくエスケープされ、方向は有効な方向のホワイトリスト(ASCDESCNULLS FIRSTなど)でチェックされます。

Subtask.findAll({
order: [
// Will escape title and validate DESC against a list of valid direction parameters
['title', 'DESC'],

// Will order by max(age)
sequelize.fn('max', sequelize.col('age')),

// Will order by max(age) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],

// Will order by otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],

// Will order an associated model's createdAt using the model name as the association's name.
[Task, 'createdAt', 'DESC'],

// Will order through an associated model's createdAt using the model names as the associations' names.
[Task, Project, 'createdAt', 'DESC'],

// Will order by an associated model's createdAt using the name of the association.
['Task', 'createdAt', 'DESC'],

// Will order by a nested associated model's createdAt using the names of the associations.
['Task', 'Project', 'createdAt', 'DESC'],

// Will order by an associated model's createdAt using an association object. (preferred method)
[Subtask.associations.Task, 'createdAt', 'DESC'],

// Will order by a nested associated model's createdAt using association objects. (preferred method)
[Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],

// Will order by an associated model's createdAt using a simple association object.
[{ model: Task, as: 'Task' }, 'createdAt', 'DESC'],

// Will order by a nested associated model's createdAt simple association objects.
[{ model: Task, as: 'Task' }, { model: Project, as: 'Project' }, 'createdAt', 'DESC'],
],

// Will order by max age descending
order: sequelize.literal('max(age) DESC'),

// Will order by max age ascending assuming ascending is the default order when direction is omitted
order: sequelize.fn('max', sequelize.col('age')),

// Will order by age ascending assuming ascending is the default order when direction is omitted
order: sequelize.col('age'),

// Will order randomly based on the dialect (instead of fn('RAND') or fn('RANDOM'))
order: sequelize.random(),
});

Foo.findOne({
order: [
// will return `name`
['name'],
// will return `username` DESC
['username', 'DESC'],
// will return max(`age`)
sequelize.fn('max', sequelize.col('age')),
// will return max(`age`) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// will return otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// will return otherfunction(awesomefunction(`col`)) DESC, This nesting is potentially infinite!
[sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC'],
],
});

要約すると、order配列の要素は次のようになります。

  • 文字列(自動的に引用符で囲まれます)
  • 配列。最初の要素は引用符で囲まれ、2番目の要素は逐語的に付加されます。
  • rawフィールドを持つオブジェクト
    • rawの内容は、引用符なしで逐語的に追加されます。
    • その他はすべて無視され、rawが設定されていない場合、クエリは失敗します。
  • Sequelize.fnの呼び出し(SQLで関数呼び出しが生成されます)
  • Sequelize.colの呼び出し(列名が引用符で囲まれます)

グループ化

グループ化と順序付けの構文は同じですが、グループ化は配列の最後引数として方向を受け入れない点が異なります(ASCDESCNULLS FIRSTなどはありません)。

文字列を直接groupに渡すこともできます。これは生成されたSQLに直接(逐語的に)含まれます。注意して使用し、ユーザーが生成したコンテンツでは使用しないでください。

Project.findAll({ group: 'name' });
// yields 'GROUP BY name'

制限とページネーション

limit および offset オプションを使用すると、制限やページネーションを扱うことができます。

// Fetch 10 instances/rows
Project.findAll({ limit: 10 });

// Skip 8 instances/rows
Project.findAll({ offset: 8 });

// Skip 5 instances and fetch the 5 after that
Project.findAll({ offset: 5, limit: 5 });

通常、これらは order オプションと組み合わせて使用されます。

ユーティリティメソッド

Sequelize はいくつかのユーティリティメソッドも提供しています。

count

count メソッドは、データベース内の要素の出現回数を単純にカウントします。

console.log(`There are ${await Project.count()} projects`);

const amount = await Project.count({
where: {
id: {
[Op.gt]: 25,
},
},
});
console.log(`There are ${amount} projects with an id greater than 25`);

max, min および sum

Sequelize は、便利な maxmin、および sum メソッドも提供しています。

3人のユーザーがいて、その年齢が10歳、5歳、40歳であると仮定しましょう。

await User.max('age'); // 40
await User.max('age', { where: { age: { [Op.lt]: 20 } } }); // 10
await User.min('age'); // 5
await User.min('age', { where: { age: { [Op.gt]: 5 } } }); // 10
await User.sum('age'); // 55
await User.sum('age', { where: { age: { [Op.gt]: 5 } } }); // 50

increment, decrement

Sequelize は便利な increment メソッドも提供しています。

年齢が10歳のユーザーがいると仮定しましょう。

await User.increment({ age: 5 }, { where: { id: 1 } }); // Will increase age to 15
await User.increment({ age: -5 }, { where: { id: 1 } }); // Will decrease age to 5