In Firebase, How to Query the Most Recent 10 Child Nodes

In Firebase, how can I query the most recent 10 child nodes?

The answer is that you need to use a bit of reverse logic, and also store a timestamp key:value pair within each node as a negative value. I omitted the user_id: 1 to keep the answer cleaner.

Here's the Firebase structure

"test" : {
"-KFUR91fso4dEKnm3RIF" : {
"timestamp" : -1.46081635550362E12
},
"-KFUR9YH5QSCTRWEzZLr" : {
"timestamp" : -1.460816357590991E12
},
"-KFURA4H60DbQ1MbrFC1" : {
"timestamp" : -1.460816359767055E12
},
"-KFURAh15i-sWD47RFka" : {
"timestamp" : -1.460816362311195E12
},
"-KFURBHuE7Z5ZvkY9mlS" : {
"timestamp" : -1.460816364735218E12
}
}

and here's how that's written out to Firebase; I just used a IBAction for a button to write out a few nodes:

let testRef = self.myRootRef.childByAppendingPath("test")

let keyRef = testRef.childByAutoId()

let nodeRef = keyRef.childByAppendingPath("timestamp")

let t1 = Timestamp

nodeRef.setValue( 0 - t1) //note the negative value

and the code to read it in

    let ref = self.myRootRef.childByAppendingPath("test")
ref.queryOrderedByChild("timestamp").queryLimitedToFirst(3).observeEventType(.ChildAdded, withBlock: { snapshot in
print("The key: \(snapshot.key)") //the key
})

and I declared a little function to return the current Timestamp

var Timestamp: NSTimeInterval {
return NSDate().timeIntervalSince1970 * 1000
}

and the output

The key: -KFURBHuE7Z5ZvkY9mlS
The key: -KFURAh15i-sWD47RFka
The key: -KFURA4H60DbQ1MbrFC1

As you can see, they are in reverse order.

Things to note:

  1. Writing out your timestamp as negative values
  2. When reading in use .queryLimitedToFirst instead of last.

On that note, you can also just read the data as usual and add it to an Array then then sort the array descending. That puts more effort on the client and if you have 10,000 nodes may not be a good solution.

firebase listen most recent 10 item at a path without losing 10th item when a new child is added

Solution is simple: use "child_added" instead of "value"

var items = [];
ref.orderByKey().limitToLast(10).on("child_added", (snap) => {
items.push(snap);
});

// To load more do this
ref.orderByKey().limitToLast(20).endAt(items[0].key).once("value")

This way you will immediately get last 10 items one-by-one and then each new item

Search child nodes in firebase database using android

When you run a query at a location, Firebase check each child node at that location for the property that you order on and the range/condition you filter on.

mDatabase.child("Medicine").child("symptoms").orderByChild("name").equalTo("Neck pain");

So this checks the children of /Medicine/symptoms for their name property and only returns them if they have a value equal to Neck pain.

There are two problems with this:

  1. Your JSON doesn't have a /Medicine/symptoms. Instead you have a Medicine, where each child node has a symptoms node.
  2. Even if it did, your symptoms child doesn't have a value Neck pain. Instead you have an array, where each value may be Neck pain.

The closest you can now get to the query you want is:

mDatabase.child("Medicine").orderByChild("symptoms/0/name").equalTo("Neck pain");

This query returns medicines for which the first symptom name is equal to Neck pain. Firebase cannot perform a query across all array members to see if it contains a specific value in any position.

As usual with NoSQL databases: if you can't perform the use-case you want with your current data structure, you can typically change/expand your data structure to allow the use-case. And usually this is done by very directly mapping what you want to show on your screen to the structure in the database.

Your current data structures allows you to efficiently look up the symptoms (and other data) for a given medicine. That's great. It does however not allow you to efficiently look up the medicines for a given symptom. To allow that you can for example add a structure that maps each specific symptom back to its medicines:

symptoms
"Neck pain"
-L6hb2...bRb0: true
-L6rW...Fuxkf: true

With this additional structure (known as an inverted index or reverse index) you can now look up the medicines for Neck pain by simple loading /symptoms/Neck pain.

For more on this approach to categorization, see my answer here: Firebase query if child of child contains a value

Firebase search by child value

You can use equalTo() to find any child by value. In your case by name:

ref.child('users').orderByChild('name').equalTo('John Doe').on("value", function(snapshot) {
console.log(snapshot.val());
snapshot.forEach(function(data) {
console.log(data.key);
});
});

The purpose of orderByChild() is to define the field you want to filter/search for. equalTo() can get an string, int and boolean value.

Also can be used with auto generated keys (pushKey) too.

You can find all the documentation here

Firebase last 10 data retrieving from database in Cloud Function

Using send(), end() or redirect() will terminate a HTTP Cloud Function:

Always end an HTTP function with send(), redirect(), or end(). Otherwise, your function might to continue to run and be forcibly terminated by the system.

In your example, you are calling res.status(200).send(data.val()); inside your forEach iteration of each child snapshot, therefore it will only get a chance to send the one response.

Likewise, because you have used a child_added event listener, it is triggered once for every child at the specified path.

If you need to respond with all the query data at once, you'll be better off with a value event listener instead, which will retrieve all data from your query in a single response:

exports.viewdata = functions.https.onRequest((req, res) => {
const userId = req.query.user;
admin.database().ref('users/' + userId)
.orderByKey()
.limitToLast(10)
.on('value', function(snapshot) {
res.status(200).send(snapshot.val());
});
});

However, if your intention is to build a response with each child separately, you could use res.write() to write data to the response, and then eventually send this with end():

exports.viewdata = functions.https.onRequest((req, res) => {
const userId = req.query.user;
admin.database().ref('users/' + userId)
.orderByKey()
.limitToLast(10)
.on('value', function(snapshot) {
snapshot.forEach(function(data) {
res.write(data.val());
});
res.status(200).end();
});
});

Or, you could add them to a list before sending them all back as a response. The method you take here all depends on your end-goal though.


Unrelated to your initial question, but for completeness, please see the below observations and side notes, from comments:

  • The return statement for admin.database().ref() is not required for HTTPS triggers as they have a different lifecycle than other triggers and do not need to return promises.

  • If you have no need to listen for further changes after you've retrieved the necessary data, you should consider using once() (to read data once) instead of on() or removing the on() listener with off().

Get last node in Firebase database Android

Try this:

DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
Query lastQuery = databaseReference.child("mp").orderByKey().limitToLast(1);
lastQuery.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
String message = dataSnapshot.child("message").getValue().toString();
}

@Override
public void onCancelled(DatabaseError databaseError) {
// Handle possible errors.
}
});

Hope this helps!

How to delete all but most recent X children in a Firebase node?

Keep the most recent N items, is one of the trickier use-cases to implement. If you have any option to change it into "keep items from the past N hours", I recommend going that route.

The reason the use-case is tricky, is that you're counting items and Firebase does (intentionally) not have any count-based operations. Because of this, you will need to retrieve the first N items to know which item is N+1.

ref.child('lines').once('value', function(snapshot) {
if (snapshot.numChildren() > MAX_COUNT) {
var childCount = 0;
var updates = {};
snapshot.forEach(function (child) {
if (++childCount < snapshot.numChildren() - MAX_COUNT) {
updates[child.key()] = null;
}
});
ref.child('lines').update(updates);
}
});

A few things to note here:

  • this will download all lines
  • it performs a single update() call to remove the extraneous lines

One way to optimize this (aside from picking a different/time-based truncating strategy) is to keep a separate list of the "line ids".

lineids
--K3qx02jslkdjNskjwLDK
--K3qx23jakjdz9Nlskjja
--K3qxRdXhUFmEJdifOdaj

So you'll still keep the data for each line in lines, but also keep a list of just the ids. The code to then delete the extra ones then becomes:

ref.child('lineids').once('value', function(snapshot) {
if (snapshot.numChildren() > MAX_COUNT) {
var childCount = 0;
var updates = {};
snapshot.forEach(function (child) {
if (++childCount < snapshot.numChildren() - MAX_COUNT) {
updates['lineids/'+child.key()] = null;
updates['lines/'+child.key()] = null;
}
});
ref.update(updates);
}
});

This last snippet is slightly more involved, but prevents from having to download all lines data by just downloading the line ids.

There are many variations you can choose, but I hope this serves as enough inspiration to get started.

How to get the last 50 element from Firebase in a ListView?

Here is something from Firebase Documentation

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
Query recentPostsQuery = databaseReference.child("posts")
.limitToFirst(100);

https://firebase.google.com/docs/database/android/lists-of-data#filtering_data



Related Topics



Leave a reply



Submit