คำอธิบาย
สร้างเอกสารใหม่สำหรับแต่ละองค์ประกอบในอาร์เรย์
เอกสารใหม่จะมีฟิลด์ทั้งหมดจากอินพุตพร้อมกับ
องค์ประกอบที่แตกต่างกันจากอาร์เรย์ ระบบจะจัดเก็บองค์ประกอบอาร์เรย์ไปยัง alias ที่ระบุ
ซึ่งอาจเขียนทับค่าที่มีอยู่ก่อนแล้วซึ่งมีชื่อฟิลด์เดียวกัน
หรือจะระบุอาร์กิวเมนต์ index_field ก็ได้ (ไม่บังคับ) หากมีอยู่
จะรวมดัชนีฐาน 0 ขององค์ประกอบจากอาร์เรย์แหล่งที่มาไว้ในเอกสารเอาต์พุต
โดยขั้นตอนนี้จะทำงานคล้ายกับ CROSS JOIN UNNEST(...) ในระบบ SQL หลายระบบ
ตัวอย่าง
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();
Go
snapshot := client.Pipeline(). Database(). UnnestWithAlias("arrayField", "unnestedArrayField", firestore.WithUnnestIndexField("index")). Execute(ctx)
ค่าที่ไม่ใช่อาร์เรย์
หากนิพจน์อินพุตประเมินเป็นค่าที่ไม่ใช่อาร์เรย์ ขั้นตอนนี้จะ
แสดงผลเอกสารอินพุตตามเดิมโดยตั้งค่า 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}
Go
// Input // { "identifier" : 1, "neighbors": [ "Alice", "Cathy" ] } // { "identifier" : 2, "neighbors": [] } // { "identifier" : 3, "neighbors": "Bob" } results, err := client.Pipeline(). Database(). UnnestWithAlias("neighbors", "unnestedNeighbors", firestore.WithUnnestIndexField("index")). Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err } // Output // { "identifier": 1, "neighbors": [ "Alice", "Cathy" ], // "unnestedNeighbors": "Alice", "index": 0 } // { "identifier": 1, "neighbors": [ "Alice", "Cathy" ], // "unnestedNeighbors": "Cathy", "index": 1 } // { "identifier": 3, "neighbors": "Bob", "index": nil}
Nested 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 }