Deskripsi
Menghasilkan dokumen baru untuk setiap elemen dalam array.
Dokumen baru berisi semua kolom dari input beserta elemen yang berbeda
dari array. Elemen array disimpan ke alias yang diberikan,
yang kemungkinan akan menimpa nilai yang sudah ada sebelumnya dengan nama kolom yang sama.
Argumen index_field juga dapat ditentukan (opsional). Jika ada,
ini akan menyertakan indeks berbasis nol elemen dari array sumber dalam dokumen
output.
Tahap ini berperilaku mirip dengan CROSS JOIN UNNEST(...) di banyak sistem SQL.
Sintaksis
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
Perilaku
Kolom Alias dan Index
alias dan index_field (opsional) akan menimpa
kolom asli jika kolom sudah ada dalam dokumen input. Jika
index_field tidak diberikan, dokumen output tidak akan berisi kolom
ini.
Misalnya, untuk koleksi berikut:
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});
Tahap unnest dapat digunakan untuk mengekstrak setiap skor individual per pengguna.
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
Dalam hal ini, userScore dan attempt sama-sama akan ditimpa.
{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}
Contoh tambahan
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();
Nilai Non-Array
Jika ekspresi input menghasilkan nilai non-array, tahap ini akan
menampilkan dokumen input sebagaimana adanya, dengan index_field yang ditetapkan ke NULL,
jika ditentukan.
Misalnya, untuk koleksi berikut:
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}});
Tahap unnest dapat digunakan untuk mengekstrak setiap skor individual per pengguna.
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
Hal ini akan menghasilkan dokumen berikut, dengan attempt yang ditetapkan ke NULL.
{name: "foo", scores: 1, attempt: null}
{name: "bar", scores: null, attempt: null}
{name: "qux", scores: {backupScores: 1}, attempt: null}
Nilai Array Kosong
Jika ekspresi input menghasilkan array kosong, tidak ada dokumen yang akan ditampilkan untuk dokumen input tersebut.
Misalnya, untuk koleksi berikut:
Node.js
await db.collection('users').add({name: "foo", scores: [5, 4]});
await db.collection('users').add({name: "bar", scores: []});
Tahap unnest dapat digunakan untuk mengekstrak setiap skor individual per pengguna.
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
Hal ini akan menghasilkan dokumen berikut, dengan bar pengguna yang tidak ada dalam
output.
{name: "foo", scores: [5, 4], userScore: 5, attempt: 0}
{name: "foo", scores: [5, 4], userScore: 4, attempt: 1}
Untuk menampilkan dokumen dengan array kosong juga, Anda dapat menggabungkan nilai unnest dalam array. Contoh:
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();
Sekarang, dokumen akan ditampilkan dengan bar pengguna.
{name: "foo", scores: [5, 4], userScore: 5, attempt: 0}
{name: "foo", scores: [5, 4], userScore: 4, attempt: 1}
{name: "bar", scores: [], userScore: [], attempt: 0}
Contoh tambahan
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 Bertingkat
Jika ekspresi menghasilkan nilai array bertingkat, beberapa tahap unnest
harus digunakan untuk meratakan setiap level bertingkat.
Misalnya, untuk koleksi berikut:
Node.js
await db.collection('users').add({name: "foo", record: [{scores: [5, 4], avg: 4.5}, {scores: [1, 3], old_avg: 2}]});
Tahap unnest dapat digunakan secara berurutan untuk mengekstrak bagian array terdalam.
Node.js
const userScore = await db.pipeline()
.collection("/users")
.unnest(field('record').as('record'))
.unnest(field('record.scores').as('userScore'), /* index_field= */ 'attempt')
.execute();
Hal ini akan menghasilkan dokumen berikut:
{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}