يتناول هذا المستند أساسيات استرداد بيانات قاعدة البيانات، وكيفية ترتيب البيانات، وكيفية إجراء طلبات بحث بسيطة على البيانات. يتم تنفيذ عملية استرداد البيانات في مدير SDK بشكل مختلف قليلاً في لغات البرمجة المختلفة.
- المستمعون غير المتزامنون: يتم استرداد البيانات المخزّنة في Firebase Realtime Database من خلال إرفاق مستمع غير متزامن بمرجع قاعدة بيانات. يتم تشغيل المستمع مرة واحدة للحالة الأولية للبيانات ومرة أخرى في أي وقت تتغير فيه البيانات. قد يتلقّى متتبِّع الأحداث عدة أنواع مختلفة من الأحداث. يتوفّر وضع استرداد البيانات هذا في حِزم تطوير البرامج (SDK) للمشرفين في Java وNode.js وPython.
- حظر القراءة: يتم استرداد البيانات المخزّنة في Firebase Realtime Database من خلال استدعاء طريقة حظر على مرجع قاعدة بيانات، والتي تعرض البيانات المخزّنة في المرجع. كل طلب إجراء هو عملية لمرة واحدة. وهذا يعني أنّ حزمة تطوير البرامج (SDK) لا تسجّل أي عمليات استدعاء تستمع إلى تحديثات البيانات اللاحقة. يتوفّر نموذج استرداد البيانات هذا في حِزم تطوير البرامج (SDK) للمشرف في Python وGo.
البدء
لنراجع مثال التدوين من المقالة السابقة لفهم كيفية قراءة البيانات من قاعدة بيانات Firebase. يُرجى العِلم أنّ منشورات المدوّنة في التطبيق النموذجي يتم تخزينها في عنوان URL لقاعدة البيانات https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json. لقراءة بيانات منشورك، يمكنك إجراء ما يلي:
Java
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() في Python تمثيلاً للبيانات بلغة Python مباشرةً. تُستخدم الدالة Get() في Go لإلغاء تنظيم البيانات في بنية بيانات معيّنة.
لاحظ أننا استخدمنا نوع الحدث value في المثال أعلاه، والذي يقرأ المحتويات الكاملة لمرجع قاعدة بيانات Firebase، حتى إذا تم تغيير جزء واحد فقط من البيانات. value هو أحد أنواع الأحداث الخمسة المختلفة المذكورة أدناه والتي يمكنك استخدامها لقراءة البيانات من قاعدة البيانات.
قراءة أنواع الأحداث في Java وNode.js
القيمة
يُستخدَم الحدث value لقراءة لقطة ثابتة للمحتوى في مسار معيّن لقاعدة البيانات، كما كانت موجودة في وقت حدث القراءة. ويتم تشغيلها مرة واحدة مع البيانات الأولية ومرة أخرى في كل مرة تتغير فيها البيانات. يتم تمرير دالة الاستدعاء للحدث إلى لقطة تحتوي على جميع البيانات في ذلك الموقع الجغرافي، بما في ذلك البيانات الفرعية. في مثال الرمز أعلاه، عرضت الدالة value جميع منشورات المدوّنة في تطبيقك. وفي كل مرة تتم إضافة منشور مدوّنة جديد، ستعرض دالّة رد الاتصال جميع المنشورات.
تمت إضافة طفل
يُستخدَم الحدث child_added عادةً عند استرداد قائمة بالعناصر من قاعدة البيانات. على عكس value التي تعرض محتوى الموقع الجغرافي بالكامل، يتم تشغيل child_added مرة واحدة لكل عنصر فرعي حالي، ثم مرة أخرى في كل مرة تتم فيها إضافة عنصر فرعي جديد إلى المسار المحدّد. يتم تمرير دالة الاستدعاء للحدث لقطة تحتوي على بيانات العنصر الفرعي الجديد. لأغراض الترتيب، يتم أيضًا تمرير وسيطة ثانية تحتوي على مفتاح العنصر الفرعي السابق.
إذا أردت استرداد البيانات المتعلّقة بكل مشاركة جديدة تتم إضافتها إلى تطبيق التدوين فقط، يمكنك استخدام child_added:
Java
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 لقراءة البيانات المعدَّلة في منشورات المدوّنة عند تعديلها:
Java
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 لتسجيل إشعار بشأن المشاركة المحذوفة في وحدة التحكّم:
Java
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 العديد من الضمانات المهمة في ما يتعلق بالأحداث:
| ضمانات أحداث قاعدة البيانات |
|---|
| سيتم دائمًا تشغيل الأحداث عند تغيير الحالة المحلية. |
| ستعكس الأحداث دائمًا الحالة الصحيحة للبيانات، حتى في الحالات التي تؤدي فيها العمليات المحلية أو التوقيت إلى اختلافات مؤقتة، مثل فقدان الاتصال بالشبكة مؤقتًا. |
| ستتم دائمًا كتابة عمليات الكتابة من عميل واحد على الخادم وبثها للمستخدمين الآخرين بالترتيب. |
| يتم دائمًا تشغيل أحداث القيمة في النهاية، ويُضمن أن تحتوي على تحديثات من أي أحداث أخرى حدثت قبل التقاط تلك اللقطة. |
بما أنّ أحداث القيمة يتم تشغيلها دائمًا في النهاية، سيعمل المثال التالي دائمًا:
Java
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); });
فصل عمليات إعادة الاستدعاء
تتم إزالة عمليات رد الاتصال من خلال تحديد نوع الحدث ودالّة رد الاتصال المطلوب إزالتها، مثل ما يلي:
Java
// Create and attach listener ValueEventListener listener = new ValueEventListener() { // ... }; ref.addValueEventListener(listener); // Remove listener ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);
إذا مرّرت سياق نطاق إلى on()، يجب تمريره عند فصل دالة ردّ الاتصال:
Java
// Not applicable for JavaNode.js
ref.off('value', originalCallback, ctx);
إذا أردت إزالة جميع عمليات معاودة الاتصال في موقع جغرافي معيّن، يمكنك إجراء ما يلي:
Java
// 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();
قراءة البيانات مرة واحدة
In some cases it may be useful for a callback to be called once and then immediately removed. لقد أنشأنا دالة مساعِدة لتسهيل هذه العملية:
Java
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(). على سبيل المثال، لقراءة جميع الديناصورات مرتّبة حسب الارتفاع، يمكنك إجراء ما يلي:
Java
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(f'{key} was {val} meters tall')
متابعة
// Dinosaur is a json-serializable type. type Dinosaur struct { Height int `json:"height"` Width int `json:"width"` } ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
يتم ترتيب أي عقدة لا تحتوي على المفتاح الفرعي الذي نرسل طلب بحث عنه بقيمة null، ما يعني أنّها ستظهر أولاً في الترتيب. للحصول على تفاصيل حول كيفية ترتيب البيانات، يُرجى الاطّلاع على قسم "كيفية ترتيب البيانات".
يمكن أيضًا ترتيب طلبات البحث حسب العناصر الفرعية المتداخلة بشكل كبير، بدلاً من العناصر الفرعية ذات المستوى الأدنى فقط. يكون ذلك مفيدًا إذا كانت لديك بيانات متداخلة بشكل كبير مثل:
{
"lambeosaurus": {
"dimensions": {
"height" : 2.1,
"length" : 12.5,
"weight": 5000
}
},
"stegosaurus": {
"dimensions": {
"height" : 4,
"length" : 9,
"weight" : 2500
}
}
}للاستعلام عن الارتفاع الآن، يمكنك استخدام المسار الكامل للكائن بدلاً من مفتاح واحد:
Java
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(f'{key} was {val} meters tall')
متابعة
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("dimensions/height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
يمكن لطلبات البحث الترتيب حسب مفتاح واحد فقط في كل مرة. يؤدي استدعاء orderByChild()
عدة مرات في طلب البحث نفسه إلى ظهور خطأ.
الترتيب حسب المفتاح
يمكنك أيضًا ترتيب العُقد حسب مفاتيحها باستخدام طريقة orderByKey(). يقرأ المثال التالي جميع الديناصورات بترتيب أبجدي:
Java
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
}
}لترتيب الديناصورات حسب نتيجتها، يمكنك إنشاء طلب البحث التالي:
Java
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(f'The {key} dinosaur\'s score is {val}')
متابعة
ref := client.NewRef("scores") results, err := ref.OrderByValue().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
يُرجى الاطّلاع على قسم كيفية ترتيب البيانات للحصول على شرح حول كيفية ترتيب القيم null والقيم المنطقية والسلاسل وقيم العناصر عند استخدام orderByValue().
طلبات البحث المعقّدة
بعد أن أصبح من الواضح كيفية ترتيب بياناتك، يمكنك استخدام الطريقتَين limit أو range الموضّحتَين أدناه لإنشاء طلبات بحث أكثر تعقيدًا.
الحدّ من طلبات البحث
يتم استخدام طلبَي البحث limitToFirst() وlimitToLast() لضبط الحد الأقصى لعدد العناصر الفرعية التي تتم مزامنتها لعملية استدعاء معيّنة. إذا ضبطت حدًا أقصى يبلغ 100، ستتلقّى في البداية ما يصل إلى 100 حدث من أحداث child_added فقط. إذا كان لديك أقل من 100 رسالة مخزَّنة في قاعدة البيانات، سيتم تنشيط حدث child_added لكل رسالة. ومع ذلك، إذا كان لديك أكثر من 100 رسالة، ستتلقّى حدث child_added
لـ 100 رسالة فقط. هذه هي أول 100 رسالة مرتبة إذا كنت تستخدم
limitToFirst() أو آخر 100 رسالة مرتبة إذا كنت تستخدم
limitToLast(). عندما تتغيّر العناصر، ستتلقّى أحداث child_added
للعناصر التي تدخل في طلب البحث وأحداث child_removed للعناصر التي تخرج منه،
بحيث يبقى إجمالي العدد 100.
باستخدام قاعدة بيانات حقائق الديناصورات و"orderByChild()"، يمكنك العثور على أثقل ديناصورَين:
Java
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 مرتين بالضبط، ما لم يكن هناك
أقل من ديناصورين مخزنين في قاعدة البيانات. وسيتم تشغيله أيضًا لكل ديناصور جديد أثقل يتم إضافته إلى قاعدة البيانات.
في Python، يعرض طلب البحث مباشرةً OrderedDict يحتوي على أثقل ديناصورَين.
وبالمثل، يمكنك العثور على أقصر ديناصورَين باستخدام limitToFirst():
Java
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 مرتين بالضبط، ما لم يكن هناك أقل من ديناصورَين مخزَّنَين في قاعدة البيانات. سيتم أيضًا تشغيله مرة أخرى إذا تمت إزالة أحد الديناصورين الأولين من قاعدة البيانات، حيث سيكون الديناصور الجديد الآن هو ثاني أقصر ديناصور. في Python، يعرض طلب البحث مباشرةً OrderedDict يحتوي على أقصر الديناصورات.
يمكنك أيضًا إجراء طلبات بحث محدودة باستخدام orderByValue(). إذا أردت إنشاء قائمة صدارة تضم أفضل 3 ديناصورات في Dino Sports من حيث النقاط، يمكنك إجراء ما يلي:
Java
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(f'The {key} dinosaur\'s score is {val}')
متابعة
ref := client.NewRef("scores") results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
طلبات البحث عن النطاق
يتيح لك استخدام startAt() وendAt() وequalTo() اختيار نقاط بداية ونهاية عشوائية لطلبات البحث. على سبيل المثال، إذا أردت العثور على جميع الديناصورات التي يبلغ طولها ثلاثة أمتار على الأقل، يمكنك الجمع بين orderByChild() وstartAt():
Java
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"
حسب الترتيب الأبجدي:
Java
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":
Java
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('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 مترًا:
Java
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()) }
تكون طلبات البحث عن النطاق مفيدة أيضًا عندما تحتاج إلى تقسيم بياناتك إلى صفحات.
جارٍ إعداد الردّ
يمكنك الجمع بين كل هذه الأساليب لإنشاء طلبات بحث معقّدة. على سبيل المثال، يمكنك العثور على اسم الديناصور الذي يقل طوله عن ستيغوصور:
Java
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(f'The dinosaur just shorter than the stegosaurus is {key}') return else: print('The stegosaurus is the shortest dino')
متابعة
ref := client.NewRef("dinosaurs") var favDinoHeight int if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil { log.Fatalln("Error querying database:", err) } query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2) results, err := query.GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } if len(results) == 2 { // Data is ordered by increasing height, so we want the first entry. // Second entry is stegosarus. fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key()) } else { fmt.Println("The stegosaurus is the shortest dino") }
كيفية ترتيب البيانات
يشرح هذا القسم كيفية ترتيب بياناتك عند استخدام كل من دوال الترتيب الأربع.
orderByChild
عند استخدام orderByChild()، يتم ترتيب البيانات التي تحتوي على المفتاح الفرعي المحدّد على النحو التالي:
- تظهر العناصر الفرعية التي لها قيمة
nullلمفتاح الطفل المحدّد أولاً. - وتأتي بعد ذلك العناصر الثانوية التي تحمل القيمة
falseللمفتاح الثانوي المحدّد. إذا كانت هناك عدة عناصر فرعية لها القيمةfalse، يتم ترتيبها معجميًا حسب المفتاح. - وتأتي بعد ذلك العناصر الثانوية التي تحمل القيمة
trueللمفتاح الثانوي المحدّد. إذا كانت هناك عناصر فرعية متعددة لها القيمةtrue، يتم ترتيبها معجميًا حسب المفتاح. - تأتي بعد ذلك العناصر الفرعية التي لها قيمة رقمية، ويتم ترتيبها تصاعديًا. إذا كان هناك عدة عناصر فرعية لها القيمة الرقمية نفسها للعقدة الفرعية المحدّدة، يتم ترتيبها حسب المفتاح.
- تأتي السلاسل بعد الأرقام، ويتم ترتيبها معجميًا بترتيب تصاعدي. إذا كان لدى عدة عناصر فرعية القيمة نفسها للعقدة الفرعية المحدّدة، يتم ترتيبها معجميًا حسب المفتاح.
- تأتي العناصر في النهاية، ويتم ترتيبها معجميًا حسب المفتاح بترتيب تصاعدي.
orderByKey
عند استخدام orderByKey() لترتيب بياناتك، يتم عرض البيانات بترتيب تصاعدي حسب المفتاح على النحو التالي. يُرجى العِلم أنّ المفاتيح يمكن أن تكون سلاسل فقط.
- تأتي العناصر الفرعية التي تحتوي على مفتاح يمكن تحليله كعدد صحيح من 32 بت في البداية، ويتم ترتيبها تصاعديًا.
- بعد ذلك، يتم ترتيب العناصر الثانوية التي تحتوي على قيمة سلسلة كمفتاح لها، ويتم ترتيبها معجميًا تصاعديًا.
orderByValue
عند استخدام orderByValue()، يتم ترتيب العناصر الفرعية حسب قيمتها. معايير الترتيب هي نفسها في orderByChild()، باستثناء أنّه يتم استخدام قيمة العقدة بدلاً من قيمة مفتاح فرعي محدّد.