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

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

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

ابدء

دعنا نعيد النظر في مثال التدوين من المقالة السابقة لفهم كيفية قراءة البيانات من قاعدة بيانات 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())
}

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

ضع كل شيء معا

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

جافا
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() ، باستثناء أن قيمة العقدة تُستخدم بدلاً من قيمة المفتاح الفرعي المحدد.