সাবকোয়েরিগুলির সাথে জয়েন সম্পাদন করুন

পটভূমি

পাইপলাইন অপারেশন হলো Cloud Firestore একটি নতুন কোয়েরি ইন্টারফেস। এই ইন্টারফেসটি জটিল এক্সপ্রেশনসহ উন্নত কোয়েরি কার্যকারিতা প্রদান করে। ফায়ারস্টোর এন্টারপ্রাইজ সংস্করণ কোরিলেটেড সাবকোয়েরির মাধ্যমে রিলেশনাল-স্টাইলের জয়েন সমর্থন করে। অনেক NoSQL ডেটাবেসের মতো নয়, যেগুলোতে প্রায়শই ডেটা ডিনরমালাইজ করা বা একাধিক ক্লায়েন্ট-সাইড অনুরোধ সম্পাদন করার প্রয়োজন হয়, সাবকোয়েরি আপনাকে সরাসরি সার্ভারে সম্পর্কিত কালেকশন বা সাবকালেকশন থেকে ডেটা একত্রিত ও অ্যাগ্রিগেট করার সুযোগ দেয়।

সাবকোয়েরি হলো এমন এক্সপ্রেশন যা বাইরের কোয়েরি দ্বারা প্রক্রিয়াকৃত প্রতিটি ডকুমেন্টের জন্য একটি নেস্টেড পাইপলাইন সম্পাদন করে। এটি জটিল ডেটা পুনরুদ্ধার প্যাটার্ন সক্ষম করে, যেমন একটি ডকুমেন্টকে তার সম্পর্কিত সাবকালেকশন আইটেমগুলোর সাথে একত্রে আনা অথবা ভিন্ন ভিন্ন রুট কালেকশনের মধ্যে যৌক্তিকভাবে সংযুক্ত ডেটা যুক্ত করা।

ধারণা

এই অংশে পাইপলাইন অপারেশনে জয়েন সম্পাদনের জন্য সাবকোয়েরি ব্যবহারের মূল ধারণাগুলো তুলে ধরা হয়েছে।

এক্সপ্রেশন হিসেবে সাবকোয়েরি

সাবকোয়েরি কোনো শীর্ষ-স্তরের পর্যায় নয়; বরং এটি একটি এক্সপ্রেশন যা এক্সপ্রেশন গ্রহণকারী যেকোনো পর্যায়ে ব্যবহার করা যায়, যেমন select(...) , add_fields(...) , where(...) , বা sort(...)

Cloud Firestore তিন ধরনের সাবকোয়েরি সমর্থন করে:

  • অ্যারে সাবকোয়েরি: সাবকোয়েরির সম্পূর্ণ ফলাফল সেটকে ডকুমেন্টের একটি অ্যারে হিসেবে বাস্তবায়ন করুন।
  • স্কেলার সাবকোয়েরি: একটি একক মানে মূল্যায়ন করে, যেমন গণনা, গড়, বা সম্পর্কিত কোনো ডকুমেন্টের একটি নির্দিষ্ট ফিল্ড।
  • subcollection(...) সাবকোয়েরি: এক-থেকে-অনেক প্যারেন্ট-চাইল্ড সম্পর্কের জন্য সরলীকৃত জয়েন।

পরিধি এবং ভেরিয়েবল

একটি জয়েন লেখার সময়, নেস্টেড সাবকোয়েরিকে প্রায়শই "বাইরের" ডকুমেন্ট (প্যারেন্ট) থেকে ফিল্ড রেফারেন্স করার প্রয়োজন হয়। এই স্কোপগুলোর মধ্যে সংযোগ স্থাপন করতে, আপনি let(...) পর্যায়টি (কিছু SDK-তে যা define(...) নামে পরিচিত) ব্যবহার করে প্যারেন্ট স্কোপে ভ্যারিয়েবল সংজ্ঞায়িত করেন, যেগুলোকে পরবর্তীতে variable(...) ফাংশন ব্যবহার করে সাবকোয়েরিতে রেফারেন্স করা যায়।

সিনট্যাক্স

নিম্নলিখিত বিভাগগুলিতে জয়েন করার সিনট্যাক্স সম্পর্কে একটি সংক্ষিপ্ত বিবরণ দেওয়া হয়েছে।

let(...) পর্যায়

let(...) পর্যায়টি (কিছু SDK-তে যা define(...) নামে পরিচিত) একটি নন-ফিল্টারিং পর্যায়, যা পরবর্তী নেস্টেড স্কোপগুলোতে ব্যবহারের জন্য প্যারেন্ট স্কোপ থেকে ডেটাকে একটি নামযুক্ত ভেরিয়েবলে সুস্পষ্টভাবে নিয়ে আসে।

Web

    async function defineStageData() {
      await setDoc(doc(collection(db, "Authors"), "author_123"), {
        "id": "author_123",
        "name": "Jane Austen"
      });
    }
    
সুইফট
      func defineStageData() async throws {
      try await db.collection("authors").document("author_123").setData([
        "id": "author_123",
        "name": "Jane Austen"
      ])
    }
    

Kotlin

    fun defineStageData() {
        val author = hashMapOf(
            "id" to "author_123",
            "name" to "Jane Austen",
        )

        db.collection("Authors").document("author_123").set(author)
    }
  

Java

    public void defineStageData() {
        Map<String, Object> author = new HashMap<>();
        author.put("id", "author_123");
        author.put("name", "Jane Austen");

        db.collection("Authors").document("author_123").set(author);
    }
  

অ্যারে সাবকোয়েরি

অ্যারে সাবকোয়েরি হলো এক্সপ্রেশন সাবকোয়েরির একটি বিশেষ রূপ, যা সাবকোয়েরির সম্পূর্ণ ফলাফল সেটকে একটি অ্যারেতে রূপান্তর করে। যদি সাবকোয়েরিটি শূন্যটি সারি ফেরত দেয়, তবে এটি একটি খালি অ্যারেতে রূপান্তরিত হয়। এটি কখনোই একটি null অ্যারে ফেরত দেয় না। এই ধরনের কোয়েরিগুলো তখন উপযোগী হয় যখন চূড়ান্ত ফলাফলে সম্পূর্ণ ফলাফলের প্রয়োজন হয়, যেমন কোনো নেস্টেড বা কোরিলেটেড কালেকশনকে রূপান্তর করার ক্ষেত্রে।

কোয়েরির খরচ কমাতে, ফেচ ও রিটার্ন করা ডেটার পরিমাণও হ্রাস করার জন্য সাবকোয়েরিতে ফিল্টার, সর্ট এবং অ্যাগ্রিগেট করা যায়। সাবকোয়েরির ক্রমকে সম্মান করা হয়, অর্থাৎ সাবকোয়েরির মধ্যে থাকা sort(...) ধাপটি চূড়ান্ত অ্যারেতে ফলাফলের ক্রম নিয়ন্ত্রণ করে।

একটি কোয়েরিকে অ্যারেতে রূপান্তর করতে toArrayExpression() SDK র‍্যাপারটি ব্যবহার করুন।

Web

    async function toArrayExpressionStageData() {
      await setDoc(doc(collection(db, "Projects"), "project_1"), {
        "id": "project_1",
        "name": "Alpha Build"
      });
      await addDoc(collection(db, "Tasks"), {
        "project_id": "project_1",
        "title": "System Architecture"
      });
      await addDoc(collection(db, "Tasks"), {
        "project_id": "project_1",
        "title": "Database Schema Design"
      });
    }
  

প্রতিক্রিয়া

    {
        id: "project_1",
        name: "Alpha Build",
        taskTitles: [
          "System Architecture", "Database Schema Design"
      ]
    }
    
সুইফট
    async function toArrayExpressionStageData() {
      await setDoc(doc(collection(db, "Projects"), "project_1"), {
        "id": "project_1",
        "name": "Alpha Build"
      });
      await addDoc(collection(db, "Tasks"), {
        "project_id": "project_1",
        "title": "System Architecture"
      });
      await addDoc(collection(db, "Tasks"), {
        "project_id": "project_1",
        "title": "Database Schema Design"
      });
    }
    

প্রতিক্রিয়া

    {
      id: "project_1",
      name: "Alpha Build",
      taskTitles: [
        "System Architecture", "Database Schema Design"
      ]
    }
    

Kotlin

    fun toArrayExpressionData() {
        val project = hashMapOf(
            "id" to "project_1",
            "name" to "Alpha Build",
        )
        db.collection("Projects").document("project_1").set(project)

        val task1 = hashMapOf(
            "project_id" to "project_1",
            "title" to "System Architecture",
        )
        db.collection("Tasks").add(task1)

        val task2 = hashMapOf(
            "project_id" to "project_1",
            "title" to "Database Schema Design",
        )
        db.collection("Tasks").add(task2)
    }
  

প্রতিক্রিয়া

    {
      id: "project_1",
      name: "Alpha Build",
      taskTitles: [
        "System Architecture", "Database Schema Design"
      ]
    }
    

Java

      public void toArrayExpressionData() {
        Map<String, Object> project = new HashMap<>();
        project.put("id", "project_1");
        project.put("name", "Alpha Build");
        db.collection("Projects").document("project_1").set(project);

        Map<String, Object> task1 = new HashMap<>();
        task1.put("project_id", "project_1");
        task1.put("title", "System Architecture");
        db.collection("Tasks").add(task1);

        Map<String, Object> task2 = new HashMap<>();
        task2.put("project_id", "project_1");
        task2.put("title", "Database Schema Design");
        db.collection("Tasks").add(task2);
    }
  

প্রতিক্রিয়া

    {
        id: "project_1",
        name: "Alpha Build",
        taskTitles: [
          "System Architecture", "Database Schema Design"
      ]
    }
    

স্কেলার সাবকোয়েরি

স্কেলার সাবকোয়েরি প্রায়শই select(...) বা where(...) পর্যায়ে ব্যবহৃত হয়, কারণ এটি সম্পূর্ণ কোয়েরিটিকে সরাসরি ম্যাটেরিয়ালাইজ না করেই সাবকোয়েরির ফলাফল ফিল্টার বা বের করার সুযোগ দেয়।

একটি স্কেলার সাবকোয়েরি যা শূন্য ফলাফল দেয়, তা নিজেই null হিসেবে ইভ্যালুয়েট হবে, অন্যদিকে একটি সাবকোয়েরি যা একাধিক উপাদানে ইভ্যালুয়েট হয়, তার ফলে একটি রানটাইম এরর দেখা দেবে।

যখন একটি স্কেলার সাবকোয়েরি প্রতি-ফলাফলে শুধুমাত্র একটি ফিল্ড তৈরি করে, তখন সেই ফিল্ডটিকে সাবকোয়েরিটির শীর্ষ-স্তরের ফলাফল হিসেবে উন্নীত করা হয়। এটি সাধারণত তখন দেখা যায় যখন সাবকোয়েরিটি select(field("user_name")) বা aggregate(countAll().as("total")) দিয়ে শেষ হয়, যেখানে সাবকোয়েরিটির স্কিমাটি কেবল একটি একক ফিল্ড। অন্যথায়, যখন একটি সাবকোয়েরি একাধিক ফিল্ড তৈরি করতে পারে, তখন সেগুলোকে একটি ম্যাপের মধ্যে রাখা হয়।

একটি কোয়েরিকে স্কেলার এক্সপ্রেশনে রূপান্তর করতে toScalarExpression() SDK র‍্যাপারটি ব্যবহার করুন।

Web

            async function toScalarExpressionStageData() {
              await setDoc(doc(collection(db, "Authors"), "author_202"), {
                "id": "author_202",
                "name": "Charles Dickens"
              });
              await addDoc(collection(db, "Books"), {
                "author_id": "author_202",
                "title": "Great Expectations",
                "rating": 4.8
              });
              await addDoc(collection(db, "Books"), {
                "author_id": "author_202",
                "title": "Oliver Twist",
                "rating": 4.5
              });
            }
        

প্রতিক্রিয়া

        {
            "id": "author_202",
            "name": "Charles Dickens",
            "averageBookRating": 4.65
        }
      
সুইফট
        try await db.collection("authors").document("author_202").setData([
          "id": "author_202",
          "name": "Charles Dickens"
        ])
        try await db.collection("books").document().setData([
          "author_id": "author_202",
          "title": "Great Expectations",
          "rating": 4.8
        ])
        try await db.collection("books").document().setData([
          "author_id": "author_202",
          "title": "Oliver Twist",
          "rating": 4.5
        ])
        

প্রতিক্রিয়া

        {
            "id": "author_202",
            "name": "Charles Dickens",
            "averageBookRating": 4.65
        }
      

Kotlin

        fun toScalarExpressionData() {
        val author = hashMapOf(
            "id" to "author_202",
            "name" to "Charles Dickens",
        )
        db.collection("Authors").document("author_202").set(author)

        val book1 = hashMapOf(
            "author_id" to "author_202",
            "title" to "Great Expectations",
            "rating" to 4.8,
        )
        db.collection("Books").add(book1)

        val book2 = hashMapOf(
            "author_id" to "author_202",
            "title" to "Oliver Twist",
            "rating" to 4.5,
        )
        db.collection("Books").add(book2)
    }
  

প্রতিক্রিয়া

        {
            "id": "author_202",
            "name": "Charles Dickens",
            "averageBookRating": 4.65
        }
      

Java

       public void toScalarExpressionData() {
        Map<String, Object> author = new HashMap<>();
        author.put("id", "author_202");
        author.put("name", "Charles Dickens");
        db.collection("Authors").document("author_202").set(author);

        Map<String, Object> book1 = new HashMap<>();
        book1.put("author_id", "author_202");
        book1.put("title", "Great Expectations");
        book1.put("rating", 4.8);
        db.collection("Books").add(book1);

        Map<String, Object> book2 = new HashMap<>();
        book2.put("author_id", "author_202");
        book2.put("title", "Oliver Twist");
        book2.put("rating", 4.5);
        db.collection("Books").add(book2);
    }
  

প্রতিক্রিয়া

        {
            "id": "author_202",
            "name": "Charles Dickens",
            "averageBookRating": 4.65
        }
      

subcollection(...) উপ-কোয়েরি

একটি স্টেজ হিসেবে উপলব্ধ হলেও, subcollection(...) ইনপুট স্টেজটি Cloud Firestore হায়ারারকিক্যাল ডেটা মডেলের উপর জয়েন অপারেশন করার সুযোগ দেয়। একটি হায়ারারকিক্যাল মডেলে, কোয়েরিগুলোর প্রায়শই একটি ডকুমেন্টের পাশাপাশি তার নিজস্ব সাব-কালেকশনগুলো থেকেও ডেটা পুনরুদ্ধার করার প্রয়োজন হয়। যদিও আপনি collection_group(...) ইনপুট স্টেজ ব্যবহার করে এবং তারপরে প্যারেন্ট রেফারেন্সের উপর ফিল্টার প্রয়োগ করে এটি করতে পারেন, subcollection(...) অনেক বেশি সংক্ষিপ্ত সিনট্যাক্স প্রদান করে।

অন্তর্নিহিত জয়েন শর্তটি ছাড়া, এটি একটি অ্যারে সাবকোয়েরির মতোই কাজ করে এবং কোনো ডকুমেন্ট মেলানো না গেলে একটি খালি ফলাফল ফেরত দেয়, এমনকি যদি নেস্টেড কালেকশনটির অস্তিত্ব না-ও থাকে।

এটি মূলত একটি সিনট্যাকটিক সুগার : এটি শ্রেণিবদ্ধ সম্পর্কটি সমাধান করার জন্য স্বয়ংক্রিয়ভাবে বাইরের স্কোপে থাকা ডকুমেন্টের __name__ জয়েন কী হিসেবে ব্যবহার করে। এই কারণে, প্যারেন্ট-চাইল্ড সম্পর্কে সংযুক্ত কালেকশনগুলোর মধ্যে লুকআপ করার জন্য এটিই সবচেয়ে পছন্দের উপায়।

সর্বোত্তম অনুশীলন

  • toArrayExpression() দিয়ে মেমরি পরিচালনা করুন: toArrayExpression() সাবকোয়েরি ব্যবহারের ক্ষেত্রে সতর্ক থাকুন, কারণ বিপুল সংখ্যক ডকুমেন্ট ম্যাটেরিয়ালাইজ করতে গেলে কোয়েরির মেমরি লিমিট (১২৮ MiB) শেষ হয়ে যেতে পারে। এটি এড়ানোর জন্য, সাবকোয়েরির মধ্যে select(...) ব্যবহার করে শুধুমাত্র প্রয়োজনীয় ফিল্ডগুলো রিটার্ন করুন এবং রিটার্ন করা ডকুমেন্টের সংখ্যা সীমিত করতে where(...) ফিল্টার প্রয়োগ করুন। প্রয়োজন হলে, সাবকোয়েরি দ্বারা রিটার্ন করা ডকুমেন্টের সংখ্যা সর্বোচ্চ করতে limit(...) ব্যবহার করার কথা বিবেচনা করতে পারেন।
  • ইনডেক্সিং: নিশ্চিত করুন যে একটি সাবকোয়েরির where(...) ক্লজে ব্যবহৃত ফিল্ডগুলো ইনডেক্স করা আছে। পারফরম্যান্ট জয়েনগুলো সম্পূর্ণ টেবিল স্ক্যানের পরিবর্তে ইনডেক্স সিক সম্পাদন করার ক্ষমতার উপর নির্ভর করে।

কোয়েরি সংক্রান্ত আরও সেরা অনুশীলন জানতে, আমাদের কোয়েরি অপ্টিমাইজেশন বিষয়ক নির্দেশিকাটি দেখুন।

সীমাবদ্ধতা

  • subcollection(...) এর পরিধি: subcollection(...) ইনপুট পর্যায়টি শুধুমাত্র সাবকোয়েরির মধ্যেই সমর্থিত, কারণ শ্রেণিবদ্ধ সম্পর্ক নির্ণয় এবং জয়েন সম্পাদনের জন্য এটির একটি প্যারেন্ট ডকুমেন্টের প্রেক্ষাপট প্রয়োজন হয়।
  • নেস্টিং গভীরতা: সাবকোয়েরি সর্বোচ্চ ২০ স্তর পর্যন্ত নেস্ট করা যেতে পারে।
  • মেমরি ব্যবহার: ম্যাটেরিয়ালাইজড ডেটার উপর প্রযোজ্য ১২৮ MiB সীমাটি সমস্ত জয়েন করা ডকুমেন্ট সহ সম্পূর্ণ কোয়েরি জুড়ে কার্যকর।