جارٍ استرداد البيانات

يتناول هذا المستند أساسيات استرداد بيانات قاعدة البيانات، وكيف يتم ترتيب البيانات، وكيفية استعلامات بسيطة عن البيانات. يتم تنفيذ استرداد البيانات في SDK للمشرف بشكل مختلف قليلاً عبر ولغات البرمجة.

  1. أدوات معالجة الأحداث غير المتزامنة: يتم استرداد البيانات المخزنة في قاعدة بيانات Firebase في الوقت الفعلي من خلال توصيل مستمع غير متزامن بـ مرجع قاعدة بيانات. يتم تشغيل المستمع مرة واحدة للحالة الأولية للبيانات مرة أخرى في أي وقت تتغير فيه البيانات. قد يتلقى مستمع الحدث العديد من لأنواع الأحداث. وضع استرجاع البيانات هذا معتمد في Java وNode.js وحزم SDK لمشرفي Python.
  2. قراءات الحظر: يتم استرداد البيانات المخزنة في قاعدة بيانات Firebase في الوقت الفعلي عن طريق استدعاء طريقة حظر على قاعدة بيانات المرجع، الذي يُرجع البيانات المخزنة في المرجع. يُعد كل استدعاء طريقة لمرة واحدة العملية. وهذا يعني أنّ حزمة تطوير البرامج (SDK) لا تسجّل أي عمليات استدعاء تصغي إلى تعديلات البيانات اللاحقة. يتوفّر نموذج استرداد البيانات هذا في حِزم تطوير البرامج (SDK) للمشرفين في Python وGo.

البدء

هيا نعيد النظر في مثال التدوين من المقالة السابقة لفهم كيفية قراءة البيانات من قاعدة بيانات Firebase. الاستدعاء أنه يتم تخزين مشاركات المدونة في التطبيق النموذجي على عنوان URL لقاعدة البيانات https://docs-examples.firebaseio.com/server/saving-data/fireblog/post.json. لقراءة بيانات مشاركتك، يمكنك إجراء ما يلي:

جافا
public static class Post {

  public String author;
  public String title;

  public Post(String author, String title) {
    // ...
  }

}

// Get a reference to our posts
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("server/saving-data/fireblog/posts");

// Attach a listener to read the data at our posts reference
ref.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    Post post = dataSnapshot.getValue(Post.class);
    System.out.println(post);
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    System.out.println("The read failed: " + databaseError.getCode());
  }
});
Node.js
// Get a database reference to our posts
const db = getDatabase();
const ref = db.ref('server/saving-data/fireblog/posts');

// Attach an asynchronous callback to read the data at our posts reference
ref.on('value', (snapshot) => {
  console.log(snapshot.val());
}, (errorObject) => {
  console.log('The read failed: ' + errorObject.name);
}); 
Python
# Import database module.
from firebase_admin import db

# Get a database reference to our posts
ref = db.reference('server/saving-data/fireblog/posts')

# Read the data at the posts reference (this is a blocking operation)
print(ref.get())
انتقال

// 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() في بايثون تمثيل بايثون للبيانات مباشرةً. تعمل الدالة 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);
});

في هذا المثال، ستحتوي اللقطة على كائن مع مشاركة مدونة فردية. بما أنّ حزمة تطوير البرامج (SDK) تحوّل المشاركات إلى عناصر من خلال استرداد القيمة، يمكنك الوصول إلى سمتَي المؤلف والعنوان من خلال طلب author وtitle على التوالي. يمكنك أيضًا الوصول إلى رقم تعريف المشاركة السابقة من وسيطة prevChildKey الثانية.

تم تغيير عنصر ثانوي

يتم تشغيل حدث child_changed في أي وقت يتم فيه تعديل عقدة فرعية. ويتضمن ذلك أي التعديلات على العناصر التابعة للعقدة الفرعية. يتم استخدام الرقم عادةً مع child_added. وchild_removed للرد على التغييرات في قائمة من العناصر. تحتوي النبذة التي يتم تمريرها إلى معاودة الاتصال بالحدث على البيانات المعدّلة الفرعية.

يمكنك استخدام ميزة "child_changed" لقراءة البيانات المعدّلة حول مشاركات المدونة عند تعديلها:

جافا
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {
    Post changedPost = dataSnapshot.getValue(Post.class);
    System.out.println("The updated post title is: " + changedPost.title);
  }

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

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

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Get the data on a post that has changed
ref.on('child_changed', (snapshot) => {
  const changedPost = snapshot.val();
  console.log('The updated post title is ' + changedPost.title);
});

تمت إزالة الطفل

يتم بدء حدث child_removed عند إزالة عنصر ثانوي فوري. ويتم استخدامها عادةً مع child_added وchild_changed. تحتوي النبذة التي يتم تمريرها إلى معاودة الاتصال بالحدث على بيانات الطفل الذي تمت إزالته.

في مثال المدونة، يمكنك استخدام child_removed لتسجيل إشعار في وحدة التحكّم بشأن المشاركة المحذوفة:

جافا
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {}

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

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {
    Post removedPost = dataSnapshot.getValue(Post.class);
    System.out.println("The blog post titled " + removedPost.title + " has been deleted");
  }

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

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Get a reference to our posts
const ref = db.ref('server/saving-data/fireblog/posts');

// Get the data on a post that has been removed
ref.on('child_removed', (snapshot) => {
  const deletedPost = snapshot.val();
  console.log('The blog post titled \'' + deletedPost.title + '\' has been deleted');
});

تم نقل طفل

ويُستخدَم الحدث child_moved عند التعامل مع البيانات المرتبة، ويمكنك الاطّلاع على هذه المقالة في القسم التالي.

ضمانات الأحداث

تقدّم قاعدة بيانات Firebase العديد من الضمانات المهمّة بشأن الأحداث:

ضمانات أحداث قاعدة البيانات
سيتم تشغيل الأحداث دائمًا عند تغيير الحالة المحلية.
وستعكس الأحداث دائمًا الحالة الصحيحة للبيانات، حتى في الحالات التي تكون فيها العمليات أو إلى حدوث اختلافات مؤقتة، مثل فقدان الاتصال بالشبكة مؤقتًا.
ستتم دائمًا كتابة الكتابة من جهاز عميل واحد على الخادم وبثها على المستخدمين الآخرين بالترتيب.
يتم دائمًا بدء أحداث القيمة في النهاية ويتم ضمان احتوائها على تعديلات من أي أحداث أخرى وقعت. قبل أخذ تلك اللقطة.

بما أنّ أحداث القيم تبدأ دائمًا في النهاية، سيكون المثال التالي صالحًا دائمًا:

جافا
final AtomicInteger count = new AtomicInteger();

ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    // New child added, increment count
    int newCount = count.incrementAndGet();
    System.out.println("Added " + dataSnapshot.getKey() + ", count is " + newCount);
  }

  // ...
});

// The number of children will always be equal to 'count' since the value of
// the dataSnapshot here will include every child_added event triggered before this point.
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    long numChildren = dataSnapshot.getChildrenCount();
    System.out.println(count.get() + " == " + numChildren);
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
let count = 0;

ref.on('child_added', (snap) => {
  count++;
  console.log('added:', snap.key);
});

// length will always equal count, since snap.val() will include every child_added event
// triggered before this point
ref.once('value', (snap) => {
  console.log('initial data loaded!', snap.numChildren() === count);
});

فصل طلبات معاودة الاتصال

تتم إزالة عمليات الاستدعاء عن طريق تحديد نوع الحدث ودالة معاودة الاتصال المراد إزالتها، كما يلي:

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

// Remove listener
ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);

في حال تمرير سياق النطاق إلى on()، يجب تمريره عند فصل معاودة الاتصال:

جافا
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);

إذا كنت تريد إزالة جميع عمليات معاودة الاتصال في موقع جغرافي ما، يمكنك إجراء ما يلي:

جافا
// No Java equivalent, listeners must be removed individually.
Node.js
// Remove all value callbacks
ref.off('value');

// Remove all callbacks of any type
ref.off();

قراءة البيانات مرة واحدة

وفي بعض الحالات، قد يكون من المفيد طلب معاودة الاتصال مرة واحدة ثم إزالتها على الفور. لقد أنشأنا أداة مساعدة لجعل ذلك سهلاً وهو:

جافا
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    // ...
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    // ...
  }
});
Node.js
ref.once('value', (data) => {
  // do some stuff once
});
Python
# Import database module.
from firebase_admin import db

# Get a database reference to our posts
ref = db.reference('server/saving-data/fireblog/posts')

# Read the data at the posts reference (this is a blocking operation)
print(ref.get())
انتقال
// Create a database client from App.
client, err := app.Database(ctx)
if err != nil {
	log.Fatalln("Error initializing database client:", err)
}

// Get a database reference to our posts
ref := client.NewRef("server/saving-data/fireblog/posts")

// Read the data at the posts reference (this is a blocking operation)
var post Post
if err := ref.Get(ctx, &post); err != nil {
	log.Fatalln("Error reading value:", err)
}

الاستعلام عن البيانات

باستخدام طلبات قاعدة بيانات Firebase، يمكنك استرداد البيانات بشكل انتقائي استنادًا إلى عوامل مختلفة. لإنشاء استعلام في قاعدة البيانات الخاصة بك، تبدأ بتحديد الطريقة التي تريد بها ترتيب بياناتك باستخدام إحدى دوال الترتيب: orderByChild() أو orderByKey() أو orderByValue(). يمكنك بعد ذلك دمج هذه الطرق مع خمس طرق أخرى لإجراء طلبات بحث معقدة: limitToFirst(). limitToLast() وstartAt() وendAt() وequalTo()

نظرًا لأن جميعنا في Firebase يعتقدون أن الديناصورات رائعة جدًا، فسنستخدم مقتطفًا من نموذج قاعدة بيانات لحقائق الديناصورات لتوضيح كيف يمكنك الاستعلام عن البيانات في قاعدة بيانات Firebase لديك.:

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

يمكنك ترتيب البيانات بثلاث طرق: حسب المفتاح الفرعي أو حسب المفتاح أو حسب القيمة. يبدأ استعلام قاعدة البيانات الأساسي بإحدى دوال الترتيب هذه، ويتم شرح كل منها أدناه.

الترتيب حسب مفتاح فرعي محدّد

يمكنك ترتيب العُقد حسب مفتاح فرعي مشترك عن طريق تمرير هذا المفتاح إلى orderByChild(). على سبيل المثال، لقراءة جميع الديناصورات مرتبة حسب الارتفاع، يمكنك إجراء ما يلي:

جافا
public static class Dinosaur {

  public int height;
  public int weight;

  public Dinosaur(int height, int weight) {
    // ...
  }

}

final DatabaseReference dinosaursRef = database.getReference("dinosaurs");
dinosaursRef.orderByChild("height").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    Dinosaur dinosaur = dataSnapshot.getValue(Dinosaur.class);
    System.out.println(dataSnapshot.getKey() + " was " + dinosaur.height + " meters tall.");
  }

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

ref.orderByChild('height').on('child_added', (snapshot) => {
  console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall');
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').get()
for key, val in snapshot.items():
    print('{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');
});
Python
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);
});
Python
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());
  });
});
Python
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 والقيم المنطقية والسلسلة والكائنات عند استخدام orderByValue().

طلبات البحث المعقّدة

والآن بعد أن اتضح كيفية ترتيب بياناتك، يمكنك استخدام طريقة الحد أو النطاق الموضحة أدناه لإنشاء طلبات بحث أكثر تعقيدًا.

الحدّ من طلبات البحث

يتمّ استخدام طلبَي البحث limitToFirst() وlimitToLast() لتحديد الحد الأقصى لعدد العناصر الثانوية المطلوب مزامنتها لمعاودة اتصال معيّنة. إذا حددت 100 مرة كحد أقصى، في البداية، سيتلقّى عدد من أحداث "child_added" يصل إلى 100 حدث فقط كحدّ أقصى. إذا كان لديك أقل من 100 رسالة مخزَّنة في قاعدة البيانات، سيتم تنشيط حدث child_added لكل منها . ولكن إذا كان لديك أكثر من 100 رسالة، ستتلقى child_added فقط. حدث لـ 100 رسالة من هذه الرسائل. هذه هي أول 100 رسالة مرتبة إذا كنت تستخدم limitToFirst() أو آخر 100 رسالة مرتَّبة إذا كنت تستخدم limitToLast() عند تغيير العناصر، ستتلقّى child_added حدث. عن العناصر التي تُدخِل طلب البحث وأحداث child_removed للعناصر التي تتركه، بحيث يظل العدد الإجمالي 100.

باستخدام قاعدة بيانات حقائق الديناصورات وorderByChild()، يمكنك العثور على أهم نتيجتين الديناصورات:

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

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
Python
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
انتقال
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);
});
Python
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());
  });
});
Python
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);
});
Python
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);
});
Python
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);
});
Python
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);
});
Python
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');
      }
    });
});
Python
ref = db.reference('dinosaurs')
favotire_dino_height = ref.child('stegosaurus').child('height').get()
query = ref.order_by_child('height').end_at(favotire_dino_height).limit_to_last(2)
snapshot = query.get()
if len(snapshot) == 2:
    # Data is ordered by increasing height, so we want the first entry.
    # Second entry is stegosarus.
    for key in snapshot:
        print('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")
}

كيفية ترتيب البيانات

يشرح هذا القسم كيف يتم ترتيب بياناتك عند استخدام كل من وظائف الترتيب الأربع.

طلب ByChild

عند استخدام orderByChild()، يتم ترتيب البيانات التي تحتوي على المفتاح الفرعي المحدّد على النحو التالي:

  1. تأتي العناصر الثانوية ذات القيمة null للمفتاح الفرعي المحدّد في المقام الأول.
  2. تأتي العناصر الثانوية بقيمة false للمفتاح الفرعي المحدّد بعد ذلك. إذا كانت القيمة false لعدة عناصر ثانوية، يتم ترتيبها معجميًا حسب المفتاح.
  3. تأتي العناصر الثانوية بقيمة true للمفتاح الفرعي المحدّد بعد ذلك. إذا كانت للعديد من العناصر الثانوية القيمة true، يتم ترتيبها قاموسًا حسب المفتاح.
  4. تأتي الأطفال ذوي القيمة الرقمية بعد ذلك، مرتبة بترتيب تصاعدي. إذا كان لعدة عناصر فرعية نفس القيمة الرقمية للعقدة الفرعية المحددة، فسيتم فرزها حسب المفتاح.
  5. تأتي السلاسل بعد الأرقام، ويتم فرزها قاموسًا بترتيب تصاعدي. إذا كانت هناك عدة عناصر فرعية لها نفس القيمة للعقدة الفرعية المحددة، فسيتم ترتيبها حسب العنوان.
  6. تأتي الكائنات في المرتبة الأخيرة، ويتم فرزها لغويًا حسب المفتاح بترتيب تصاعدي.

مفتاح OrderByKey

عند استخدام orderByKey() لترتيب بياناتك، يتم عرض البيانات بترتيب تصاعدي حسب المفتاح على النحو التالي. ضع في اعتبارك أن المفاتيح لا يمكن أن تكون سوى سلاسل.

  1. تأتي العناصر الثانوية التي تتضمن مفتاحًا يمكن تحليله كعدد صحيح 32 بت أولاً، ويتم ترتيبها تصاعديًا.
  2. يأتي العناصر الثانوية التي لها قيمة سلسلة كمفتاحها بعد ذلك، ويتم فرزها ترتيبًا تصاعديًا.

قيمة الطلب

عند استخدام orderByValue()، يتم ترتيب العناصر الثانوية حسب قيمتها. معايير الترتيب هي نفسها المعايير في orderByChild()، باستثناء استخدام قيمة العُقدة بدلاً من قيمة مفتاح فرعي محدّد.