अननेस्ट

ब्यौरा

यह किसी कलेक्शन में मौजूद हर एलिमेंट के लिए एक नया दस्तावेज़ जनरेट करता है.

नए दस्तावेज़ों में, इनपुट के सभी फ़ील्ड के साथ-साथ कलेक्शन का कोई दूसरा एलिमेंट भी शामिल होता है. कलेक्शन के एलिमेंट को दिए गए alias में सेव किया जाता है. इससे, एक ही फ़ील्ड के नाम वाली पहले से मौजूद कोई भी वैल्यू ओवरराइट हो सकती है.

ज़रूरत पड़ने पर, index_field आर्ग्युमेंट के बारे में बताया जा सकता है. अगर यह आर्ग्युमेंट मौजूद है, तो आउटपुट दस्तावेज़ों में, सोर्स कलेक्शन से एलिमेंट का ज़ीरो-आधारित इंडेक्स शामिल किया जाता है.

यह स्टेज, कई 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();
जाएं
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}

कलेक्शन में कोई वैल्यू नहीं होने पर भी दस्तावेज़ वापस पाने के लिए, unnested वैल्यू को कलेक्शन में रैप किया जा सकता है. उदाहरण के लिए:

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}
जाएं
// 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}

नेस्टेड अननेस्ट

अगर एक्सप्रेशन का आकलन, नेस्ट किए गए कलेक्शन के तौर पर किया जाता है, तो नेस्ट किए गए हर लेवल को फ़्लैट करने के लिए, 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 }