フィールド投影
クエリで必要なフィールドだけを返すことで、帯域幅を削減し、パフォーマンスを向上させます。
多数のフィールドを持つドキュメントをクエリする場合、必要なのはそのデータの一部だけであることがよくあります。フィールド投影がないと、リスト表示で名前とメールアドレスだけを表示したい場合でも、アプリケーションはドキュメント全体を取得してしまいます。フィールド投影は、返すフィールドを正確に指定できるようにすることで、帯域幅を削減し、パフォーマンスを向上させます。投影はサーバー側で行われるため、クライアント側でデータをフィルタリングするのではなく、ネットワーク転送量自体を減らせます。
// 投影なし: 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: ドキュメントの主キー値。built-in database を使用している場合にのみ存在します。
// __docId__ を投影せずに __docId__ でフィルタ
const results = await squid
.collection<User>('users')
.query()
.where('__docId__', '>=', 'user_100')
.projectFields(['name', 'email'])
.dereference()
.snapshot();
複合主キーを持つコレクションでは、個々のキーのフィールドも結果のトップレベルに含まれます。
ネストされたフィールド
ドット記法を使って、ネストされたフィールドパスを投影できます。
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');
// エラー: 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');
// エラー: 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