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

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

  1. شنوندگان ناهمزمان: داده های ذخیره شده در یک Firebase Realtime Database با پیوست کردن یک شنونده ناهمزمان به یک مرجع پایگاه داده بازیابی می شود. شنونده یک بار برای وضعیت اولیه داده ها و بار دیگر هر زمان که داده ها تغییر کند فعال می شود. یک شنونده رویداد ممکن است چندین نوع مختلف رویداد را دریافت کند. این حالت بازیابی داده در Java، Node.js و Python Admin SDK پشتیبانی می شود.
  2. Blocking می گوید: داده های ذخیره شده در یک 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);
}); 
پایتون
# 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، تابع callback یک DataSnapshot دریافت می کند که یک عکس فوری از داده ها است. یک عکس فوری تصویری از داده ها در یک مرجع پایگاه داده خاص در یک نقطه از زمان است. فراخوانی val() / getValue() در یک عکس فوری، نمایش یک شی خاص زبان از داده ها را برمی گرداند. اگر هیچ داده ای در محل مرجع وجود نداشته باشد، مقدار عکس فوری null است. متد get() در پایتون یک نمایش پایتون از داده ها را مستقیما برمی گرداند. تابع Get() در Go داده ها را به یک ساختار داده معین باز می کند.

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

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

ارزش

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

کودک اضافه شد

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

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

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

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

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

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

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
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);

اگر یک زمینه scope را به 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('{0} was {1} meters tall'.format(key, val))
برو
// 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('{0} was {1} meters tall'.format(key, val))
برو
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('The {0} dinosaur\'s score is {1}'.format(key, 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() به بخش How Data is Ordered مراجعه کنید.

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

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

محدودیت کوئری ها

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

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

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

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

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

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

  // ...
});
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('The {0} dinosaur\'s score is {1}'.format(key, 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() برای یافتن همه دایناسورهایی که نامشان قبل از 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);
});
پایتون
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(u'b\uf8ff').get()
for key in snapshot:
    print(key)
برو
ref := client.NewRef("dinosaurs")

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

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

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

  // ...
});
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('The dinosaur just shorter than the stegosaurus is {0}'.format(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. کودکان دارای کلیدی که می تواند به عنوان یک عدد صحیح 32 بیتی تجزیه شود، در درجه اول قرار می گیرند و به ترتیب صعودی مرتب می شوند.
  2. کودکان با مقدار رشته به عنوان کلید در مرحله بعدی قرار می گیرند که از نظر واژگانی به ترتیب صعودی مرتب شده اند.

orderByValue

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