এই ডকুমেন্টে ডাটাবেস থেকে ডেটা পুনরুদ্ধারের প্রাথমিক বিষয়, ডেটা কীভাবে সাজানো থাকে এবং ডেটার উপর কীভাবে সাধারণ কোয়েরি চালাতে হয়, তা আলোচনা করা হয়েছে। বিভিন্ন প্রোগ্রামিং ল্যাঙ্গুয়েজে অ্যাডমিন এসডিকে-তে ডেটা পুনরুদ্ধারের বাস্তবায়ন কিছুটা ভিন্ন হয়ে থাকে।
- অ্যাসিঙ্ক্রোনাস লিসেনার: একটি ডেটাবেস রেফারেন্সে একটি অ্যাসিঙ্ক্রোনাস লিসেনার সংযুক্ত করার মাধ্যমে Firebase Realtime Database সংরক্ষিত ডেটা পুনরুদ্ধার করা হয়। ডেটার প্রাথমিক অবস্থার জন্য লিসেনারটি একবার ট্রিগার হয় এবং ডেটা পরিবর্তিত হলেই আবার ট্রিগার হয়। একটি ইভেন্ট লিসেনার বিভিন্ন ধরনের ইভেন্ট গ্রহণ করতে পারে। ডেটা পুনরুদ্ধারের এই পদ্ধতিটি জাভা, নোড.জেএস এবং পাইথন অ্যাডমিন এসডিকে-তে সমর্থিত।
- ব্লকিং রিড: Firebase Realtime Database সংরক্ষিত ডেটা একটি ডেটাবেস রেফারেন্সের উপর একটি ব্লকিং মেথড কল করার মাধ্যমে পুনরুদ্ধার করা হয়, যা ওই রেফারেন্সে সংরক্ষিত ডেটা ফেরত দেয়। প্রতিটি মেথড কল একটি এককালীন অপারেশন। এর মানে হলো, SDK পরবর্তী ডেটা আপডেটের জন্য কোনো কলব্যাক রেজিস্টার করে না। ডেটা পুনরুদ্ধারের এই মডেলটি পাইথন এবং গো অ্যাডমিন SDK-তে সমর্থিত।
শুরু করা
ফায়ারবেস ডাটাবেস থেকে কীভাবে ডেটা পড়তে হয় তা বোঝার জন্য, চলুন আগের আর্টিকেলের ব্লগিং উদাহরণটি আবার দেখি। মনে রাখবেন যে, উদাহরণ অ্যাপটির ব্লগ পোস্টগুলো https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json এই ডাটাবেস ইউআরএল-এ সংরক্ষিত আছে। আপনার পোস্টের ডেটা পড়ার জন্য, আপনি নিম্নলিখিত পদক্ষেপগুলো অনুসরণ করতে পারেন:
জাভা
public static class Post { public String author; public String title; public Post(String author, String title) { // ... } } // Get a reference to our posts final FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("server/saving-data/fireblog/posts"); // Attach a listener to read the data at our posts reference ref.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Post post = dataSnapshot.getValue(Post.class); System.out.println(post); } @Override public void onCancelled(DatabaseError databaseError) { System.out.println("The read failed: " + databaseError.getCode()); } });
নোড.জেএস
// Get a database reference to our posts const db = getDatabase(); const ref = db.ref('server/saving-data/fireblog/posts'); // Attach an asynchronous callback to read the data at our posts reference ref.on('value', (snapshot) => { console.log(snapshot.val()); }, (errorObject) => { console.log('The read failed: ' + errorObject.name); });
পাইথন
# Import database module. from firebase_admin import db # Get a database reference to our posts ref = db.reference('server/saving-data/fireblog/posts') # Read the data at the posts reference (this is a blocking operation) print(ref.get())
যান
// Post is a json-serializable type. type Post struct { Author string `json:"author,omitempty"` Title string `json:"title,omitempty"` } // Create a database client from App. client, err := app.Database(ctx) if err != nil { log.Fatalln("Error initializing database client:", err) } // Get a database reference to our posts ref := client.NewRef("server/saving-data/fireblog/posts") // Read the data at the posts reference (this is a blocking operation) var post Post if err := ref.Get(ctx, &post); err != nil { log.Fatalln("Error reading value:", err) }
উপরের কোডটি রান করলে, আপনি কনসোলে আপনার সমস্ত পোস্ট সম্বলিত একটি অবজেক্ট লগ হতে দেখবেন। Node.js এবং Java-এর ক্ষেত্রে, আপনার ডাটাবেস রেফারেন্সে যখনই নতুন ডেটা যুক্ত হয়, তখনই লিসেনার ফাংশনটি কল করা হয় এবং এটি করার জন্য আপনাকে কোনো অতিরিক্ত কোড লিখতে হবে না।
জাভা এবং নোড.জেএস-এ, কলব্যাক ফাংশন একটি DataSnapshot গ্রহণ করে, যা হলো ডেটার একটি স্ন্যাপশট। একটি স্ন্যাপশট হলো একটি নির্দিষ্ট সময়ে কোনো বিশেষ ডাটাবেস রেফারেন্সে থাকা ডেটার একটি ছবি। একটি স্ন্যাপশটের উপর val() / getValue() কল করলে ডেটার একটি ভাষা-নির্দিষ্ট অবজেক্ট রিপ্রেজেন্টেশন ফেরত আসে। যদি রেফারেন্সের অবস্থানে কোনো ডেটা না থাকে, তাহলে স্ন্যাপশটের মান null হয়। পাইথনের get() মেথড সরাসরি ডেটার একটি পাইথন রিপ্রেজেন্টেশন ফেরত দেয়। গো-এর Get() ফাংশন ডেটাকে একটি প্রদত্ত ডেটা স্ট্রাকচারে আনমার্শাল করে।
লক্ষ্য করুন যে আমরা উপরের উদাহরণে ' value ' ইভেন্ট টাইপ ব্যবহার করেছি, যা একটি ফায়ারবেস ডাটাবেস রেফারেন্সের সম্পূর্ণ বিষয়বস্তু পড়ে, এমনকি যদি শুধুমাত্র একটি ডেটাও পরিবর্তিত হয়। ডাটাবেস থেকে ডেটা পড়ার জন্য আপনি ব্যবহার করতে পারেন এমন নিচে তালিকাভুক্ত পাঁচটি ভিন্ন ইভেন্ট টাইপের মধ্যে value একটি।
জাভা এবং নোড.জেএস-এ ইভেন্টের প্রকারভেদ পড়ুন
মূল্য
value ইভেন্টটি একটি নির্দিষ্ট ডাটাবেস পাথের বিষয়বস্তুর একটি স্ট্যাটিক স্ন্যাপশট পড়ার জন্য ব্যবহৃত হয়, যা রিড ইভেন্টের সময় যেমন ছিল ঠিক তেমন অবস্থায় থাকে। এটি প্রাথমিক ডেটা দিয়ে একবার এবং প্রতিবার ডেটা পরিবর্তিত হলে আবার ট্রিগার হয়। ইভেন্ট কলব্যাকে একটি স্ন্যাপশট পাস করা হয়, যাতে চাইল্ড ডেটা সহ সেই অবস্থানের সমস্ত ডেটা থাকে। উপরের কোড উদাহরণে, value আপনার অ্যাপের সমস্ত ব্লগ পোস্ট রিটার্ন করেছে। যখনই একটি নতুন ব্লগ পোস্ট যোগ করা হবে, কলব্যাক ফাংশনটি সমস্ত পোস্ট রিটার্ন করবে।
শিশু যোগ করা হয়েছে
` child_added ইভেন্টটি সাধারণত ডাটাবেস থেকে আইটেমের তালিকা পুনরুদ্ধার করার সময় ব্যবহৃত হয়। value মতো নয়, যা একটি লোকেশনের সম্পূর্ণ বিষয়বস্তু ফেরত দেয়, child_added ইভেন্টটি প্রতিটি বিদ্যমান চাইল্ডের জন্য একবার এবং তারপর নির্দিষ্ট পাথে যখনই একটি নতুন চাইল্ড যুক্ত হয়, তখন আবার ট্রিগার হয়। ইভেন্ট কলব্যাকে নতুন চাইল্ডের ডেটা সম্বলিত একটি স্ন্যাপশট পাঠানো হয়। ক্রমবিন্যাসের উদ্দেশ্যে, এটিকে পূর্ববর্তী চাইল্ডের কী (key) সম্বলিত একটি দ্বিতীয় আর্গুমেন্টও পাঠানো হয়।
আপনার ব্লগিং অ্যাপে যোগ করা প্রতিটি নতুন পোস্টের ডেটা যদি আপনি পেতে চান, তাহলে আপনি child_added ব্যবহার করতে পারেন:
জাভা
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { Post newPost = dataSnapshot.getValue(Post.class); System.out.println("Author: " + newPost.author); System.out.println("Title: " + newPost.title); System.out.println("Previous Post ID: " + prevChildKey); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
নোড.জেএস
// Retrieve new posts as they are added to our database ref.on('child_added', (snapshot, prevChildKey) => { const newPost = snapshot.val(); console.log('Author: ' + newPost.author); console.log('Title: ' + newPost.title); console.log('Previous Post ID: ' + prevChildKey); });
এই উদাহরণে স্ন্যাপশটটিতে একটি স্বতন্ত্র ব্লগ পোস্ট সহ একটি অবজেক্ট থাকবে। যেহেতু SDK ভ্যালুটি গ্রহণ করে পোস্টগুলোকে অবজেক্টে রূপান্তর করে, তাই আপনি যথাক্রমে author এবং title কল করার মাধ্যমে পোস্টটির author এবং title প্রপার্টিগুলো অ্যাক্সেস করতে পারবেন। এছাড়াও, আপনি দ্বিতীয় prevChildKey আর্গুমেন্ট থেকে পূর্ববর্তী পোস্ট আইডি-টিও অ্যাক্সেস করতে পারবেন।
শিশু পরিবর্তিত হয়েছে
যখনই কোনো চাইল্ড নোড পরিবর্তিত হয়, তখনই child_changed ইভেন্টটি ট্রিগার হয়। এর মধ্যে চাইল্ড নোডের ডিসেন্ডেন্টদের যেকোনো পরিবর্তনও অন্তর্ভুক্ত। আইটেমের তালিকার পরিবর্তনে সাড়া দেওয়ার জন্য এটি সাধারণত child_added এবং child_removed সাথে একত্রে ব্যবহৃত হয়। ইভেন্ট কলব্যাকে পাঠানো স্ন্যাপশটটিতে চাইল্ডের জন্য আপডেট করা ডেটা থাকে।
ব্লগ পোস্ট সম্পাদনা করার সময় সেগুলোর হালনাগাদ তথ্য পড়ার জন্য আপনি child_changed ব্যবহার করতে পারেন:
জাভা
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) { Post changedPost = dataSnapshot.getValue(Post.class); System.out.println("The updated post title is: " + changedPost.title); } @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
নোড.জেএস
// Get the data on a post that has changed ref.on('child_changed', (snapshot) => { const changedPost = snapshot.val(); console.log('The updated post title is ' + changedPost.title); });
শিশু অপসারণ
যখন কোনো নিকটবর্তী চাইল্ডকে সরিয়ে দেওয়া হয়, তখন child_removed ইভেন্টটি ট্রিগার হয়। এটি সাধারণত child_added এবং child_changed সাথে একত্রে ব্যবহৃত হয়। ইভেন্ট কলব্যাকে পাঠানো স্ন্যাপশটটিতে সরিয়ে দেওয়া চাইল্ডের ডেটা থাকে।
ব্লগের উদাহরণে, মুছে ফেলা পোস্ট সম্পর্কে একটি বিজ্ঞপ্তি কনসোলে লগ করতে আপনি child_removed ব্যবহার করতে পারেন:
জাভা
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) { Post removedPost = dataSnapshot.getValue(Post.class); System.out.println("The blog post titled " + removedPost.title + " has been deleted"); } @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
নোড.জেএস
// Get a reference to our posts const ref = db.ref('server/saving-data/fireblog/posts'); // Get the data on a post that has been removed ref.on('child_removed', (snapshot) => { const deletedPost = snapshot.val(); console.log('The blog post titled \'' + deletedPost.title + '\' has been deleted'); });
শিশু স্থানান্তরিত
অর্ডার করা ডেটা নিয়ে কাজ করার সময় child_moved ইভেন্টটি ব্যবহৃত হয়, যা পরবর্তী বিভাগে আলোচনা করা হয়েছে।
ইভেন্টের গ্যারান্টি
ফায়ারবেস ডেটাবেস ইভেন্টগুলির বিষয়ে বেশ কিছু গুরুত্বপূর্ণ নিশ্চয়তা প্রদান করে:
| ডাটাবেস ইভেন্ট গ্যারান্টি |
|---|
| স্থানীয় অবস্থার পরিবর্তন হলেই ইভেন্টগুলো সর্বদা সক্রিয় হবে। |
| ইভেন্টগুলো শেষ পর্যন্ত ডেটার সঠিক অবস্থাই প্রতিফলিত করবে, এমনকি স্থানীয় কার্যক্রম বা সময়ের কারণে সাময়িক পার্থক্য তৈরি হলেও, যেমন নেটওয়ার্ক সংযোগ সাময়িকভাবে বিচ্ছিন্ন হয়ে গেলে। |
| একটি ক্লায়েন্ট থেকে করা রাইটগুলো সর্বদা সার্ভারে লেখা হবে এবং ক্রমানুসারে অন্যান্য ব্যবহারকারীদের কাছে সম্প্রচার করা হবে। |
| ভ্যালু ইভেন্টগুলো সর্বদা সবশেষে ট্রিগার হয় এবং ওই স্ন্যাপশটটি নেওয়ার আগে ঘটে যাওয়া অন্য যেকোনো ইভেন্টের আপডেট এতে অন্তর্ভুক্ত থাকার নিশ্চয়তা থাকে। |
যেহেতু ভ্যালু ইভেন্টগুলো সর্বদা শেষে ট্রিগার হয়, তাই নিম্নলিখিত উদাহরণটি সবসময় কাজ করবে:
জাভা
final AtomicInteger count = new AtomicInteger(); ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { // New child added, increment count int newCount = count.incrementAndGet(); System.out.println("Added " + dataSnapshot.getKey() + ", count is " + newCount); } // ... }); // The number of children will always be equal to 'count' since the value of // the dataSnapshot here will include every child_added event triggered before this point. ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { long numChildren = dataSnapshot.getChildrenCount(); System.out.println(count.get() + " == " + numChildren); } @Override public void onCancelled(DatabaseError databaseError) {} });
নোড.জেএস
let count = 0; ref.on('child_added', (snap) => { count++; console.log('added:', snap.key); }); // length will always equal count, since snap.val() will include every child_added event // triggered before this point ref.once('value', (snap) => { console.log('initial data loaded!', snap.numChildren() === count); });
কলব্যাক বিচ্ছিন্ন করা
ইভেন্টের ধরণ এবং যে কলব্যাক ফাংশনটি সরাতে চান তা নির্দিষ্ট করে কলব্যাকগুলি সরানো হয়, যেমনটি নিচে দেখানো হয়েছে:
জাভা
// Create and attach listener ValueEventListener listener = new ValueEventListener() { // ... }; ref.addValueEventListener(listener); // Remove listener ref.removeEventListener(listener);
নোড.জেএস
ref.off('value', originalCallback);
যদি আপনি on() ফাংশনে একটি স্কোপ কনটেক্সট পাস করে থাকেন, তাহলে কলব্যাকটি ডিটাচ করার সময়ও সেটি অবশ্যই পাস করতে হবে:
জাভা
// Not applicable for Javaনোড.জেএস
ref.off('value', originalCallback, ctx);
কোনো একটি নির্দিষ্ট স্থানের সমস্ত কলব্যাক মুছে ফেলতে চাইলে, আপনি নিম্নলিখিত পদক্ষেপগুলো অনুসরণ করতে পারেন:
জাভা
// No Java equivalent, listeners must be removed individually.নোড.জেএস
// Remove all value callbacks ref.off('value'); // Remove all callbacks of any type ref.off();
ডেটা একবার পড়া
কিছু ক্ষেত্রে একটি কলব্যাককে একবার কল করে সাথে সাথে সরিয়ে ফেলা সুবিধাজনক হতে পারে। এই কাজটি সহজ করার জন্য আমরা একটি হেল্পার ফাংশন তৈরি করেছি:
জাভা
ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // ... } @Override public void onCancelled(DatabaseError databaseError) { // ... } });
নোড.জেএস
ref.once('value', (data) => { // do some stuff once });
পাইথন
# Import database module. from firebase_admin import db # Get a database reference to our posts ref = db.reference('server/saving-data/fireblog/posts') # Read the data at the posts reference (this is a blocking operation) print(ref.get())
যান
// Create a database client from App. client, err := app.Database(ctx) if err != nil { log.Fatalln("Error initializing database client:", err) } // Get a database reference to our posts ref := client.NewRef("server/saving-data/fireblog/posts") // Read the data at the posts reference (this is a blocking operation) var post Post if err := ref.Get(ctx, &post); err != nil { log.Fatalln("Error reading value:", err) }
ডেটা কোয়েরি করা
ফায়ারবেস ডাটাবেস কোয়েরির মাধ্যমে, আপনি বিভিন্ন বিষয়ের উপর ভিত্তি করে নির্দিষ্টভাবে ডেটা পুনরুদ্ধার করতে পারেন। আপনার ডাটাবেসে একটি কোয়েরি তৈরি করতে, প্রথমে আপনাকে orderByChild() , orderByKey() , বা orderByValue() - এই অর্ডারিং ফাংশনগুলোর যেকোনো একটি ব্যবহার করে আপনার ডেটা কীভাবে সাজাতে চান তা নির্দিষ্ট করতে হবে। এরপর আপনি জটিল কোয়েরি চালানোর জন্য এই ফাংশনগুলোকে আরও পাঁচটি মেথডের সাথে একত্রিত করতে পারেন: limitToFirst() , limitToLast() , startAt() , endAt() , এবং equalTo() ।
যেহেতু Firebase-এ আমরা সবাই ডাইনোসরকে বেশ আকর্ষণীয় মনে করি, তাই আপনার Firebase ডেটাবেসে কীভাবে ডেটা কোয়েরি করতে পারেন তা দেখানোর জন্য আমরা ডাইনোসর সম্পর্কিত তথ্যের একটি নমুনা ডেটাবেস থেকে কোডের একটি অংশ ব্যবহার করব।
{
"lambeosaurus": {
"height" : 2.1,
"length" : 12.5,
"weight": 5000
},
"stegosaurus": {
"height" : 4,
"length" : 9,
"weight" : 2500
}
}আপনি তিনভাবে ডেটা সাজাতে পারেন: চাইল্ড কী (child key) , কী (key ) অথবা ভ্যালু ( value) অনুসারে। একটি সাধারণ ডাটাবেস কোয়েরি এই অর্ডারিং ফাংশনগুলোর একটি দিয়ে শুরু হয়, যার প্রত্যেকটি নিচে ব্যাখ্যা করা হলো।
একটি নির্দিষ্ট চাইল্ড কী দ্বারা সাজানো
আপনি orderByChild() ফাংশনে একটি সাধারণ চাইল্ড কী পাস করে নোডগুলোকে সেই কী-এর ভিত্তিতে সাজাতে পারেন। উদাহরণস্বরূপ, উচ্চতা অনুসারে সাজানো সমস্ত ডাইনোসর পড়ার জন্য, আপনি নিম্নলিখিত কাজটি করতে পারেন:
জাভা
public static class Dinosaur { public int height; public int weight; public Dinosaur(int height, int weight) { // ... } } final DatabaseReference dinosaursRef = database.getReference("dinosaurs"); dinosaursRef.orderByChild("height").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { Dinosaur dinosaur = dataSnapshot.getValue(Dinosaur.class); System.out.println(dataSnapshot.getKey() + " was " + dinosaur.height + " meters tall."); } // ... });
নোড.জেএস
const ref = db.ref('dinosaurs'); ref.orderByChild('height').on('child_added', (snapshot) => { console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall'); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').get() for key, val in snapshot.items(): print(f'{key} was {val} meters tall')
যান
// Dinosaur is a json-serializable type. type Dinosaur struct { Height int `json:"height"` Width int `json:"width"` } ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
যে কোনো নোডে যদি আমাদের কোয়েরি করা চাইল্ড কী না থাকে, তবে সেটিকে null ভ্যালু দিয়ে সর্ট করা হয়, যার অর্থ হলো এটি ক্রমের প্রথমে আসবে। ডেটা কীভাবে সাজানো হয় সে সম্পর্কে বিস্তারিত জানতে, “ডেটা কীভাবে সাজানো হয়” বিভাগটি দেখুন।
কোয়েরিগুলোকে শুধু এক স্তর নিচের চাইল্ডের পরিবর্তে, আরও গভীরে থাকা চাইল্ড দ্বারাও সাজানো যেতে পারে। এটি তখন কাজে আসে যখন আপনার কাছে এই ধরনের গভীরভাবে নেস্টেড ডেটা থাকে:
{
"lambeosaurus": {
"dimensions": {
"height" : 2.1,
"length" : 12.5,
"weight": 5000
}
},
"stegosaurus": {
"dimensions": {
"height" : 4,
"length" : 9,
"weight" : 2500
}
}
}এখন উচ্চতা জানতে, আপনি একটিমাত্র কী-এর পরিবর্তে অবজেক্টটির সম্পূর্ণ পাথ ব্যবহার করতে পারেন:
জাভা
dinosaursRef.orderByChild("dimensions/height").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { // ... } // ... });
নোড.জেএস
const ref = db.ref('dinosaurs'); ref.orderByChild('dimensions/height').on('child_added', (snapshot) => { console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall'); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('dimensions/height').get() for key, val in snapshot.items(): print(f'{key} was {val} meters tall')
যান
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("dimensions/height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
কোয়েরি একবারে শুধুমাত্র একটি কী দ্বারা অর্ডার করতে পারে। একই কোয়েরিতে একাধিকবার orderByChild() কল করলে একটি ত্রুটি দেখা দেয়।
চাবি দ্বারা ক্রমবিন্যাস
আপনি orderByKey() মেথড ব্যবহার করে নোডগুলোকে তাদের কী (key) অনুযায়ী সাজাতেও পারেন। নিচের উদাহরণটি সমস্ত ডাইনোসরকে বর্ণানুক্রমিকভাবে পড়ে:
জাভা
dinosaursRef.orderByKey().addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
নোড.জেএস
var ref = db.ref('dinosaurs'); ref.orderByKey().on('child_added', (snapshot) => { console.log(snapshot.key); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().get() print(snapshot)
যান
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } snapshot := make([]Dinosaur, len(results)) for i, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } snapshot[i] = d } fmt.Println(snapshot)
মান অনুসারে সাজানো
আপনি orderByValue() মেথড ব্যবহার করে নোডগুলোকে তাদের চাইল্ড কী-এর মান অনুসারে সাজাতে পারেন। ধরা যাক, ডাইনোসরদের মধ্যে একটি ডাইনো ক্রীড়া প্রতিযোগিতা চলছে এবং আপনি নিম্নলিখিত ফরম্যাটে তাদের স্কোরের হিসাব রাখছেন:
{
"scores": {
"bruhathkayosaurus" : 55,
"lambeosaurus" : 21,
"linhenykus" : 80,
"pterodactyl" : 93,
"stegosaurus" : 5,
"triceratops" : 22
}
}ডাইনোসরগুলোকে তাদের স্কোর অনুসারে সাজাতে, আপনি নিম্নলিখিত কোয়েরিটি তৈরি করতে পারেন:
জাভা
DatabaseReference scoresRef = database.getReference("scores"); scoresRef.orderByValue().addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue()); } // ... });
নোড.জেএস
const scoresRef = db.ref('scores'); scoresRef.orderByValue().on('value', (snapshot) => { snapshot.forEach((data) => { console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val()); }); });
পাইথন
ref = db.reference('scores') snapshot = ref.order_by_value().get() for key, val in snapshot.items(): print(f'The {key} dinosaur\'s score is {val}')
যান
ref := client.NewRef("scores") results, err := ref.OrderByValue().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
orderByValue() ব্যবহার করার সময় null , boolean, string, এবং object ভ্যালুগুলো কীভাবে সাজানো হয়, তার ব্যাখ্যার জন্য "ডেটা কীভাবে সাজানো হয়" বিভাগটি দেখুন।
জটিল কোয়েরি
এখন যেহেতু আপনার ডেটা কীভাবে সাজানো আছে তা স্পষ্ট, আপনি আরও জটিল কোয়েরি তৈরি করতে নিচে বর্ণিত limit বা range পদ্ধতিগুলো ব্যবহার করতে পারেন।
কোয়েরি সীমিত করুন
limitToFirst() এবং limitToLast() কোয়েরিগুলো একটি নির্দিষ্ট কলব্যাকের জন্য সিঙ্ক করার মতো চাইল্ড আইটেমের সর্বোচ্চ সংখ্যা নির্ধারণ করতে ব্যবহৃত হয়। আপনি যদি ১০০-এর একটি সীমা নির্ধারণ করেন, তাহলে প্রাথমিকভাবে আপনি সর্বোচ্চ ১০০টি child_added ইভেন্ট পাবেন। আপনার ডেটাবেসে যদি ১০০টির কম মেসেজ সংরক্ষিত থাকে, তাহলে প্রতিটি মেসেজের জন্য একটি করে child_added ইভেন্ট ফায়ার হবে। তবে, আপনার যদি ১০০টির বেশি মেসেজ থাকে, তাহলে আপনি শুধুমাত্র সেই ১০০টি মেসেজের জন্য একটি child_added ইভেন্ট পাবেন। আপনি যদি limitToFirst() ব্যবহার করেন, তাহলে এগুলো হলো প্রথম ১০০টি ক্রমানুসারে সাজানো মেসেজ, অথবা আপনি যদি limitToLast() ব্যবহার করেন, তাহলে এগুলো হলো শেষ ১০০টি ক্রমানুসারে সাজানো মেসেজ। আইটেমগুলো পরিবর্তিত হওয়ার সাথে সাথে, যে আইটেমগুলো কোয়েরিতে প্রবেশ করে সেগুলোর জন্য আপনি child_added ইভেন্ট এবং যে আইটেমগুলো কোয়েরি থেকে বেরিয়ে যায় সেগুলোর জন্য child_removed ইভেন্ট পাবেন, যাতে মোট সংখ্যা ১০০-তেই স্থির থাকে।
ডাইনোসর তথ্য ডেটাবেস এবং orderByChild() ব্যবহার করে, আপনি সবচেয়ে ভারী দুটি ডাইনোসর খুঁজে পেতে পারেন:
জাভা
dinosaursRef.orderByChild("weight").limitToLast(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
নোড.জেএস
const ref = db.ref('dinosaurs'); ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => { console.log(snapshot.key); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('weight').limit_to_last(2).get() for key in snapshot: print(key)
যান
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("weight").LimitToLast(2).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
child_added কলব্যাকটি ঠিক দুইবার ট্রিগার হয়, যদি না ডাটাবেসে দুইটির কম ডাইনোসর সংরক্ষিত থাকে। ডাটাবেসে যুক্ত হওয়া প্রতিটি নতুন, ভারী ডাইনোসরের জন্যও এটি ফায়ার হবে। পাইথনে, কোয়েরিটি সরাসরি একটি OrderedDict রিটার্ন করে, যাতে সবচেয়ে ভারী দুটি ডাইনোসর থাকে।
একইভাবে, আপনি limitToFirst() ব্যবহার করে দুটি সবচেয়ে ছোট ডাইনোসর খুঁজে পেতে পারেন:
জাভা
dinosaursRef.orderByChild("weight").limitToFirst(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
নোড.জেএস
const ref = db.ref('dinosaurs'); ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => { console.log(snapshot.key); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').limit_to_first(2).get() for key in snapshot: print(key)
যান
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").LimitToFirst(2).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
` child_added কলব্যাকটি ঠিক দুইবার ট্রিগার হয়, যদি না ডাটাবেসে দুইটির কম ডাইনোসর সংরক্ষিত থাকে। প্রথম দুটি ডাইনোসরের মধ্যে একটি ডাটাবেস থেকে সরিয়ে ফেলা হলেও এটি আবার ফায়ার হবে, কারণ তখন নতুন ডাইনোসরটি হবে দ্বিতীয় ক্ষুদ্রতম। পাইথনে, কোয়েরিটি সরাসরি ক্ষুদ্রতম ডাইনোসরগুলো সম্বলিত একটি OrderedDict রিটার্ন করে।
আপনি orderByValue() ব্যবহার করে লিমিট কোয়েরিও চালাতে পারেন। আপনি যদি ডাইনো স্পোর্টসের সর্বোচ্চ স্কোর করা শীর্ষ ৩টি ডাইনোসরকে নিয়ে একটি লিডারবোর্ড তৈরি করতে চান, তাহলে আপনি নিম্নলিখিত পদক্ষেপগুলো অনুসরণ করতে পারেন:
জাভা
scoresRef.orderByValue().limitToFirst(3).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue()); } // ... });
নোড.জেএস
const scoresRef = db.ref('scores'); scoresRef.orderByValue().limitToLast(3).on('value', (snapshot) =>{ snapshot.forEach((data) => { console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val()); }); });
পাইথন
scores_ref = db.reference('scores') snapshot = scores_ref.order_by_value().limit_to_last(3).get() for key, val in snapshot.items(): print(f'The {key} dinosaur\'s score is {val}')
যান
ref := client.NewRef("scores") results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
রেঞ্জ কোয়েরি
startAt() , endAt() , এবং equalTo() ব্যবহার করে আপনি আপনার কোয়েরিগুলোর জন্য ইচ্ছামতো শুরু এবং শেষ বিন্দু বেছে নিতে পারেন। উদাহরণস্বরূপ, যদি আপনি কমপক্ষে তিন মিটার লম্বা সমস্ত ডাইনোসর খুঁজে বের করতে চান, তাহলে আপনি orderByChild() এবং startAt() একসাথে ব্যবহার করতে পারেন:
জাভা
dinosaursRef.orderByChild("height").startAt(3).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
নোড.জেএস
const ref = db.ref('dinosaurs'); ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => { console.log(snapshot.key); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').start_at(3).get() for key in snapshot: print(key)
যান
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").StartAt(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
আপনি endAt() ব্যবহার করে সেই সমস্ত ডাইনোসরকে খুঁজে বের করতে পারেন যাদের নাম আভিধানিকভাবে Pterodactyl-এর আগে আসে:
জাভা
dinosaursRef.orderByKey().endAt("pterodactyl").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
নোড.জেএস
const ref = db.ref('dinosaurs'); ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => { console.log(snapshot.key); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().end_at('pterodactyl').get() for key in snapshot: print(key)
যান
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().EndAt("pterodactyl").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
আপনার কোয়েরির উভয় প্রান্ত সীমিত করতে আপনি startAt() এবং endAt() একসাথে ব্যবহার করতে পারেন। নিচের উদাহরণটি সেই সমস্ত ডাইনোসর খুঁজে বের করে যাদের নাম "b" অক্ষর দিয়ে শুরু হয়:
জাভা
dinosaursRef.orderByKey().startAt("b").endAt("b\uf8ff").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
নোড.জেএস
var ref = db.ref('dinosaurs'); ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => { console.log(snapshot.key); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().start_at('b').end_at('b\uf8ff').get() for key in snapshot: print(key)
যান
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().StartAt("b").EndAt("b\uf8ff").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
` equalTo() ` মেথডটি আপনাকে হুবহু মিলের ভিত্তিতে ফিল্টার করার সুযোগ দেয়। অন্যান্য রেঞ্জ কোয়েরির মতোই, এটি প্রতিটি মিলে যাওয়া চাইল্ড নোডের জন্য কার্যকর হবে। উদাহরণস্বরূপ, ২৫ মিটার লম্বা সমস্ত ডাইনোসর খুঁজে বের করতে আপনি নিম্নলিখিত কোয়েরিটি ব্যবহার করতে পারেন:
জাভা
dinosaursRef.orderByChild("height").equalTo(25).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
নোড.জেএস
const ref = db.ref('dinosaurs'); ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => { console.log(snapshot.key); });
পাইথন
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').equal_to(25).get() for key in snapshot: print(key)
যান
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").EqualTo(25).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
আপনার ডেটাকে পেজিনেট করার প্রয়োজন হলেও রেঞ্জ কোয়েরি কার্যকর।
সবকিছু একসাথে মিলিয়ে
আপনি এই সমস্ত কৌশল একত্রিত করে জটিল কোয়েরি তৈরি করতে পারেন। উদাহরণস্বরূপ, আপনি স্টেগোসরাসের চেয়ে সামান্য ছোট ডাইনোসরটির নাম খুঁজে বের করতে পারেন:
জাভা
dinosaursRef.child("stegosaurus").child("height").addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot stegoHeightSnapshot) { Integer favoriteDinoHeight = stegoHeightSnapshot.getValue(Integer.class); Query query = dinosaursRef.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2); query.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Data is ordered by increasing height, so we want the first entry DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next(); System.out.println("The dinosaur just shorter than the stegosaurus is: " + firstChild.getKey()); } @Override public void onCancelled(DatabaseError databaseError) { // ... } }); } @Override public void onCancelled(DatabaseError databaseError) { // ... } });
নোড.জেএস
const ref = db.ref('dinosaurs'); ref.child('stegosaurus').child('height').on('value', (stegosaurusHeightSnapshot) => { const favoriteDinoHeight = stegosaurusHeightSnapshot.val(); const queryRef = ref.orderByChild('height').endAt(favoriteDinoHeight).limitToLast(2); queryRef.on('value', (querySnapshot) => { if (querySnapshot.numChildren() === 2) { // Data is ordered by increasing height, so we want the first entry querySnapshot.forEach((dinoSnapshot) => { console.log('The dinosaur just shorter than the stegasaurus is ' + dinoSnapshot.key); // Returning true means that we will only loop through the forEach() one time return true; }); } else { console.log('The stegosaurus is the shortest dino'); } }); });
পাইথন
ref = db.reference('dinosaurs') favotire_dino_height = ref.child('stegosaurus').child('height').get() query = ref.order_by_child('height').end_at(favotire_dino_height).limit_to_last(2) snapshot = query.get() if len(snapshot) == 2: # Data is ordered by increasing height, so we want the first entry. # Second entry is stegosarus. for key in snapshot: print(f'The dinosaur just shorter than the stegosaurus is {key}') return else: print('The stegosaurus is the shortest dino')
যান
ref := client.NewRef("dinosaurs") var favDinoHeight int if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil { log.Fatalln("Error querying database:", err) } query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2) results, err := query.GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } if len(results) == 2 { // Data is ordered by increasing height, so we want the first entry. // Second entry is stegosarus. fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key()) } else { fmt.Println("The stegosaurus is the shortest dino") }
ডেটা কীভাবে সাজানো হয়
এই অংশে ব্যাখ্যা করা হয়েছে যে চারটি অর্ডারিং ফাংশনের প্রত্যেকটি ব্যবহার করার সময় আপনার ডেটা কীভাবে সাজানো হয়।
শিশু অনুসারে সাজানো
orderByChild() ব্যবহার করার সময়, নির্দিষ্ট চাইল্ড কী ধারণকারী ডেটা নিম্নলিখিতভাবে সাজানো হয়:
- নির্দিষ্ট চাইল্ড কী-এর মান
nullহলে চাইল্ডগুলো প্রথমে আসে। - নির্দিষ্ট চাইল্ড কী-এর জন্য যেসব চাইল্ডের মান
false, সেগুলো এরপরে আসে। যদি একাধিক চাইল্ডের মানfalseহয়, তবে সেগুলোকে কী অনুসারে আভিধানিকভাবে সাজানো হয়। - নির্দিষ্ট চাইল্ড কী-এর মান '
trueহলে চাইল্ডগুলো এরপরে আসে। যদি একাধিক চাইল্ডের মান 'trueহয়, তবে সেগুলোকে কী অনুসারে আভিধানিকভাবে সাজানো হয়। - সাংখ্যিক মানযুক্ত চাইল্ডগুলো এরপর আরোহী ক্রমে আসে। যদি নির্দিষ্ট চাইল্ড নোডটির জন্য একাধিক চাইল্ডের একই সাংখ্যিক মান থাকে, তবে সেগুলোকে কী (key) অনুসারে সাজানো হয়।
- সংখ্যার পরে স্ট্রিংগুলো আসে এবং আভিধানিকভাবে আরোহী ক্রমে সাজানো হয়। যদি নির্দিষ্ট চাইল্ড নোডের জন্য একাধিক চাইল্ডের একই মান থাকে, তবে সেগুলোকে কী (key) অনুসারে আভিধানিকভাবে সাজানো হয়।
- বস্তুগুলো শেষে আসে এবং চাবি অনুসারে আভিধানিকভাবে আরোহী ক্রমে সাজানো হয়।
কী অনুসারে অর্ডার করুন
orderByKey() ব্যবহার করে আপনার ডেটা সর্ট করার সময়, ডেটা কী (key) অনুসারে নিম্নলিখিতভাবে আরোহী ক্রমে ফেরত দেওয়া হয়। মনে রাখবেন যে কী (key) শুধুমাত্র স্ট্রিং হতে পারে।
- যেসব চাইল্ডের কী-কে ৩২-বিট পূর্ণসংখ্যা হিসেবে পার্স করা যায়, সেগুলো আরোহী ক্রমে প্রথমে আসে।
- যেসব চাইল্ডের কী (key) হিসেবে স্ট্রিং ভ্যালু রয়েছে, সেগুলো আভিধানিকভাবে আরোহী ক্রমে সাজানো অবস্থায় এরপরে আসে।
মান অনুসারে সাজানো
orderByValue() ব্যবহার করার সময়, চাইল্ড নোডগুলো তাদের ভ্যালু অনুসারে সাজানো হয়। সাজানোর মানদণ্ড orderByChild() এর মতোই, তবে এক্ষেত্রে একটি নির্দিষ্ট চাইল্ড কী-এর ভ্যালুর পরিবর্তে নোডটির ভ্যালু ব্যবহৃত হয়।