ডেটা ফিরিয়ে আনা হচ্ছে

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

  1. অ্যাসিঙ্ক্রোনাস লিসেনার: Firebase Realtime Database-এ স্টোর করা ডেটা, ডেটাবেস রেফারেন্সে অ্যাসিঙ্ক্রোনাস লিসেনার অ্যাটাচ করার মাধ্যমে উদ্ধার করা হয়। ডেটার প্রাথমিক অবস্থার জন্য একবার এবং ডেটা পরিবর্তন হলে আবার যেকোনও সময় লিসেনার ট্রিগার করা হয়। ইভেন্ট লিসেনার বিভিন্ন ধরনের ইভেন্ট পেতে পারে। ডেটা ফিরিয়ে আনার এই মোড Java, Node.js এবং Python Admin SDK-তে কাজ করে।
  2. ব্লকিং রিড: Firebase Realtime Database-এ স্টোর করা ডেটা, ডেটাবেস রেফারেন্সে ব্লকিং পদ্ধতি প্রয়োগ করে উদ্ধার করা হয়, যা রেফারেন্সে স্টোর করা ডেটা ফিরিয়ে দেয়। প্রতিটি মেথড কল হল এককালীন অপারেশন। এর অর্থ হল, SDK এমন কোনও কলব্যাক রেজিস্টার করে না যা পরবর্তী ডেটা আপডেট শোনে। ডেটা ফিরিয়ে আনার এই মডেল Python এবং Go Admin 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());
  }
});
Node.js
// 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);
}); 
Python
# 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())
Go
// 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-এর ক্ষেত্রে, আপনার ডেটাবেস রেফারেন্সে নতুন ডেটা যোগ করা হলে যেকোনও সময় লিসেনার ফাংশন কল করা হয় এবং এটি করার জন্য আপনাকে কোনও অতিরিক্ত কোড লিখতে হবে না।

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

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

Java এবং 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) {}
});
Node.js
// 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) {}
});
Node.js
// 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) {}
});
Node.js
// 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 ইভেন্টটি অর্ডার করা ডেটা নিয়ে কাজ করার সময় ব্যবহার করা হয়, যা পরবর্তী বিভাগে কভার করা হয়েছে।

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

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

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

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

জাভা
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) {}
});
Node.js
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);
Node.js
ref.off('value', originalCallback);

আপনি on()-এ স্কোপ কনটেক্সট পাস করলে, কলব্যাক ডিটাচ করার সময় এটি অবশ্যই পাস করতে হবে:

জাভা
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);

আপনি কোনও লোকেশনে সব কলব্যাক সরাতে চাইলে, নিম্নলিখিত কাজগুলি করতে পারেন:

জাভা
// No Java equivalent, listeners must be removed individually.
Node.js
// 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) {
    // ...
  }
});
Node.js
ref.once('value', (data) => {
  // do some stuff once
});
Python
# 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())
Go
// 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()।

Firebase-এ আমরা সবাই মনে করি যে ডাইনোসররা বেশ দারুণ, তাই আপনার Firebase ডেটাবেসে কীভাবে কোয়েরি করতে পারেন তা দেখানোর জন্য আমরা ডাইনোসর সম্পর্কিত তথ্যের একটি স্যাম্পেল ডেটাবেস থেকে একটি স্নিপেট ব্যবহার করব।:

{
  "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.");
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');

ref.orderByChild('height').on('child_added', (snapshot) => {
  console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall');
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').get()
for key, val in snapshot.items():
    print(f'{key} was {val} meters tall')
Go
// 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) {
    // ...
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('dimensions/height').on('child_added', (snapshot) => {
  console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall');
});
Python
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')
Go
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());
  }

  // ...
});
Node.js
var ref = db.ref('dinosaurs');
ref.orderByKey().on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().get()
print(snapshot)
Go
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());
  }

  // ...
});
Node.js
const scoresRef = db.ref('scores');
scoresRef.orderByValue().on('value', (snapshot) => {
  snapshot.forEach((data) => {
    console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val());
  });
});
Python
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}')
Go
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, বুলিয়ান, স্ট্রিং ও অবজেক্ট ভ্যালু কীভাবে সাজানো হয় তার ব্যাখ্যা পেতে ডেটা কীভাবে সাজানো হয় বিভাগ দেখুন।

জটিল ক্যোয়ারীগুলি

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

কোয়েরি সীমিত করুন

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());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
Go
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 কলব্যাক ঠিক দুবার ট্রিগার করা হয়, যদি না ডেটাবেসে দুটির কম ডাইনোসর স্টোর করা থাকে। এছাড়াও, ডেটাবেসে যোগ করা প্রতিটি নতুন, ভারী ডাইনোসরের জন্য এটি ট্রিগার করা হবে। Python-এ, কোয়েরি সরাসরি একটি OrderedDict প্রদান করে যাতে দুটি সবচেয়ে ভারী ডাইনোসর থাকে।

একইভাবে, আপনি limitToFirst() ব্যবহার করে সবচেয়ে ছোট দুটি ডাইনোসর খুঁজে পেতে পারেন:

জাভা
dinosaursRef.orderByChild("weight").limitToFirst(2).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
Go
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 কলব্যাক ঠিক দুবার ট্রিগার করা হয়। প্রথম দুটি ডাইনোসরের মধ্যে একটিকে ডেটাবেস থেকে সরিয়ে দিলে, এটি আবার ট্রিগার হবে, কারণ এখন একটি নতুন ডাইনোসর দ্বিতীয় সবচেয়ে ছোট ডাইনোসর হবে। Python-এ, কোয়েরি সরাসরি একটি 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());
  }

  // ...
});
Node.js
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());
  });
});
Python
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}')
Go
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());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').start_at(3).get()
for key in snapshot:
    print(key)
Go
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());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().end_at('pterodactyl').get()
for key in snapshot:
    print(key)
Go
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());
  }

  // ...
});
Node.js
var ref = db.ref('dinosaurs');
ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().start_at('b').end_at('b\uf8ff').get()
for key in snapshot:
    print(key)
Go
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());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').equal_to(25).get()
for key in snapshot:
    print(key)
Go
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) {
    // ...
  }
});
Node.js
  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');
      }
    });
});
Python
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')
Go
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

orderByChild() ব্যবহার করার সময়, নির্দিষ্ট চাইল্ড কী সহ ডেটা নিম্নলিখিত ক্রমে সাজানো হয়:

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

orderByKey

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

  1. যেসব চিলড্রেনকে ৩২-বিট ইন্টিজার হিসেবে পার্স করা যায়, সেগুলি প্রথমে আসে এবং ছোট থেকে বড় হিসেবে সাজানো হয়।
  2. এর পরে স্ট্রিং ভ্যালু কী হিসেবে আছে এমন চাইল্ড আসে, যা লেক্সিকোগ্রাফিক্যালি ছোট থেকে বড় হিসেবে সাজানো থাকে।

orderByValue

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