بازیابی داده ها

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

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

شروع به کار

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

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

توجه داشته باشید که ما در مثال بالا از نوع رویداد value استفاده کردیم که کل محتوای یک مرجع پایگاه داده Firebase را می‌خواند، حتی اگر فقط یک قطعه داده تغییر کرده باشد. value یکی از پنج نوع رویداد مختلف ذکر شده در زیر است که می‌توانید برای خواندن داده‌ها از پایگاه داده از آنها استفاده کنید.

خواندن انواع رویدادها در جاوا و Node.js

ارزش

رویداد value برای خواندن یک snapshot ایستا از محتویات یک مسیر پایگاه داده مشخص، همانطور که در زمان رویداد read وجود داشته‌اند، استفاده می‌شود. این رویداد یک بار با داده‌های اولیه و بار دیگر هر بار که داده‌ها تغییر می‌کنند، فعال می‌شود. رویداد callback یک snapshot حاوی تمام داده‌های موجود در آن مکان، از جمله داده‌های فرزند، ارسال می‌کند. در مثال کد بالا، value تمام پست‌های وبلاگ را در برنامه شما برمی‌گرداند. هر بار که یک پست وبلاگ جدید اضافه می‌شود، تابع callback تمام پست‌ها را برمی‌گرداند.

کودک اضافه شد

رویداد child_added معمولاً هنگام بازیابی لیستی از موارد از پایگاه داده استفاده می‌شود. برخلاف value که کل محتوای مکان را برمی‌گرداند، child_added یک بار برای هر فرزند موجود و سپس هر بار که فرزند جدیدی به مسیر مشخص شده اضافه می‌شود، فعال می‌شود. فراخوانی رویداد، یک snapshot حاوی داده‌های فرزند جدید ارسال می‌شود. برای مرتب‌سازی، یک آرگومان دوم نیز ارسال می‌شود که حاوی کلید فرزند قبلی است.

اگر می‌خواهید فقط داده‌های مربوط به هر پست جدید اضافه شده به برنامه وبلاگ نویسی خود را بازیابی کنید، می‌توانید از child_added استفاده کنید:

جاوا
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    Post newPost = dataSnapshot.getValue(Post.class);
    System.out.println("Author: " + newPost.author);
    System.out.println("Title: " + newPost.title);
    System.out.println("Previous Post ID: " + prevChildKey);
  }

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
نود جی اس
// Retrieve new posts as they are added to our database
ref.on('child_added', (snapshot, prevChildKey) => {
  const newPost = snapshot.val();
  console.log('Author: ' + newPost.author);
  console.log('Title: ' + newPost.title);
  console.log('Previous Post ID: ' + prevChildKey);
});

در این مثال، تصویر لحظه‌ای شامل یک شیء با یک پست وبلاگ جداگانه خواهد بود. از آنجا که SDK با بازیابی مقدار، پست‌ها را به اشیاء تبدیل می‌کند، شما با فراخوانی author و title به ترتیب به ویژگی‌های author و title پست دسترسی دارید. همچنین از آرگومان دوم prevChildKey به شناسه پست قبلی دسترسی دارید.

کودک تغییر کرد

رویداد child_changed هر زمان که یک گره فرزند تغییر کند، فعال می‌شود. این شامل هرگونه تغییری در فرزندان گره فرزند نیز می‌شود. این رویداد معمولاً همراه با child_added و child_removed برای پاسخ به تغییرات در لیستی از موارد استفاده می‌شود. تصویر لحظه‌ای ارسال شده به رویداد callback حاوی داده‌های به‌روزرسانی شده برای فرزند است.

می‌توانید از 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 استفاده می‌شود. اسنپ‌شات ارسالی به رویداد callback حاوی داده‌های فرزند حذف شده است.

در مثال وبلاگ، می‌توانید از 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 هنگام کار با داده‌های مرتب‌شده استفاده می‌شود که در بخش بعدی به آن پرداخته خواهد شد.

ضمانت‌های رویداد

پایگاه داده Firebase چندین تضمین مهم در مورد رویدادها ارائه می‌دهد:

ضمانت‌های رویداد پایگاه داده
رویدادها همیشه زمانی فعال می‌شوند که وضعیت محلی تغییر کند.
رویدادها در نهایت همیشه وضعیت صحیح داده‌ها را منعکس می‌کنند، حتی در مواردی که عملیات محلی یا زمان‌بندی باعث ایجاد اختلافات موقت شوند، مانند قطع موقت اتصال شبکه.
نوشتن‌ها از یک کلاینت واحد همیشه به سرور نوشته می‌شوند و به ترتیب به سایر کاربران ارسال می‌شوند.
رویدادهای مقداری همیشه آخرین رویدادی هستند که اجرا می‌شوند و تضمین می‌شود که شامل به‌روزرسانی‌هایی از هر رویداد دیگری باشند که قبل از گرفتن آن snapshot رخ داده است.

از آنجایی که رویدادهای مقداری همیشه آخرین رویداد هستند، مثال زیر همیشه کار خواهد کرد:

جاوا
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);
});

جدا کردن Callbackها

فراخوانی‌های برگشتی با مشخص کردن نوع رویداد و تابع فراخوانی که باید حذف شود، حذف می‌شوند، مانند زیر:

جاوا
// Create and attach listener
ValueEventListener listener = new ValueEventListener() {
    // ...
};
ref.addValueEventListener(listener);

// Remove listener
ref.removeEventListener(listener);
نود جی اس
ref.off('value', originalCallback);

اگر یک زمینه دامنه (scope context) را به on() ارسال کرده‌اید، باید هنگام جدا کردن تابع فراخوانی (callback) ارسال شود:

جاوا
// 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();

خواندن داده‌ها یک بار

در برخی موارد، ممکن است مفید باشد که یک تابع فراخوانی (callback) یک بار فراخوانی شود و سپس بلافاصله حذف شود. ما یک تابع کمکی ایجاد کرده‌ایم تا این کار را آسان کند:

جاوا
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 مرتب می‌شود، به این معنی که در ترتیب، اول قرار می‌گیرد. برای جزئیات بیشتر در مورد نحوه مرتب‌سازی داده‌ها، به بخش «نحوه مرتب‌سازی داده‌ها» مراجعه کنید.

کوئری‌ها همچنین می‌توانند توسط فرزندان تو در تو مرتب شوند، نه فقط فرزندان یک سطح پایین‌تر. این روش در صورتی مفید است که داده‌های تو در تو مانند زیر داشته باشید:

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

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

پرس‌وجوهای پیچیده

اکنون که نحوه مرتب‌سازی داده‌های شما مشخص شد، می‌توانید از روش‌های حد یا محدوده که در زیر توضیح داده شده است برای ساخت پرس‌وجوهای پیچیده‌تر استفاده کنید.

محدود کردن کوئری‌ها

کوئری‌های limitToFirst() و limitToLast() برای تعیین حداکثر تعداد فرزندانی که باید برای یک فراخوانی مجدد همگام‌سازی شوند، استفاده می‌شوند. اگر محدودیت ۱۰۰ را تعیین کنید، در ابتدا فقط تا ۱۰۰ رویداد child_added دریافت خواهید کرد. اگر کمتر از ۱۰۰ پیام در پایگاه داده خود ذخیره کرده باشید، یک رویداد child_added برای هر پیام اجرا می‌شود. با این حال، اگر بیش از ۱۰۰ پیام داشته باشید، فقط برای ۱۰۰ مورد از آن پیام‌ها یک رویداد child_added دریافت خواهید کرد. اگر از limitToFirst() استفاده می‌کنید، این ۱۰۰ پیام مرتب‌شده اول و اگر از limitToLast() استفاده می‌کنید، ۱۰۰ پیام مرتب‌شده آخر هستند. با تغییر موارد، برای مواردی که وارد کوئری می‌شوند، رویدادهای child_added و برای مواردی که از آن خارج می‌شوند، رویدادهای child_removed دریافت خواهید کرد، به طوری که تعداد کل ۱۰۰ باقی می‌ماند.

با استفاده از پایگاه داده حقایق دایناسور و orderByChild() ، می‌توانید دو تا از سنگین‌ترین دایناسورها را پیدا کنید:

جاوا
dinosaursRef.orderByChild("weight").limitToLast(2).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
نود جی اس
const ref = db.ref('dinosaurs');
ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
پایتون
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
برو
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("weight").LimitToLast(2).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

تابع فراخوانی child_added دقیقاً دو بار اجرا می‌شود، مگر اینکه کمتر از دو دایناسور در پایگاه داده ذخیره شده باشد. همچنین برای هر دایناسور جدید و سنگین‌تری که به پایگاه داده اضافه می‌شود، این تابع فراخوانی می‌شود. در پایتون، کوئری مستقیماً یک OrderedDict حاوی دو دایناسور سنگین‌تر را برمی‌گرداند.

به طور مشابه، می‌توانید دو دایناسور کوتاه‌تر را با استفاده از limitToFirst() پیدا کنید:

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

  // ...
});
نود جی اس
const ref = db.ref('dinosaurs');
ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
پایتون
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
برو
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").LimitToFirst(2).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

تابع فراخوانی child_added دقیقاً دو بار اجرا می‌شود، مگر اینکه کمتر از دو دایناسور در پایگاه داده ذخیره شده باشد. همچنین اگر یکی از دو دایناسور اول از پایگاه داده حذف شود، دوباره اجرا می‌شود، زیرا اکنون یک دایناسور جدید، دومین دایناسور کوتاه خواهد بود. در پایتون، کوئری مستقیماً یک OrderedDict حاوی کوتاه‌ترین دایناسورها را برمی‌گرداند.

همچنین می‌توانید با استفاده از orderByValue() پرس‌وجوهای محدودکننده انجام دهید. اگر می‌خواهید یک جدول امتیازات شامل ۳ دایناسور برتر مسابقات دایناسوری با بالاترین امتیاز ایجاد کنید، می‌توانید موارد زیر را انجام دهید:

جاوا
scoresRef.orderByValue().limitToFirst(3).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue());
  }

  // ...
});
نود جی اس
const scoresRef = db.ref('scores');
scoresRef.orderByValue().limitToLast(3).on('value', (snapshot)  =>{
  snapshot.forEach((data) => {
    console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val());
  });
});
پایتون
scores_ref = db.reference('scores')
snapshot = scores_ref.order_by_value().limit_to_last(3).get()
for key, val in snapshot.items():
    print(f'The {key} dinosaur\'s score is {val}')
برو
ref := client.NewRef("scores")

results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var score int
	if err := r.Unmarshal(&score); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score)
}

پرس و جوهای محدوده

استفاده از startAt() ، endAt() و equalTo() به شما امکان می‌دهد نقاط شروع و پایان دلخواه را برای پرس‌وجوهای خود انتخاب کنید. برای مثال، اگر می‌خواهید تمام دایناسورهایی را که حداقل سه متر قد دارند پیدا کنید، می‌توانید orderByChild() و startAt() با هم ترکیب کنید:

جاوا
dinosaursRef.orderByChild("height").startAt(3).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
نود جی اس
const ref = db.ref('dinosaurs');
ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
پایتون
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').start_at(3).get()
for key in snapshot:
    print(key)
برو
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").StartAt(3).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

شما می‌توانید endAt() برای یافتن تمام دایناسورهایی که نامشان قبل از Pterodactyl آمده است، به صورت لغوی استفاده کنید:

جاوا
dinosaursRef.orderByKey().endAt("pterodactyl").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
نود جی اس
const ref = db.ref('dinosaurs');
ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
پایتون
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().end_at('pterodactyl').get()
for key in snapshot:
    print(key)
برو
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().EndAt("pterodactyl").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

شما می‌توانید startAt() و endAt() برای محدود کردن هر دو انتهای کوئری خود ترکیب کنید. مثال زیر تمام دایناسورهایی را که نام آنها با حرف "b" شروع می‌شود، پیدا می‌کند:

جاوا
dinosaursRef.orderByKey().startAt("b").endAt("b\uf8ff").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
نود جی اس
var ref = db.ref('dinosaurs');
ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
پایتون
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().start_at('b').end_at('b\uf8ff').get()
for key in snapshot:
    print(key)
برو
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().StartAt("b").EndAt("b\uf8ff").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

متد equalTo() به شما امکان می‌دهد تا بر اساس تطابق دقیق فیلتر کنید. همانند سایر کوئری‌های محدوده، این متد برای هر گره فرزند منطبق اجرا می‌شود. برای مثال، می‌توانید از کوئری زیر برای یافتن تمام دایناسورهایی که ۲۵ متر قد دارند استفاده کنید:

جاوا
dinosaursRef.orderByChild("height").equalTo(25).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
نود جی اس
const ref = db.ref('dinosaurs');
ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
پایتون
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').equal_to(25).get()
for key in snapshot:
    print(key)
برو
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").EqualTo(25).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

کوئری‌های محدوده‌ای همچنین زمانی مفید هستند که نیاز به صفحه‌بندی داده‌ها داشته باشید.

همه چیز را کنار هم قرار دادن

شما می‌توانید همه این تکنیک‌ها را برای ایجاد پرس‌وجوهای پیچیده ترکیب کنید. برای مثال، می‌توانید نام دایناسوری را که کمی کوتاه‌تر از استگوسوروس است، پیدا کنید:

جاوا
dinosaursRef.child("stegosaurus").child("height").addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot stegoHeightSnapshot) {
    Integer favoriteDinoHeight = stegoHeightSnapshot.getValue(Integer.class);
    Query query = dinosaursRef.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2);
    query.addValueEventListener(new ValueEventListener() {
      @Override
      public void onDataChange(DataSnapshot dataSnapshot) {
        // Data is ordered by increasing height, so we want the first entry
        DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next();
        System.out.println("The dinosaur just shorter than the stegosaurus is: " + firstChild.getKey());
      }

      @Override
      public void onCancelled(DatabaseError databaseError) {
        // ...
      }
    });
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    // ...
  }
});
نود جی اس
  const ref = db.ref('dinosaurs');
  ref.child('stegosaurus').child('height').on('value', (stegosaurusHeightSnapshot) => {
    const favoriteDinoHeight = stegosaurusHeightSnapshot.val();

    const queryRef = ref.orderByChild('height').endAt(favoriteDinoHeight).limitToLast(2);
    queryRef.on('value', (querySnapshot) => {
      if (querySnapshot.numChildren() === 2) {
        // Data is ordered by increasing height, so we want the first entry
        querySnapshot.forEach((dinoSnapshot) => {
          console.log('The dinosaur just shorter than the stegasaurus is ' + dinoSnapshot.key);

          // Returning true means that we will only loop through the forEach() one time
          return true;
        });
      } else {
        console.log('The stegosaurus is the shortest dino');
      }
    });
});
پایتون
ref = db.reference('dinosaurs')
favotire_dino_height = ref.child('stegosaurus').child('height').get()
query = ref.order_by_child('height').end_at(favotire_dino_height).limit_to_last(2)
snapshot = query.get()
if len(snapshot) == 2:
    # Data is ordered by increasing height, so we want the first entry.
    # Second entry is stegosarus.
    for key in snapshot:
        print(f'The dinosaur just shorter than the stegosaurus is {key}')
        return
else:
    print('The stegosaurus is the shortest dino')
برو
ref := client.NewRef("dinosaurs")

var favDinoHeight int
if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil {
	log.Fatalln("Error querying database:", err)
}

query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2)
results, err := query.GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
if len(results) == 2 {
	// Data is ordered by increasing height, so we want the first entry.
	// Second entry is stegosarus.
	fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key())
} else {
	fmt.Println("The stegosaurus is the shortest dino")
}

نحوه مرتب سازی داده ها

این بخش نحوه مرتب‌سازی داده‌های شما هنگام استفاده از هر یک از چهار تابع مرتب‌سازی را توضیح می‌دهد.

سفارش توسط فرزند

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

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

سفارش با کلید

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

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

سفارش بر اساس مقدار

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