クエリ(別名クエリインスタンス)は、.find()
や.create()
のようなモデルメソッドから返される、チェーン可能な遅延オブジェクトです。これらは、データベースからレコードを取得または変更するという、まだ完全に実行されていない意図を表します。
var query = Zookeeper.find();
クエリインスタンスの目的は、モデルを操作するための便利でチェーン可能な構文を提供することです。.populate()
、.where()
、.sort()
などのメソッドを使用すると、データベース呼び出しがネットワークに送信される前に、それを絞り込むことができます。そして、データベースにクエリを送信する準備ができたら、await
することができます。
JavaScriptの
await
キーワードをサポートしていない古いバージョンのNode.jsを使用している場合は、.exec()
または.then()
+.catch()
を使用できます。詳細については、以下の「Promiseとコールバック」のセクションを参照してください。
ほとんどの場合、クエリインスタンスをオブジェクト自体としてではなく、データベースと通信するための構文の一部として考えるでしょう。実際、Sailsアプリでこれらのオブジェクトを既に使用しているかもしれません!もしそうなら、次の構文は見覚えがあるはずです。
var zookeepers = await Zookeeper.find();
この例では、Zookeeper.find()
の呼び出しはクエリインスタンスを返しますが、await
キーワードを使用して実行されるまで実際には何も行いません。そして、結果はzookeepers
変数に割り当てられます。
await
を使用してクエリを実行すると、多くのことが起こります。
await query;
まず、クエリはWaterlineコアによって正規化されたクエリに「整理」されます。次に、関連するWaterlineアダプターを通過し、データベースの生のクエリ構文(例:RedisまたはMongoコマンド、さまざまなSQL方言など)に変換されます。次に、関与する各アダプターは、ネイティブのNode.jsデータベースドライバーを使用して、クエリをネットワーク経由で対応する物理データベースに送信します。
アダプターが応答を受信すると、Waterlineインターフェイス仕様にマーシャリングされ、Waterlineコアに返され、他の生のアダプター応答と統合されて、一貫した結果セットになります。その時点で、アプリで消費するために「ユーザーランド」(つまり、コード)に返される前に、最後にもう一度正規化されます。
必要に応じて、try/catchを使用して特定のエラーを処理できます。
var zookeepersAtThisZoo;
try {
zookeepersAtThisZoo = await Zookeeper.find({
zoo: req.param('zoo')
}).limit(30);
} catch (err) {
switch (err.name) {
case 'UsageError': return res.badRequest(err);
default: throw err;
}
}
return res.json(zookeepersAtThisZoo);
受け取る可能性のあるエラーの種類は、実行しているクエリの種類によって異なります。詳細については、さまざまなクエリメソッドの参照ドキュメントを参照してください。
await
の代替として、SailsとWaterlineはコールバックとPromiseチェーンのサポートを提供します。一般的に、可能な限りawait
を使用すべきです。これにより、コードがよりシンプルで理解しやすくなり、非同期コールバックでキャッチされない例外をスローすることによって発生する可能性のあるDDoS脆弱性や安定性の問題を回避するのに役立ちます。とはいえ、古いバージョンのNode.jsとの下位互換性を維持する必要がある場合もあります。このため、SailsとWaterlineのすべてのクエリは、.exec()
メソッドを公開しています。
Zookeeper.find().exec(function afterFind(err, zookeepers) {
// Careful! Do not throw an error in here without a `try` block!
// (Even a simple typo or null pointer exception could crash the process!)
if (err) {
// uh oh
// (handle error; e.g. `return res.serverError(err)`)
return;
}
// would you look at all those zookeepers?
// (now let's do the next thing;
// e.g. `_.reduce(zookeepers, ...)` and/or `return res.json(zookeepers)`)
// …
});
//
// (don't put code out here)
上記の例に示すように、クエリはすぐに実行されるわけではありませんが、await
を使用してクエリを実行して結果を待つ代わりに、コールバック関数を持つ従来の.exec()
メソッドを使用していることに注意してください。この使用法では、JavaScriptのtry/catchと通常のエラー処理に頼ってエラーを処理することはできません!代わりに、.exec()
へのコールバックで手動で処理する必要があります。このスタイルのエラー処理は、2017年夏頃までのNode.jsアプリで従来使用されていたアプローチです。
内部的には、SailsとWaterlineは、Bluebird Promiseライブラリとの最小限の統合も提供しており、.then()
および.catch()
メソッドを公開しています。
Zookeeper.find()
.then(function (zookeepers) {...})
.catch(function (err) {...});
//
// (don't put code out here)
この例では、.catch()
に渡されるコールバックは、上記の.exec()
の例のif(err) {}
ブロックの内容(例:res.serverError()
)と同じです。同様に、.then()
コールバックは、if(err) {}
と最初のreturn
の下のコードと同じです。
Promiseが好きで、それらに関する相応の経験をお持ちの場合は、このインターフェイスを使用しても問題ないはずです。ただし、Promiseにあまり慣れていない場合、またはどちらでも構わない場合は、標準のNode.jsコールバック規則を使用しているため、.exec()
を使用する方が簡単かもしれません。
アプリの特定のクエリで従来のPromiseチェーンを使用することにした場合は、
.then()
と.catch()
の両方のコールバックを提供してください。そうしないと、エラーが処理されずに、不快な競合状態やメモリリークが発生する可能性があります。これはSailsまたはWaterlineの概念だけではありません。むしろ、JavaScript、特にNode.jsでこのタイプの使用法を実装する場合は常に注意すべきことです。サーバー側のコードで処理されないエラーは、クライアント側のエラーよりも問題が発生する傾向があるためです。.catch()
を省略することは、従来のNodeコールバックでerr
引数を無視することと同じであり、同様に危険です。実際、これは、あらゆるスキルレベルのNode.js開発者にとって、バグの最も一般的な原因の1つです。非同期コードに慣れていない場合は、適切なエラー処理を特に無視しやすいです。しばらくすると、成功した場合を処理するコードを記述した後(または、より良いのは、直前)に、非同期エラーを処理する習慣が身に付くでしょう。このような習慣は、上記の一般的なバグからアプリを免疫します。
(さらに良いのは、
await
を使用することです!)
- クエリインスタンスは、Promiseとまったく同じものではありませんが、私たちの目的には十分です。違いは、SailsとWaterlineのクエリインスタンスは、実際にはparleyライブラリによって実装されたDeferredであるということです。つまり、すぐに実行を開始するわけではありません。代わりに、
await
、.exec()
、.then()
、または.toPromise()
で開始した場合にのみ実行を開始します。- Nodeスタイルのコールバックは、モデルメソッド(例:
.find()
)の最後の引数として直接渡すことができます。この場合、クエリはすぐに実行され、モデルメソッドはクエリインスタンスを返しません(代わりに、クエリが完了すると、提供したNodeスタイルのコールバックがトリガーされます)。非常に高度なことをしているのでない限り、通常は標準的な使用法に従う方が良いでしょう。つまり、.exec()
を呼び出すか、.then()
と.catch()
を呼び出すことです。