درحال بازیابی داده‌ها

این سند اصول بازیابی داده‌های پایگاه داده، نحوه مرتب‌سازی داده‌ها، و نحوه انجام پُرسمان‌های ساده روی داده‌ها را پوشش می‌دهد. بازیابی داده‌ها در Admin SDK در زبان‌های برنامه‌نویسی مختلف کمی متفاوت پیاده‌سازی شده است.

  1. شنوندگان ناهمگام: داده‌های ذخیره‌شده در Firebase Realtime Database با پیوست کردن شنونده ناهمگام به مرجع پایگاه داده بازیابی می‌شود. شنونده یک‌بار برای وضعیت اولیه داده‌ها و دوباره هر زمان که داده‌ها تغییر کنند راه‌اندازی می‌شود. شنونده رویداد ممکن است چندین نوع رویداد مختلف دریافت کند. این حالت بازیابی داده در Java، Node.js، و Python Admin SDKs پشتیبانی می‌شود.
  2. مسدودسازی خواندن: داده‌های ذخیره‌شده در Firebase Realtime Database با فراخوانی روش مسدودسازی در مرجع پایگاه داده بازیابی می‌شود، که داده‌های ذخیره‌شده در مرجع را برمی‌گرداند. هر فراخوانی روش یک عملیات یک‌باره است. این یعنی کیت توسعه نرم‌افزار هیچ فراخوانی برگشتی‌ای را که به به‌روزرسانی‌های بعدی داده گوش دهد ثبت نمی‌کند. این مدل بازیابی داده در Python و Go Admin SDK پشتیبانی می‌شود.

شروع به کار

بیایید مثال وبلاگ‌نویسی از مقاله قبلی را دوباره بررسی کنیم تا بفهمیم چگونه داده‌ها را از پایگاه داده Firebase بخوانیم. به‌یاد داشته باشید که پست‌های وبلاگ در برنامه نمونه در نشانی وب پایگاه داده 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);
}); 
پایتون
# 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، هرزمان داده جدیدی به مرجع پایگاه داده شما اضافه شود، تابع شنونده فراخوانده می‌شود و برای انجام این کار نیازی به نوشتن کد اضافی ندارید.

در Java و Node.js، تابع بازخوانی DataSnapshot را دریافت می‌کند که عکس آنی داده‌ها است. عکس آنی تصویری از داده‌ها در یک مرجع پایگاه داده خاص در یک نقطه زمانی واحد است. فراخوانی val() / getValue() در عکس آنی، نمایش شیء داده‌ها را به‌صورت مختص زبان برمی‌گرداند. اگر هیچ داده‌ای در مکان مرجع وجود نداشته باشد، مقدار عکس آنی null است. روش get() در Python مستقیماً یک نمایش Python از داده‌ها را برمی‌گرداند. تابع Get() در Go داده‌ها را به ساختار داده مشخصی تبدیل می‌کند.

توجه داشته باشید که در مثال بالا از نوع رویداد 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);
});

در این مثال، عکس آنی حاوی شیئی با پست وبلاگ تکی خواهد بود. چون کیت توسعه نرم‌افزار با بازیابی مقدار، پست‌ها را به شیء تبدیل می‌کند، با فراخوانی 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() ارسال کرده‌اید، باید هنگام جدا کردن callback ارسال شود:

جاوا
// 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
});
پایتون
# 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().

ازآنجایی‌که همه ما در 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');
});
پایتون
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) {
    // ...
  }

  // ...
});
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');
});
پایتون
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());
  }

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

  // ...
});
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());
  });
});
پایتون
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)
}

برای توضیح درباره نحوه مرتب‌سازی مقادیر null، بولی، رشته، و شیء هنگام استفاده از orderByValue()، به بخش نحوه مرتب‌سازی داده‌ها مراجعه کنید.

درخواست‌های پیچیده

اکنون که مشخص شد داده‌هایتان چگونه مرتب می‌شوند، می‌توانید از روش‌های 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());
  }

  // ...
});
Node.js
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 دقیقاً دو بار راه‌اندازی می‌شود، مگراینکه کمتر از دو دایناسور در پایگاه داده ذخیره شده باشد. همچنین برای هر دایناسور جدید و سنگین‌تری که به پایگاه داده اضافه می‌شود، فعال خواهد شد. در 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);
});
پایتون
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 دقیقاً دو بار راه‌اندازی می‌شود، مگراینکه کمتر از دو دایناسور در پایگاه داده ذخیره شده باشد. همچنین اگر یکی از دو دایناسور اول از پایگاه داده برداشته شود، دوباره فعال می‌شود، زیرا دایناسور جدیدی اکنون دومین دایناسور کوتاه خواهد بود. در 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());
  });
});
پایتون
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());
  }

  // ...
});
Node.js
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() برای پیدا کردن همه دایناسورهایی که نامشان ازنظر واژه‌نامه‌ای قبل‌از «پتروداکتیل» می‌آید استفاده کنید:

جاوا
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);
});
پایتون
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());
  }

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

  // ...
});
Node.js
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) {
    // ...
  }
});
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');
      }
    });
});
پایتون
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

هنگام استفاده از orderByChild()، داده‌هایی که حاوی کلید فرزند مشخص‌شده هستند به‌صورت زیر مرتب می‌شوند:

  1. فرزندان با مقدار null برای کلید فرزند مشخص‌شده در ابتدا می‌آیند.
  2. فرزندان با مقدار false برای کلید فرزند مشخص‌شده در ردیف بعدی قرار می‌گیرند. اگر چند فرزند مقدار false داشته باشند، براساس کلید به‌ترتیب الفبایی مرتب می‌شوند.
  3. فرزندان با مقدار true برای کلید فرزند مشخص‌شده در ردیف بعدی قرار می‌گیرند. اگر چند فرزند مقدار true داشته باشند، به‌صورت واژه‌نامه‌ای براساس کلید مرتب می‌شوند.
  4. فرزندان با مقدار عددی در ردیف بعدی قرار می‌گیرند و به‌ترتیب صعودی مرتب می‌شوند. اگر چند فرزند مقدار عددی یکسانی برای گره فرزند مشخص‌شده داشته باشند، براساس کلید مرتب می‌شوند.
  5. رشته‌ها بعداز اعداد می‌آیند و به‌ترتیب صعودی واژه‌نگاری مرتب می‌شوند. اگر چند فرزند مقدار یکسانی برای گره فرزند مشخص‌شده داشته باشند، به‌ترتیب واژه‌نامه‌ای براساس کلید مرتب می‌شوند.
  6. اشیاء در آخر می‌آیند و به‌ترتیب صعودی براساس کلید مرتب می‌شوند.

orderByKey

هنگام استفاده از orderByKey() برای مرتب‌سازی داده‌ها، داده‌ها به‌صورت صعودی براساس کلید به‌شرح زیر برگردانده می‌شوند. به‌خاطر داشته باشید که کلیدها فقط می‌توانند رشته باشند.

  1. فرزندانی که کلیدی دارند که می‌تواند به‌عنوان عدد صحیح ۳۲ بیتی تجزیه شود، ابتدا می‌آیند و به‌صورت صعودی مرتب می‌شوند.
  2. فرزندانی که مقدار رشته‌ای به‌عنوان کلید دارند در ردیف بعدی قرار می‌گیرند و به‌ترتیب صعودی واژه‌نامه‌ای مرتب می‌شوند.

orderByValue

هنگام استفاده از orderByValue()، فرزندان براساس مقدارشان مرتب می‌شوند. معیار ترتیب‌بندی همانند orderByChild() است، با این تفاوت که به‌جای مقدار کلید فرزند مشخص‌شده، از مقدار گره استفاده می‌شود.