सबक्वेरी के साथ JOIN ऑपरेशन करना

खास जानकारी

Firestore Enterprise एडिशन में, कोरिलेटेड सबक्वेरी की मदद से, रिलेशनल-स्टाइल जॉइन का इस्तेमाल किया जा सकता है. कई NoSQL डेटाबेस में, डेटा को डीनॉर्मलाइज़ करने या क्लाइंट-साइड से कई अनुरोध करने की ज़रूरत होती है. हालांकि, सबक्वेरी की मदद से, सर्वर पर सीधे तौर पर संबंधित कलेक्शन या सब-कलेक्शन से डेटा को जोड़ा और इकट्ठा किया जा सकता है.

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

कॉन्सेप्ट

इस सेक्शन में, पाइपलाइन ऑपरेशन में शामिल होने के लिए सबक्वेरी का इस्तेमाल करने से जुड़े मुख्य कॉन्सेप्ट के बारे में बताया गया है.

एक्सप्रेशन के तौर पर सबक्वेरी

सबक्वेरी, टॉप-लेवल का स्टेज नहीं है. इसके बजाय, यह एक एक्सप्रेशन है. इसका इस्तेमाल ऐसे किसी भी स्टेज में किया जा सकता है जो एक्सप्रेशन स्वीकार करता है. जैसे, select(...), add_fields(...), where(...) या sort(...).

Cloud Firestore तीन तरह की सबक्वेरी के साथ काम करता है:

  • ऐरे सबक्वेरी: सबक्वेरी के पूरे नतीजे को दस्तावेज़ों के ऐरे के तौर पर सेव करती हैं.
  • स्केलर सबक्वेरी: इनका आकलन एक वैल्यू के तौर पर किया जाता है. जैसे, गिनती, औसत या मिलते-जुलते दस्तावेज़ का कोई खास फ़ील्ड.
  • subcollection(...) सबक्वेरी: पैरंट-चाइल्ड के एक से ज़्यादा संबंधों के लिए, जॉइन को आसान बनाया गया है.

स्कोप और वैरिएबल

जॉइन लिखते समय, नेस्ट की गई सबक्वेरी को अक्सर "आउटर" दस्तावेज़ (पैरंट) के फ़ील्ड का रेफ़रंस देना होता है. इन स्कोप को जोड़ने के लिए, let(...) स्टेज का इस्तेमाल किया जाता है. इसे कुछ SDK में define(...) कहा जाता है. इसका इस्तेमाल पैरंट स्कोप में ऐसे वैरिएबल तय करने के लिए किया जाता है जिन्हें बाद में variable(...) फ़ंक्शन का इस्तेमाल करके, सबक्वेरी में रेफ़र किया जा सकता है.

सिंटैक्स

यहां दिए गए सेक्शन में, जॉइन करने के लिए सिंटैक्स के बारे में खास जानकारी दी गई है.

let(...) स्टेज

let(...) स्टेज (कुछ SDK में इसे define(...) कहा जाता है) एक नॉन-फ़िल्टरिंग स्टेज है. यह पैरंट स्कोप से डेटा को साफ़ तौर पर किसी नाम वाले वैरिएबल में लाता है, ताकि इसका इस्तेमाल बाद के नेस्ट किए गए स्कोप में किया जा सके.

ऐरे सबक्वेरी

ऐरे सबक्वेरी, एक्सप्रेशन सबक्वेरी का एक खास उदाहरण है. यह सबक्वेरी के पूरे नतीजे को ऐरे में बदल देती है. अगर सबक्वेरी से कोई भी पंक्ति नहीं मिलती है, तो यह एक खाली कलेक्शन के तौर पर काम करती है. यह कभी भी null कलेक्शन नहीं दिखाता. इस तरह की क्वेरी तब काम आती हैं, जब फ़ाइनल नतीजे में पूरे नतीजे चाहिए होते हैं. जैसे, नेस्ट किए गए या मिलते-जुलते कलेक्शन को मटीरियलाइज़ करते समय.

क्वेरी, सबक्वेरी में फ़िल्टर, क्रम से लगाने, और एग्रीगेट करने की सुविधा देती हैं. इससे फ़ेच किए जाने वाले डेटा की मात्रा को कम किया जा सकता है. साथ ही, क्वेरी की लागत को कम करने के लिए, डेटा को वापस किया जा सकता है. सबक्वेरी के क्रम का पालन किया जाता है. इसका मतलब है कि सबक्वेरी में मौजूद sort(...) स्टेज, फ़ाइनल कलेक्शन में नतीजों के क्रम को कंट्रोल करती है.

क्वेरी को ऐरे में बदलने के लिए, toArrayExpression() एसडीके रैपर का इस्तेमाल करें.

स्केलर सबक्वेरी

स्केलर सबक्वेरी का इस्तेमाल अक्सर select(...) या where(...) स्टेज में किया जाता है. इससे फ़िल्टर करने या पूरी क्वेरी को सीधे तौर पर लागू किए बिना, सबक्वेरी के नतीजे पाने की अनुमति मिलती है.

स्केलर सबक्वेरी, जो कोई नतीजा नहीं देती है उसका आकलन null के तौर पर किया जाएगा. वहीं, कई एलिमेंट का आकलन करने वाली सबक्वेरी से रनटाइम गड़बड़ी होगी.

जब कोई स्केलर सबक्वेरी, हर नतीजे के लिए सिर्फ़ एक फ़ील्ड जनरेट करती है, तो उस फ़ील्ड को सबक्वेरी के लिए टॉप-लेवल का नतीजा बना दिया जाता है. यह गड़बड़ी आम तौर पर तब दिखती है, जब सबक्वेरी select(field("user_name")) या aggregate(countAll().as("total")) से खत्म होती है. इसमें सबक्वेरी का स्कीमा सिर्फ़ एक फ़ील्ड होता है. इसके अलावा, जब कोई सबक्वेरी कई फ़ील्ड जनरेट कर सकती है, तो उन्हें मैप में रैप किया जाता है.

क्वेरी को स्केलर एक्सप्रेशन में बदलने के लिए, toScalarExpression() SDK रैपर का इस्तेमाल करें.

subcollection(...) सबक्वेरी

subcollection(...) इनपुट स्टेज को एक स्टेज के तौर पर उपलब्ध कराया जाता है. इसकी मदद से, Cloud Firestore के हैरारिकल डेटा मॉडल पर जॉइन किए जा सकते हैं. किसी क्रमिक मॉडल में, क्वेरी को अक्सर किसी दस्तावेज़ के साथ-साथ उसकी सब-कलेक्शन से डेटा भी वापस पाना होता है. इस काम को collection_group(...) इनपुट स्टेज का इस्तेमाल करके किया जा सकता है. इसके बाद, पैरंट रेफ़रंस पर फ़िल्टर लगाया जा सकता है. हालांकि, subcollection(...) का सिंटैक्स ज़्यादा छोटा होता है.

जॉइन करने की शर्त के अलावा, यह एक ऐरे सबक्वेरी की तरह काम करता है. अगर कोई दस्तावेज़ मैच नहीं होता है, तो यह खाली नतीजा दिखाता है. भले ही, नेस्ट किया गया कलेक्शन मौजूद न हो.

यह मूल रूप से सिंटैक्टिक शुगर है: यह बाहरी स्कोप में मौजूद दस्तावेज़ के __name__ को, क्रमबद्ध संबंध को हल करने के लिए जॉइन की के तौर पर अपने-आप इस्तेमाल करता है. इसलिए, पैरंट-चाइल्ड रिलेशनशिप में लिंक की गई सभी कलेक्शन में लुकअप करने के लिए, इस फ़ंक्शन का इस्तेमाल करना सबसे अच्छा तरीका है.

उदाहरण

डेटा का उदाहरण

यहां दिए गए कोड से, टेस्ट डेटा का एक सेट लोड होता है. इसका इस्तेमाल यहां दिए गए सभी उदाहरणों में किया जा सकता है.

Node.js

// Load set of cities.
const cities = collection(db, "cities");

await setDoc(doc(cities, "SF"), {
  name: "San Francisco",
  state: "CA",
  country: "USA",
});
await setDoc(doc(cities, "LA"), {
  name: "Los Angeles",
  state: "CA",
  country: "USA"
});
await setDoc(doc(cities, "DC"), {
  name: "Washington, D.C.",
  state: null,
  country: "USA"
});
await setDoc(doc(cities, "TOK"), {
  name: "Tokyo",
  state: null,
  country: "Japan"
});

// Load restaurants in various cities.
const sfRestaurants = collection(db, "cities", "SF", "restaurants");
const laRestaurants = collection(db, "cities", "LA", "restaurants");
const dcRestaurants = collection(db, "cities", "DC", "restaurants");

const rest1 = await addDoc(sfRestaurants, {
  name: "Golden Gate Pizza",
  type: "pizza",
  owner_id: "Mario Rossi"
});
const rest2 = await addDoc(sfRestaurants, {
  name: "Bay Area Burger",
  type: "burger",
  owner_id: "Sarah Jenkins"
});
const rest3 = await addDoc(sfRestaurants, {
  name: "Sunset Taco",
  type: "mexican",
  owner_id: "Edward"
});

const rest4 = await addDoc(laRestaurants, {
  name: "Hollywood Sushi",
  type: "sushi",
  owner_id: "Ken Kenji"
});
const rest5 = await addDoc(laRestaurants, {
  name: "Venice Pizza",
  type: "pizza",
  owner_id: "Luigi Romano"
});

const rest6 = await addDoc(dcRestaurants, {
  name: "Capitol Tacos",
  type: "mexican",
  owner_id: "Maria Garcia"
});
const rest7 = await addDoc(dcRestaurants, {
  name: "Georgetown Coffee",
  type: "cafe",
  owner_id: "David Kim"
});

// Load collection of reviews.
const reviews = collection(db, "reviews");

await addDoc(reviews, { restaurant: rest1, rating: 5, reviewer_id "Alice" });
await addDoc(reviews, { restaurant: rest1, rating: 4, reviewer_id "Bob" });
await addDoc(reviews, { restaurant: rest2, rating: 4, reviewer_id "Charlie" });
await addDoc(reviews, { restaurant: rest3, rating: 5, reviewer_id "Diana" });
await addDoc(reviews, { restaurant: rest3, rating: 4, reviewer_id "Edward" });
await addDoc(reviews, { restaurant: rest3, rating: 4, reviewer_id "Fiona" });
// rest4 has 0 reviews
await addDoc(reviews, { restaurant: rest5, rating: 3, reviewer_id "George" });
await addDoc(reviews, { restaurant: rest6, rating: 5, reviewer_id "Hannah" });
await addDoc(reviews, { restaurant: rest6, rating: 4, reviewer_id "Ian" });
await addDoc(reviews, { restaurant: rest7, rating: 5, reviewer_id "Julia" });

किसी दूसरे कलेक्शन में मौजूद दस्तावेज़ खोजना

reviews कलेक्शन ग्रुप पर मौजूद यह क्वेरी, प्राइमरी कुंजी के रेफ़रंस का इस्तेमाल करके restaurant कलेक्शन ग्रुप में लुकअप करती है.

Node.js

let results = await execute(db.pipeline()
  .collectionGroup("reviews")
  .define(field("restaurant").as("restaurant_name"))
  .addFields(db.pipeline()
    .collectionGroup("restaurant")
    .where(field("__name__").equal(variable("restaurant_name")))
    .select("name", "type")
    .toScalarExpression()
    .as("restaurant")));

जवाब

{
  rating: 5,
  reviewer_id "Alice",
  restaurant: { name: "Golden Gate Pizza", type: "pizza" }
},
{
  rating: 4,
  reviewer_id "Bob",
  restaurant: { name: "Golden Gate Pizza", type: "pizza" }
},
{
  rating: 4,
  reviewer_id "Charlie",
  restaurant: { name: "Bay Area Burger", type: "burger" }
},
{
  rating: 5,
  reviewer_id "Diana",
  restaurant: { name: "Sunset Taco", type: "mexican" }
},
{
  rating: 4,
  reviewer_id "Edward",
  restaurant: { name: "Sunset Taco", type: "mexican" }
},
{
  rating: 4,
  reviewer_id "Fiona",
  restaurant: { name: "Sunset Taco", type: "mexican" }
},
{
  rating: 3,
  reviewer_id "George",
  restaurant: { name: "Venice Pizza", type: "pizza" }
},
{
  rating: 5,
  reviewer_id "Hannah",
  restaurant: { name: "Capitol Tacos", type: "mexican" }
},
{
  rating: 4,
  reviewer_id "Ian",
  restaurant: { name: "Capitol Tacos", type: "mexican" }
},
{
  rating: 5,
  reviewer_id "Julia",
  restaurant: { name: "Georgetown Coffee", type: "cafe" }
}

एक से ज़्यादा कलेक्शन को एक साथ जोड़ना

यहां दी गई क्वेरी, restaurants कलेक्शन ग्रुप से सभी पिज़्ज़ा शॉप की जानकारी फ़ेच करती है. साथ ही, ऐरे सबक्वेरी का इस्तेमाल करके, उनसे जुड़ी समीक्षाओं को फ़ेच करती है और उन्हें सीधे तौर पर जवाब में एम्बेड करती है.

Node.js

let results = await execute(db.pipeline()
  .collectionGroup("restaurants")
  .where(field("type").equal("pizza"))
  .define(field("__name__").as("restaurant_name"))
  .select(
    field("name"),
    db.pipeline()
      .collectionGroup("reviews")
      .where(field("restaurant").equal(variable("restaurant_name")))
      .select("rating", "reviewer_id")
      .toArrayExpression()
      .as("reviews")));

जवाब

{
  name: "Golden Gate Pizza",
  reviews: [
    { rating: 5, reviewer_id "Alice" },
    { rating: 4, reviewer_id "Bob" }
  ]
},
{
  name: "Venice Pizza",
  type: "pizza",
  owner_id: "Luigi Romano",
  reviews: [
    { rating: 3, reviewer_id "George" }
  ]
}

कई कलेक्शन में कुल संख्या देखना

restaurants कलेक्शन ग्रुप पर की गई इस क्वेरी में, कोरिलेटेड सबक्वेरी का इस्तेमाल किया गया है. इससे reviews कलेक्शन ग्रुप में मौजूद हर रेस्टोरेंट की औसत रेटिंग मिलती है.

Node.js

let results = await execute(db.pipeline()
  .collectionGroup("restaurants")
  .where(field("type").equal("pizza"))
  .define(field("__name__").as("restaurant_name"))
  .select(
    field("name"),
    db.pipeline()
      .collectionGroup("reviews")
      .where(field("restaurant").equal(variable("restaurant_name")))
      .aggregate(average("rating").as("avg_rating"))
      .toScalarExpression()
      .as("avg_rating")));

जवाब

{
  name: "Golden Gate Pizza",
  avg_rating: 4.5
},
{
  name: "Venice Pizza",
  avg_rating: 3.0
}

हर ग्रुप के हिसाब से टॉप-एन (सीमा के साथ सबक्वेरी)

नीचे दी गई क्वेरी, restaurants कलेक्शन ग्रुप से सभी दस्तावेज़ फ़ेच करती है. साथ ही, हर रेस्टोरेंट के लिए सबसे ज़्यादा रेटिंग वाली दो समीक्षाएं फ़ेच करने के लिए, कोरिलेटेड सबक्वेरी का इस्तेमाल करती है.

इससे यह पक्का होता है कि समीक्षाओं की संख्या बहुत ज़्यादा न हो और क्वेरी की मेमोरी की सीमा पूरी न हो.

Node.js

let results = await execute(db.pipeline()
  .collectionGroup("restaurants")
  .define(field("__name__").as("restaurant_name"))
  .select(
    field("name"),
    db.pipeline()
      .collectionGroup("reviews")
      .where(field("restaurant").equal(variable("restaurant_name")))
      .sort(field("rating").descending())
      .limit(2)
      .select("rating", "reviewer_id")
      .toArrayExpression()
      .as("top_reviews")));

जवाब

{
  name: "Golden Gate Pizza",
  top_reviews: [
    { rating: 5, reviewer_id "Alice" },
    { rating: 4, reviewer_id "Bob" }
  ]
},
{
  name: "Bay Area Burger",
  top_reviews: [
    { rating: 4, reviewer_id "Charlie" }
  ]
},
{
  name: "Sunset Taco",
  top_reviews: [
    { rating: 5, reviewer_id "Diana" },
    { rating: 4, reviewer_id "Edward" }
  ]
},
{
  name: "Hollywood Sushi",
  top_reviews: []
},
{
  name: "Venice Pizza",
  top_reviews: [
    { rating: 3, reviewer_id "George" }
  ]
},
{
  name: "Capitol Tacos",
  top_reviews: [
    { rating: 5, reviewer_id "Hannah" },
    { rating: 4, reviewer_id "Ian" }
  ]
},
{
  name: "Georgetown Coffee",
  top_reviews: [
    { rating: 5, reviewer_id "Julia" }
  ]
}

सब-कलेक्शन में शामिल होना

नीचे दी गई क्वेरी, cities कलेक्शन को स्कैन करती है. साथ ही, subcollection(...) स्टेज का इस्तेमाल करके, नेस्ट किए गए कलेक्शन के दस्तावेज़ों को अपने-आप जोड़ती है. इससे, हर शहर में रेस्टोरेंट की संख्या का पता चलता है.

Node.js

let results = await execute(db.pipeline()
  .collection("cities")
  .addFields(subcollection("restaurants")
    .toArrayExpression()
    .length()
    .as("restaurant_count")));

जवाब

{
  __name__: cities/SF,
  name: "San Francisco",
  state: "CA",
  country: "USA",
  restaurant_count: 3
},
{
  __name__: cities/LA,
  name: "Los Angeles",
  state: "CA",
  country: "USA",
  restaurant_count: 2
},
{
  __name__: cities/DC,
  name: "Washington, D.C.",
  state: null,
  country: "USA",
  restaurant_count: 2
},
{
  __name__: cities/TOK,
  name: "Tokyo",
  state: null,
  country: "Japan",
  restaurant_count: 0
}

एक साथ कई शर्तें जोड़ना

नीचे दी गई क्वेरी, restaurants कलेक्शन ग्रुप को स्कैन करती है. साथ ही, restaurants कलेक्शन ग्रुप के साथ मल्टी-फ़ील्ड जॉइन करती है, ताकि उन मालिकों का पता लगाया जा सके जो अपने रेस्टोरेंट की समीक्षा कर रहे हैं.reviews

Node.js

let results = await execute(db.pipeline()
  .collectionGroup("restaurants")
  .define(field("owner_id"), field("__name__"))
  .where(db.pipeline()
    .collectionGroup("reviews")
    .where(field("restaurant").equal(variable("__name__")))
    .where(field("author").equal(variable("owner_id")))
    .aggregate(count().as("c"))
    .toScalarExpression()
    .greaterThan(0)));

जवाब

{
  __name__: cities/SF/restaurants/X9An0HIlx29A9GPuRthS,
  name: "Sunset Taco",
  type: "mexican",
  owner_id: "Edward"
}

एंटी-जॉइन (NOT EXISTS)

नीचे दी गई क्वेरी, restaurants कलेक्शन ग्रुप को स्कैन करती है. साथ ही, उन सभी रेस्टोरेंट का पता लगाती है जिनकी अब तक कोई समीक्षा नहीं की गई है.

Node.js

let results = await execute(db.pipeline()
  .collectionGroup("restaurants")
  .define(field("__name__").as("restaurant_name"))
  .where(db.pipeline()
    .collectionGroup("reviews")
    .where(field("restaurant").equal(variable("restaurant_name")))
    .aggregate(count().as("review_count"))
    .toScalarExpression()
    .equal(0)));

जवाब

{
  __name__: "cities/LA/restaurants/X9An0HIlx29A9GPuRthS",
  name: "Hollywood Sushi",
  type: "sushi",
  owner_id: "Ken Kenji"
}

सबक्वेरी को जॉइन के तौर पर इस्तेमाल करना

यहां दी गई क्वेरी, हर पिज़्ज़ा प्लेस और उसके रिव्यू के बीच के संबंध को फ़्लैट करती है. सबक्वेरी को unnest(...) स्टेज में रखने पर, सर्वर हर मैचिंग समीक्षा के लिए बाहरी रेस्टोरेंट दस्तावेज़ को डुप्लीकेट करता है. इससे फ़्लैट, जॉइंट किए गए दस्तावेज़ तैयार होते हैं. ये SQL INNER JOIN के जैसे होते हैं.

Node.js

let results = await execute(db.pipeline()
  .collectionGroup("restaurants")
  .where(field("type").equal("pizza"))
  .define(field("__name__").as("restaurant_name"))
  .unnest(
    db.pipeline()
      .collectionGroup("reviews")
      .where(field("restaurant").equal(variable("restaurant_name")))
      .select("rating", "reviewer_id")
      .toArrayExpression()
      .as("review")));

जवाब

{
  __name__: "cities/SF/restaurants/xU4pu8nFpnJDPZOwcSPP",
  name: "Golden Gate Pizza",
  type: "pizza",
  owner_id: "Mario Rossi"
  review: { rating: 5, reviewer_id "Alice" }
},
{
  __name__: "cities/SF/restaurants/xU4pu8nFpnJDPZOwcSPP",
  name: "Golden Gate Pizza",
  type: "pizza",
  owner_id: "Mario Rossi",
  review: { rating: 4, reviewer_id "Bob" }
},
{
  __name__: "cities/LA/restaurants/6CYntvNgbYzgaW652Gq1",
  name: "Venice Pizza",
  type: "pizza",
  owner_id: "Luigi Romano",
  review: { rating: 3, reviewer_id "George" }
}

फ़िल्टर के तौर पर इस्तेमाल की गई, बिना संबंध वाली सबक्वेरी

reviews कलेक्शन पर की गई इस क्वेरी में, फ़िल्टर करने के लिए खुद पर असंबद्ध सबक्वेरी का इस्तेमाल किया जाता है. इससे औसत रेटिंग से ज़्यादा रेटिंग वाली समीक्षाएं मिलती हैं.

Node.js

let results = await execute(db.pipeline()
  .collection("reviews")
  // Average review rating is 4.3
  .where(field("rating").greaterThan(db.pipeline()
    .collection("reviews")
    .aggregate(average("rating").as("avg"))
    .toScalarExpression())))
  .select("rating", "reviewer_id");

जवाब

{
  rating: 5,
  reviewer_id "Alice"
},
{
  rating: 5,
  reviewer_id "Diana"
},
{
  rating: 5,
  reviewer_id "Hannah"
},
{
  rating: 5,
  reviewer_id "Julia"
}

सबसे सही तरीके

  • toArrayExpression() की मदद से मेमोरी मैनेज करें: toArrayExpression() सबक्वेरी का इस्तेमाल करते समय सावधानी बरतें. ऐसा इसलिए, क्योंकि बड़ी संख्या में दस्तावेज़ों को प्रोसेस करने से क्वेरी की मेमोरी की सीमा (128 MiB) खत्म हो सकती है. इस समस्या को कम करने के लिए, सबक्वेरी में select(...) का इस्तेमाल करें, ताकि सिर्फ़ ज़रूरी फ़ील्ड दिखाए जा सकें. साथ ही, where(...) फ़िल्टर लागू करके, दिखाए गए दस्तावेज़ों की संख्या को सीमित करें. अगर ज़रूरी हो, तो सबक्वेरी से मिले दस्तावेज़ों की संख्या सीमित करने के लिए, limit(...) का इस्तेमाल करें.
  • इंडेक्सिंग: पक्का करें कि सबक्वेरी के where(...) क्लॉज़ में इस्तेमाल किए गए फ़ील्ड इंडेक्स किए गए हों. परफ़ॉर्मेंस वाले जॉइन, पूरी टेबल स्कैन करने के बजाय इंडेक्स सीक करने की क्षमता पर निर्भर करते हैं.

क्वेरी के सबसे सही तरीकों के बारे में ज़्यादा जानने के लिए, क्वेरी ऑप्टिमाइज़ेशन के बारे में जानकारी देने वाली हमारी गाइड पढ़ें.

सीमाएं

  • subcollection(...) स्कोप: subcollection(...) इनपुट स्टेज का इस्तेमाल सिर्फ़ सबक्वेरी में किया जा सकता है. ऐसा इसलिए, क्योंकि इसे हैरारिकल रिलेशनशिप को हल करने और जॉइन करने के लिए, पैरंट दस्तावेज़ के कॉन्टेक्स्ट की ज़रूरत होती है.
  • नेस्टिंग डेप्थ: सबक्वेरी को ज़्यादा से ज़्यादा 20 लेयर तक नेस्ट किया जा सकता है.
  • मेमोरी का इस्तेमाल: मेटेरियलाइज़ किए गए डेटा के लिए 128 MiB की सीमा, पूरी क्वेरी पर लागू होती है. इसमें शामिल किए गए सभी दस्तावेज़ भी शामिल हैं.