説明
配列内の要素ごとに新しいドキュメントを生成します。
新しいドキュメントには、入力からのすべてのフィールド、および配列からの異なる要素が含まれます。配列要素は指定された alias に保存されるため、同じフィールド名を持つ既存の値が上書きされる可能性があります。
必要に応じて、index_field 引数を指定できます。存在する場合、出力ドキュメントにソース配列の要素の 0 ベース インデックスが含まれます。
このステージは、多くの SQL システムで CROSS JOIN UNNEST(...) と同様に動作します。
構文
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
動作
エイリアスとインデックス フィールド
alias とオプションの index_field は、フィールドが入力ドキュメントにすでに存在する場合、元のフィールドを上書きします。index_field が指定されていない場合、出力ドキュメントにこのフィールドは含まれません。
コレクションの例:
Node.js
await db.collection('users').add({name: "foo", scores: [5, 4], userScore: 0});
await db.collection('users').add({name: "bar", scores: [1, 3], attempt: 5});
unnest ステージを使用して、ユーザーごとの個々のスコアを抽出できます。
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
この場合、userScore と attempt の両方が上書きされます。
{name: "foo", scores: [5, 4], userScore: 5, attempt: 0}
{name: "foo", scores: [5, 4], userScore: 4, attempt: 1}
{name: "bar", scores: [1, 3], userScore: 1, attempt: 0}
{name: "bar", scores: [1, 3], userScore: 3, attempt: 1}
その他の例
Swift
let results = try await db.pipeline() .database() .unnest(Field("arrayField").as("unnestedArrayField"), indexField: "index") .execute()
Kotlin
val results = db.pipeline() .database() .unnest(field("arrayField").alias("unnestedArrayField"), UnnestOptions().withIndexField("index")) .execute()
Java
Task<Pipeline.Snapshot> results = db.pipeline() .database() .unnest(field("arrayField").alias("unnestedArrayField"), new UnnestOptions().withIndexField("index")) .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field from google.cloud.firestore_v1.pipeline_stages import UnnestOptions results = ( client.pipeline() .database() .unnest( Field.of("arrayField").as_("unnestedArrayField"), options=UnnestOptions(index_field="index"), ) .execute() )
Java
Pipeline.Snapshot results = firestore .pipeline() .database() .unnest("arrayField", "unnestedArrayField", new UnnestOptions().withIndexField("index")) .execute() .get();
配列以外の値
入力式を評価すると配列以外の値になる場合、このステージは入力ドキュメントをそのまま返し、index_field を NULL に設定します(指定されている場合)。
コレクションの例:
Node.js
await db.collection('users').add({name: "foo", scores: 1});
await db.collection('users').add({name: "bar", scores: null});
await db.collection('users').add({name: "qux", scores: {backupScores: 1}});
unnest ステージを使用して、ユーザーごとの個々のスコアを抽出できます。
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
これにより、attempt が NULL に設定された次のドキュメントが生成されます。
{name: "foo", scores: 1, attempt: null}
{name: "bar", scores: null, attempt: null}
{name: "qux", scores: {backupScores: 1}, attempt: null}
空の配列値
入力式を評価すると空の配列になる場合、その入力ドキュメントに対してドキュメントは返されません。
コレクションの例:
Node.js
await db.collection('users').add({name: "foo", scores: [5, 4]});
await db.collection('users').add({name: "bar", scores: []});
unnest ステージを使用して、ユーザーごとの個々のスコアを抽出できます。
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
これにより、出力からユーザー bar が欠落した次のドキュメントが生成されます。
{name: "foo", scores: [5, 4], userScore: 5, attempt: 0}
{name: "foo", scores: [5, 4], userScore: 4, attempt: 1}
空の配列も含むドキュメントを返すには、ネストされていない値を配列でラップします。次に例を示します。
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(
conditional(
equal(field('scores'), []),
array([field('scores')]),
field('scores')
).as("userScore"),
/* index_field= */ "attempt")
.execute();
これで、ユーザー bar を含んだドキュメントが返されるようになります。
{name: "foo", scores: [5, 4], userScore: 5, attempt: 0}
{name: "foo", scores: [5, 4], userScore: 4, attempt: 1}
{name: "bar", scores: [], userScore: [], attempt: 0}
その他の例
Node.js
// Input // { identifier : 1, neighbors: [ "Alice", "Cathy" ] } // { identifier : 2, neighbors: [] } // { identifier : 3, neighbors: "Bob" } const results = await db.pipeline() .database() .unnest(Field.of('neighbors'), 'unnestedNeighbors', 'index') .execute(); // Output // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 } // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 } // { identifier: 3, neighbors: "Bob", index: null}
Swift
// Input // { identifier : 1, neighbors: [ "Alice", "Cathy" ] } // { identifier : 2, neighbors: [] } // { identifier : 3, neighbors: "Bob" } let results = try await db.pipeline() .database() .unnest(Field("neighbors").as("unnestedNeighbors"), indexField: "index") .execute() // Output // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 } // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 } // { identifier: 3, neighbors: "Bob", index: null}
Kotlin
// Input // { identifier : 1, neighbors: [ "Alice", "Cathy" ] } // { identifier : 2, neighbors: [] } // { identifier : 3, neighbors: "Bob" } val results = db.pipeline() .database() .unnest(field("neighbors").alias("unnestedNeighbors"), UnnestOptions().withIndexField("index")) .execute() // Output // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 } // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 } // { identifier: 3, neighbors: "Bob", index: null}
Java
// Input // { identifier : 1, neighbors: [ "Alice", "Cathy" ] } // { identifier : 2, neighbors: [] } // { identifier : 3, neighbors: "Bob" } Task<Pipeline.Snapshot> results = db.pipeline() .database() .unnest(field("neighbors").alias("unnestedNeighbors"), new UnnestOptions().withIndexField("index")) .execute(); // Output // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 } // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 } // { identifier: 3, neighbors: "Bob", index: null}
Python
from google.cloud.firestore_v1.pipeline_expressions import Field from google.cloud.firestore_v1.pipeline_stages import UnnestOptions # Input # { "identifier" : 1, "neighbors": [ "Alice", "Cathy" ] } # { "identifier" : 2, "neighbors": [] } # { "identifier" : 3, "neighbors": "Bob" } results = ( client.pipeline() .database() .unnest( Field.of("neighbors").as_("unnestedNeighbors"), options=UnnestOptions(index_field="index"), ) .execute() ) # Output # { "identifier": 1, "neighbors": [ "Alice", "Cathy" ], # "unnestedNeighbors": "Alice", "index": 0 } # { "identifier": 1, "neighbors": [ "Alice", "Cathy" ], # "unnestedNeighbors": "Cathy", "index": 1 } # { "identifier": 3, "neighbors": "Bob", "index": null}
Java
// Input // { "identifier" : 1, "neighbors": [ "Alice", "Cathy" ] } // { "identifier" : 2, "neighbors": [] } // { "identifier" : 3, "neighbors": "Bob" } Pipeline.Snapshot results = firestore .pipeline() .database() .unnest("neighbors", "unnestedNeighbors", new UnnestOptions().withIndexField("index")) .execute() .get(); // Output // { "identifier": 1, "neighbors": [ "Alice", "Cathy" ], // "unnestedNeighbors": "Alice", "index": 0 } // { "identifier": 1, "neighbors": [ "Alice", "Cathy" ], // "unnestedNeighbors": "Cathy", "index": 1 } // { "identifier": 3, "neighbors": "Bob", "index": null}
ネストされた unnest
式を評価するとネストされた配列になる場合は、複数の unnest ステージを使用して、ネストされた各レベルをフラット化する必要があります。
コレクションの例:
Node.js
await db.collection('users').add({name: "foo", record: [{scores: [5, 4], avg: 4.5}, {scores: [1, 3], old_avg: 2}]});
unnest ステージを続けて使用すると、最も内側の配列を抽出できます。
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('record').as('record'))
.unnest(field('record.scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
これにより、次のドキュメントが生成されます。
{name: "foo", record: [{scores: [5, 4], avg: 4.5}], userScore: 5, attempt: 0}
{name: "foo", record: [{scores: [5, 4], avg: 4.5}], userScore: 4, attempt: 1}
{name: "foo", record: [{scores: [1, 3], avg: 2}], userScore: 1, attempt: 0}
{name: "foo", record: [{scores: [1, 3], avg: 2}], userScore: 3, attempt: 1}