তথ্য উদ্ধার করা হচ্ছে

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

  1. অ্যাসিঙ্ক্রোনাস লিসেনার্স: Firebase Realtime Database সংরক্ষিত ডেটা একটি অ্যাসিঙ্ক্রোনাস লিসেনার্সকে একটি ডাটাবেস রেফারেন্সের সাথে সংযুক্ত করে পুনরুদ্ধার করা হয়। লিসেনার্স ডেটার প্রাথমিক অবস্থার জন্য একবার ট্রিগার করা হয় এবং ডেটা পরিবর্তনের সময় আবারও সক্রিয় করা হয়। একটি ইভেন্ট লিসেনার্স বিভিন্ন ধরণের ইভেন্ট পেতে পারে। ডেটা পুনরুদ্ধারের এই মোডটি জাভা, নোড.জেএস এবং পাইথন অ্যাডমিন এসডিকে সমর্থিত।
  2. ব্লকিং পড়া যায়: Firebase Realtime Database সংরক্ষিত ডেটা ডাটাবেস রেফারেন্সে ব্লকিং পদ্ধতি ব্যবহার করে পুনরুদ্ধার করা হয়, যা রেফারেন্সে সংরক্ষিত ডেটা ফেরত দেয়। প্রতিটি মেথড কল একটি এককালীন অপারেশন। এর অর্থ হল SDK পরবর্তী ডেটা আপডেট শোনার জন্য কোনও কলব্যাক নিবন্ধন করে না। ডেটা পুনরুদ্ধারের এই মডেলটি পাইথন এবং গো অ্যাডমিন SDK তে সমর্থিত।

শুরু করা

Firebase ডাটাবেস থেকে ডেটা কীভাবে পড়তে হয় তা বোঝার জন্য আগের প্রবন্ধের ব্লগিং উদাহরণটি আবার দেখা যাক। মনে রাখবেন যে উদাহরণ অ্যাপের ব্লগ পোস্টগুলি ডাটাবেস URL 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 এর ক্ষেত্রে, আপনার ডাটাবেস রেফারেন্সে নতুন ডেটা যোগ করার সময় লিসেনার ফাংশনটি কল করা হয় এবং এটি করার জন্য আপনাকে কোনও অতিরিক্ত কোড লিখতে হবে না।

জাভা এবং Node.js-এ, কলব্যাক ফাংশন একটি DataSnapshot গ্রহণ করে, যা ডেটার একটি স্ন্যাপশট। একটি স্ন্যাপশট হল একটি নির্দিষ্ট ডাটাবেস রেফারেন্সে একটি নির্দিষ্ট সময়ে ডেটার একটি ছবি। একটি স্ন্যাপশটে val() / getValue() কল করলে ডেটার একটি ভাষা-নির্দিষ্ট বস্তুর প্রতিনিধিত্ব ফেরত আসে। যদি রেফারেন্সের অবস্থানে কোনও ডেটা না থাকে, তাহলে স্ন্যাপশটের মান null হয়। Python-এ get() পদ্ধতি সরাসরি ডেটার একটি Python প্রতিনিধিত্ব ফেরত দেয়। Go-তে Get() ফাংশন ডেটাটিকে একটি নির্দিষ্ট ডেটা কাঠামোতে আনমার্শাল করে।

লক্ষ্য করুন যে উপরের উদাহরণে আমরা value ইভেন্ট টাইপ ব্যবহার করেছি, যা ফায়ারবেস ডাটাবেস রেফারেন্সের সম্পূর্ণ বিষয়বস্তু পড়ে, এমনকি যদি শুধুমাত্র একটি ডেটা পরিবর্তিত হয়। value হল নীচে তালিকাভুক্ত পাঁচটি ভিন্ন ইভেন্ট টাইপের মধ্যে একটি যা আপনি ডাটাবেস থেকে ডেটা পড়ার জন্য ব্যবহার করতে পারেন।

জাভা এবং Node.js-এ ইভেন্টের ধরণগুলি পড়ুন

মূল্য

একটি নির্দিষ্ট ডাটাবেস পাথে থাকা কন্টেন্টের স্ট্যাটিক স্ন্যাপশট পড়ার জন্য value ইভেন্ট ব্যবহার করা হয়, যেমনটি পঠিত ইভেন্টের সময় বিদ্যমান ছিল। এটি প্রাথমিক ডেটা দিয়ে একবার ট্রিগার করা হয় এবং প্রতিবার ডেটা পরিবর্তনের সময় আবার ট্রিগার করা হয়। ইভেন্ট কলব্যাকটি সেই অবস্থানের সমস্ত ডেটা ধারণকারী একটি স্ন্যাপশট পাস করে, যার মধ্যে চাইল্ড ডেটাও অন্তর্ভুক্ত থাকে। উপরের কোড উদাহরণে, value আপনার অ্যাপের সমস্ত ব্লগ পোস্ট ফেরত দিয়েছে। প্রতিবার একটি নতুন ব্লগ পোস্ট যোগ করার সময়, কলব্যাক ফাংশন সমস্ত পোস্ট ফেরত দেবে।

শিশু যোগ করা হয়েছে

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

আপনার ব্লগিং অ্যাপে যোগ করা প্রতিটি নতুন পোস্টের ডেটা পুনরুদ্ধার করতে চাইলে, আপনি 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 কল করে পোস্টের লেখক এবং শিরোনাম বৈশিষ্ট্যগুলিতে অ্যাক্সেস পাবেন। দ্বিতীয় 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 ইভেন্টটি ব্যবহার করা হয়, যা পরবর্তী বিভাগে আলোচনা করা হয়েছে।

ইভেন্ট গ্যারান্টি

ফায়ারবেস ডাটাবেস ইভেন্ট সম্পর্কিত বেশ কয়েকটি গুরুত্বপূর্ণ গ্যারান্টি দেয়:

ডাটাবেস ইভেন্ট গ্যারান্টি
স্থানীয় রাজ্য পরিবর্তন হলে ইভেন্টগুলি সর্বদা ট্রিগার হবে।
ইভেন্টগুলি সর্বদা অবশেষে ডেটার সঠিক অবস্থা প্রতিফলিত করবে, এমনকি এমন ক্ষেত্রেও যেখানে স্থানীয় ক্রিয়াকলাপ বা সময় অস্থায়ী পার্থক্য সৃষ্টি করে, যেমন নেটওয়ার্ক সংযোগের অস্থায়ী ক্ষতি।
একটি একক ক্লায়েন্ট থেকে লেখা সবসময় সার্ভারে লেখা হবে এবং ক্রমানুসারে অন্যান্য ব্যবহারকারীদের কাছে সম্প্রচারিত হবে।
ভ্যালু ইভেন্টগুলি সর্বদা শেষবারের মতো ট্রিগার করা হয় এবং স্ন্যাপশট নেওয়ার আগে ঘটে যাওয়া অন্য যেকোনো ইভেন্টের আপডেট থাকার নিশ্চয়তা থাকে।

যেহেতু value ইভেন্টগুলি সর্বদা শেষ ট্রিগার হয়, তাই নিম্নলিখিত উদাহরণটি সর্বদা কাজ করবে:

জাভা
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)
}

তথ্য অনুসন্ধান করা হচ্ছে

Firebase ডাটাবেস কোয়েরি ব্যবহার করে, আপনি বিভিন্ন কারণের উপর ভিত্তি করে বেছে বেছে ডেটা পুনরুদ্ধার করতে পারেন। আপনার ডাটাবেসে একটি কোয়েরি তৈরি করতে, আপনাকে orderByChild() , orderByKey() , অথবা orderByValue() ক্রমানুসার ফাংশন ব্যবহার করে আপনার ডেটা কীভাবে অর্ডার করতে চান তা নির্দিষ্ট করে শুরু করতে হবে। এরপর আপনি জটিল কোয়েরি পরিচালনা করার জন্য এগুলিকে আরও পাঁচটি পদ্ধতির সাথে একত্রিত করতে পারেন: limitToFirst() , limitToLast() , startAt() , endAt() , এবং equalTo()

যেহেতু ফায়ারবেসে আমরা সকলেই ডাইনোসরদের বেশ ভালো মনে করি, তাই আমরা ডাইনোসরের তথ্যের একটি নমুনা ডাটাবেস থেকে একটি স্নিপেট ব্যবহার করব যাতে আপনি আপনার ফায়ারবেস ডাটাবেসে ডেটা কীভাবে অনুসন্ধান করতে পারেন তা প্রদর্শন করতে পারেন।:

{
  "lambeosaurus": {
    "height" : 2.1,
    "length" : 12.5,
    "weight": 5000
  },
  "stegosaurus": {
    "height" : 4,
    "length" : 9,
    "weight" : 2500
  }
}

আপনি তিনটি উপায়ে ডেটা অর্ডার করতে পারেন: চাইল্ড কী দ্বারা, কী দ্বারা, অথবা মান দ্বারা। একটি মৌলিক ডাটাবেস কোয়েরি এই অর্ডারিং ফাংশনগুলির একটি দিয়ে শুরু হয়, যার প্রতিটি নীচে ব্যাখ্যা করা হয়েছে।

একটি নির্দিষ্ট চাইল্ড কী দ্বারা ক্রমানুসারে

আপনি একটি সাধারণ চাইল্ড কী দিয়ে নোডগুলি অর্ডার করতে পারেন, সেই কীটি 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 মান দিয়ে সাজানো হয়, অর্থাৎ এটি ক্রমানুসারে প্রথমে আসবে। ডেটা কীভাবে অর্ডার করা হয় সে সম্পর্কে বিস্তারিত জানার জন্য, How Data is Ordered বিভাগটি দেখুন।

শুধুমাত্র এক স্তরের নিচের শিশুদের পরিবর্তে, গভীরভাবে নেস্টেড শিশুদের দ্বারাও কোয়েরিগুলি অর্ডার করা যেতে পারে। যদি আপনার এই ধরণের গভীরভাবে নেস্টেড ডেটা থাকে তবে এটি কার্যকর:

{
  "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() পদ্ধতি ব্যবহার করে নোডগুলিকে তাদের কী অনুসারেও সাজাতে পারেন। নিম্নলিখিত উদাহরণে সমস্ত ডাইনোসরকে বর্ণানুক্রমিক ক্রমে পড়া হয়েছে:

জাভা
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 মানগুলি কীভাবে সাজানো হয় তার ব্যাখ্যার জন্য "How Data is Ordered" বিভাগটি দেখুন।

জটিল প্রশ্ন

এখন যেহেতু আপনার ডেটা কীভাবে সাজানো হয়েছে তা স্পষ্ট, আপনি আরও জটিল প্রশ্ন তৈরি করতে নীচে বর্ণিত সীমা বা পরিসর পদ্ধতিগুলি ব্যবহার করতে পারেন।

সীমাবদ্ধ প্রশ্ন

limitToFirst() এবং limitToLast() কোয়েরিগুলি একটি নির্দিষ্ট কলব্যাকের জন্য সর্বাধিক সংখ্যক শিশু সিঙ্ক করার জন্য সেট করতে ব্যবহৃত হয়। আপনি যদি 100 এর সীমা নির্ধারণ করেন, তাহলে আপনি প্রাথমিকভাবে কেবলমাত্র 100 টি child_added ইভেন্ট পাবেন। যদি আপনার ডাটাবেসে 100 টিরও কম বার্তা সংরক্ষিত থাকে, তাহলে প্রতিটি বার্তার জন্য একটি child_added ইভেন্ট চালু হবে। তবে, যদি আপনার 100 টিরও বেশি বার্তা থাকে, তাহলে আপনি কেবলমাত্র 100 টি বার্তার জন্য একটি child_added ইভেন্ট পাবেন। যদি আপনি limitToFirst() ব্যবহার করেন তবে এগুলি হল প্রথম 100 টি অর্ডার করা বার্তা অথবা যদি আপনি limitToLast() ব্যবহার করেন তবে শেষ 100 টি অর্ডার করা বার্তা। আইটেম পরিবর্তনের সাথে সাথে, আপনি কোয়েরিতে প্রবেশ করা আইটেমগুলির জন্য child_added ইভেন্ট এবং এটি ছেড়ে যাওয়া আইটেমগুলির জন্য child_removed ইভেন্ট পাবেন, যাতে মোট সংখ্যা 100 এ থাকে।

ডাইনোসর তথ্য ডাটাবেস এবং 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() দিয়েও সীমা অনুসন্ধান পরিচালনা করতে পারেন। যদি আপনি শীর্ষ 3 সর্বোচ্চ স্কোরিং ডাইনো স্পোর্টস ডাইনোসরের একটি লিডারবোর্ড তৈরি করতে চান, তাহলে আপনি নিম্নলিখিতগুলি করতে পারেন:

জাভা
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() পদ্ধতি আপনাকে সঠিক মিলের উপর ভিত্তি করে ফিল্টার করার সুযোগ দেয়। অন্যান্য রেঞ্জ কোয়েরির মতো, এটি প্রতিটি মিলিত চাইল্ড নোডের জন্য কার্যকর হবে। উদাহরণস্বরূপ, আপনি 25 মিটার লম্বা সমস্ত ডাইনোসর খুঁজে পেতে নিম্নলিখিত কোয়েরি ব্যবহার করতে পারেন:

জাভা
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() ব্যবহার করার সময়, নির্দিষ্ট চাইল্ড কী ধারণকারী ডেটা নিম্নরূপ সাজানো হয়:

  1. নির্দিষ্ট শিশু কী-এর জন্য null মান সহ শিশুরা প্রথমে আসে।
  2. নির্দিষ্ট শিশু কী-এর জন্য false মান সহ শিশুদের পরে আসে। যদি একাধিক শিশুদের false মান থাকে, তবে তাদের কী অনুসারে অভিধানিকভাবে সাজানো হয়।
  3. নির্দিষ্ট child key-এর জন্য true মান সম্পন্ন শিশুদের পরে আসে। যদি একাধিক শিশুদের true মান থাকে, তাহলে তাদের অভিধানিকভাবে key অনুসারে সাজানো হয়।
  4. সংখ্যাসূচক মান সম্পন্ন শিশুরা পরবর্তীতে আসে, ঊর্ধ্বক্রমানুসারে সাজানো। যদি একাধিক শিশু নির্দিষ্ট শিশু নোডের জন্য একই সংখ্যাসূচক মান ধারণ করে, তাহলে তাদের কী অনুসারে সাজানো হয়।
  5. স্ট্রিংগুলি সংখ্যার পরে আসে এবং লেক্সিকোগ্রাফিকভাবে ঊর্ধ্বক্রমানুসারে সাজানো হয়। যদি একাধিক চাইল্ড নোডের জন্য একই মান থাকে, তাহলে সেগুলিকে কী দ্বারা লেক্সিকোগ্রাফিকভাবে সাজানো হয়।
  6. বস্তুগুলি শেষে আসে, এবং শব্দকোষ অনুসারে ঊর্ধ্বক্রমানুসারে কী অনুসারে সাজানো হয়।

অর্ডারবাইকি

যখন orderByKey() ব্যবহার করে আপনার ডেটা সাজানো হয়, তখন কী অনুসারে ডেটা ঊর্ধ্বমুখী ক্রমে ফেরত পাঠানো হয়, যেমনটি নিম্নরূপ। মনে রাখবেন যে কীগুলি কেবল স্ট্রিং হতে পারে।

  1. ৩২-বিট পূর্ণসংখ্যা হিসেবে পার্স করা যায় এমন কী সহ শিশুদের প্রথমে আসে, আরোহী ক্রমে সাজানো হয়।
  2. যেসব শিশুদের কী হিসেবে স্ট্রিং মান থাকে, তারা পরবর্তীতে আসে, লেক্সিকোগ্রাফিকভাবে ঊর্ধ্বক্রমানুসারে সাজানো হয়।

অর্ডারবাইভ্যালু

orderByValue() ব্যবহার করার সময়, শিশুদের তাদের মান অনুসারে সাজানো হয়। অর্ডারিং মানদণ্ড orderByChild() এর মতোই, তবে নির্দিষ্ট চাইল্ড কী-এর মানের পরিবর্তে নোডের মান ব্যবহার করা হয়।