Check out what’s new from Firebase at Google I/O 2022. Learn more

استرجاع البيانات

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

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

ابدء

دعنا نعيد النظر في مثال التدوين من المقالة السابقة لفهم كيفية قراءة البيانات من قاعدة بيانات 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 و Java ، يتم استدعاء وظيفة المستمع في أي وقت يتم فيه إضافة بيانات جديدة إلى مرجع قاعدة البيانات الخاصة بك ، ولا تحتاج إلى كتابة أي كود إضافي لتحقيق ذلك.

في Java و Node.js ، تتلقى وظيفة رد الاتصال DataSnapshot ، وهي لقطة من البيانات. اللقطة هي صورة للبيانات في مرجع قاعدة بيانات معين في نقطة زمنية واحدة. يؤدي استدعاء val() / getValue() في لقطة ما إلى إرجاع تمثيل كائن خاص بلغة معينة من البيانات. في حالة عدم وجود بيانات في موقع المرجع ، تكون قيمة اللقطة null . تقوم طريقة get() في Python بإرجاع تمثيل Python للبيانات مباشرة. تقوم وظيفة Get() في Go بإلغاء تنظيم البيانات في بنية بيانات معينة.

لاحظ أننا استخدمنا نوع حدث value في المثال أعلاه ، والذي يقرأ المحتويات الكاملة لمرجع قاعدة بيانات Firebase ، حتى لو تم تغيير جزء واحد فقط من البيانات. value هي أحد أنواع الأحداث الخمسة المختلفة المدرجة أدناه والتي يمكنك استخدامها لقراءة البيانات من قاعدة البيانات.

اقرأ أنواع الأحداث في Java و Node.js

قيمة

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

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

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

إذا كنت تريد استرداد البيانات فقط من كل منشور جديد مضاف إلى تطبيق التدوين الخاص بك ، فيمكنك استخدام child_added :

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

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

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

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

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

في هذا المثال ، ستحتوي اللقطة على كائن مع مشاركة مدونة فردية. نظرًا لأن 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
});
بايثون
# 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 والمنطقية والسلسلة والكائن عند استخدام orderByValue() .

استعلامات معقدة

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

استعلامات الحد

تُستخدم limitToFirst() و limitToLast() لتعيين الحد الأقصى لعدد الأطفال المراد مزامنتهم لاستدعاء معين. إذا قمت بتعيين حد 100 ، فستتلقى في البداية ما يصل إلى 100 حدث child_added . إذا كان لديك أقل من 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);
});
بايثون
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
يذهب
ref := client.NewRef("dinosaurs")

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

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

وبالمثل ، يمكنك إيجاد أقصر ديناصورات باستخدام limitToFirst() :

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

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
بايثون
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
يذهب
ref := client.NewRef("dinosaurs")

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

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

يمكنك أيضًا إجراء استعلامات محدودة باستخدام orderByValue() . إذا كنت ترغب في إنشاء لوحة ليدربورد تحتوي على أعلى 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() للعثور على جميع الديناصورات التي تأتي أسماؤها قبل معجم الزاحف المجنح:

جافا
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())
}

استعلامات النطاق مفيدة أيضًا عندما تحتاج إلى ترقيم بياناتك.

ضع كل شيء معا

يمكنك دمج كل هذه الأساليب لإنشاء استعلامات معقدة. على سبيل المثال ، يمكنك العثور على اسم الديناصور الأقصر من Stegosaurus:

جافا
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() ، يتم ترتيب البيانات التي تحتوي على المفتاح الفرعي المحدد على النحو التالي:

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

الطلب

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

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

ترتيب حسب القيمة

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