フィールド投影
クエリから必要なフィールドだけを返すことで、帯域幅を削減しパフォーマンスを向上させます。
多くのフィールドを持つドキュメントをクエリする場合、必要なのはそのデータの一部だけであることがよくあります。フィールド投影がないと、リストビューで名前とメールアドレスだけを表示したい場合でも、アプリケーションはドキュメント全体を取得してしまいます。フィールド投影を使うと、返すフィールドを正確に指定できるため、帯域幅を削減しパフォーマンスを向上させられます。投影はサーバー側で行われるので、クライアント側でデータをフィルタリングするのではなく、ネットワーク転送量そのものを削減できます。
// 投影なし: 20+ 個のフィールドをすべて取得
const users = await squid
.collection<User>('users')
.query()
.snapshot();
// 投影あり: 必要なものだけを取得
const users = await squid
.collection<User>('users')
.query()
.projectFields(['name', 'email'])
.snapshot();
クイックスタート
任意のクエリで projectFields を呼び出し、フィールド名の配列を渡します。
const results = await squid
.collection<User>('users')
.query()
.projectFields(['name', 'age'])
.dereference()
.snapshot();
// 結果には投影したフィールドのみが含まれます
フィールド投影はすべてのクエリメソッドで利用できます。
const results = await squid
.collection<User>('users')
.query()
.where('status', '==', 'active')
.sortBy('name')
.limit(50)
.projectFields(['name', 'email', 'status'])
.dereference()
.snapshot();
コアコンセプト
ドキュメント識別子は常に含まれます
どのフィールドを投影しても、次の識別子は結果に常に含まれます。
__docId__: ドキュメントの一意識別子。すべての database connector で利用できます。投影に含めなくても、フィルタリングやソートに使用できます。__id: ドキュメントの primary key の値。built-in database を使用している場合にのみ存在します。
// __docId__ を投影せずに __docId__ でフィルタリング
const results = await squid
.collection<User>('users')
.query()
.where('__docId__', '>=', 'user_100')
.projectFields(['name', 'email'])
.dereference()
.snapshot();
複合 primary key を持つコレクションでは、個々のキーのフィールドも結果のトップレベルに含まれます。
ネストされたフィールド
ドット記法を使って、ネストされたフィールドパスを投影できます。
interface User {
name: string;
address: {
city: string;
zip: string;
country: string;
};
}
const results = await squid
.collection<User>('users')
.query()
.projectFields(['name', 'address.city'])
.dereference()
.snapshot();
// 結果: { name: 'Alice', address: { city: 'NYC' } }
// 注: address.zip と address.country は含まれません
エラーハンドリング
ドキュメント識別子フィールド(__docId__ と __id)はこれらのルールの例外であり、投影に含めなくてもフィルタリングとソートに常に使用できます。
フィルタに使うフィールドは投影に含める必要があります
projectFields を使う場合、where 句で使用するフィールドは投影に含める必要があります。
const usersCollection = squid.collection<User>('users');
// Error: projectFields に含まれていない 'email' ではフィルタリングできません
usersCollection
.query()
.where('email', '==', 'test@example.com')
.projectFields(['name', 'age']);
// 正しい例: 投影に email を含める
usersCollection
.query()
.where('email', '==', 'test@example.com')
.projectFields(['name', 'age', 'email']);
ソートに使うフィールドは投影に含める必要があります
sortBy で使用するフィールドは投影に含める必要があります。
const usersCollection = squid.collection<User>('users');
// Error: projectFields に含まれていない 'email' ではソートできません
usersCollection.query().sortBy('email').projectFields(['name', 'age']);
// 正しい例: 投影に email を含める
usersCollection.query().sortBy('email').projectFields(['name', 'age', 'email']);
空配列の挙動
投影配列が空の場合の挙動は、データベースによって異なります。
- Built-in database: ドキュメント識別子のみを持つレコードを返し、ユーザーデータのフィールドは返しません。
- External databases(MongoDB、PostgreSQL、MySQL など): エラーになります。少なくとも 1 つのフィールドを指定する必要があります。
// built-in database の場合: 識別子のみを持つレコードを返す
const results = await squid
.collection<User>('users')
.query()
.projectFields([])
.snapshot();
// external databases の場合: エラー
const mongoCollection = squid.collection<User>('users', 'mongoConnectorId');
mongoCollection.query().projectFields([]); // Error!
リアルタイムサブスクリプション
フィールド投影はリアルタイムサブスクリプションでも動作します。各更新には投影したフィールドのみが含まれます。
squid
.collection<User>('users')
.query()
.projectFields(['name', 'age'])
.snapshots()
.subscribe((refs) => {
// 各更新には name と age のみが含まれます
console.log(refs.map((ref) => ref.data));
});
ベストプラクティス
-
必要なフィールドだけを投影することで、ネットワーク転送を最小化し、特にフィールド数が多い/大きいコレクションにおいてクエリ性能を向上させます。
-
フィルタとソートで使用するフィールドは常に投影に含めることで、バリデーションエラーを回避します。
Client code// 正しい例: フィルタで使うため 'status' を含める
const users = await squid
.collection<User>('users')
.query()
.eq('status', 'active')
.projectFields(['name', 'email', 'status'])
.snapshot(); -
ネストされたフィールドにはドット記法を使うことで、特定のプロパティだけが必要なときにネストオブジェクト全体を取得せずに済みます。
-
クライアント側でのフィールドのフィルタリングより、フィールド投影を優先することで、ソースで帯域幅を削減できます。
サポートされているデータベース
フィールド投影は、すべての Squid database connector でサポートされています。
- Built-in database
- MongoDB
- PostgreSQL
- MySQL
- ClickHouse
- MS SQL Server