How to Properly Order Data from Firebase Chronologically

How do you properly order data from Firebase chronologically

Using only reverse() for your array is not enough way to encompass everything. There are different things you need to think about:

  • Limit while retrieving data, use append() and then reverse() to save time. You don't need to delete all array for each time.

  • Scroll trigger or willDisplay cell method loading

Let's start. You can create a child for your posts timestamp or date/time being global. To provide like Instagram seconds, weeks I advice you using UTC time. So I will call this: (timeUTC)

For sorting your all post, use since1970 timestamp. So I will call this (timestamp) and then also you can keep another node as (reversedTimestamp) adding - prefix to timestamp. So when you use queryOrdered to this node. You can handle latest 5 post using with yourQuery.queryLimited(toFirst: 5).

1.Get UTC date/time for timeUTC node in Swift 3:

        let date = Date()
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = TimeZone(abbreviation: "UTC")
let utcTimeZoneStr = formatter.string(from: date)

+0000 means it's universal time, look at http://time.is/tr/UTC

2.Get since1970 timestamp to sort posts in Swift 3:

let timestamp = (Date().timeIntervalSince1970 as NSString).doubleValue
let reversedTimestamp = -1.0 * timestamp

Now, you can save them on your Firebase posts like this.

"posts" : {
"-KHLOy5mOSq0SeB7GBXv" : {
"timestamp": "1475858019.2306"
"timeUTC" : "2012-02-04 12:11:56 +0000"
},
"-KHLrapS0wbjqPP5ZSUY" : {
"timestamp": "1475858010.1245"
"timeUTC" : "2014-02-04 12:11:56 +0000"
},

I will retrieve five by five post, so I'm doing queryLimited(toFirst: 5) in viewDidLoad:

let yourQuery = ...queryOrdered(byChild: "reverseTimestamp")
.queryEnding(atValue: "\(self.pageOnTimestamp)", childKey: "reverseTimestamp")

yourQuery.observeSingleEvent(of: .value, with: { (snapshot) in

if snapshot.value is NSNull {

print("There is no post.")

}
else {

yourQuery.queryLimited(toFirst: 5).observeSingleEvent(of: .value, with: { (snapshot) in

self.posts.removeAll(keepingCapacity: true)

for (i, snap) in snapshot.children.enumerated() {

if let postAllDict = snapshot.value as? [String: AnyObject] {
if let postDict = postAllDict[(snap as AnyObject).key as String] as? [String: AnyObject] {

let post = Post(key: (snap as AnyObject).key as String, postDict: postDict)
self.posts.append(post)
}
}
}

completion(true)
})
}
})

If user reached latest post, you can handle it with willDisplay method like below, then you can call loadMore function.

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {

if self.posts.count - 1 == indexPath.row {
// call loadMore function.
}
}

In loadMore() function you can handle latest post's timestamp, then start-end query as with that, so you can easily continue with next first 5 posts while appending before array.

For Swift 3 conversion as nice formatted, take a look here: Swift 3 - UTC to time ago label, thinking 12h / 24h device time changes

How to properly order data chronologically in my Firebase infinite scroll?

If I understood you correctly, you don't have to use end. This could be achieved using startAt + limit

fb.orderByChild('id').startAt(_start).limit(n)...

And use a layout to order items descending. Using this you don't have to worry about sequence of ids if they are consecutive

UPDATE

You can do this even simpler. Just update your start with a latest value

fb.orderByChild('id').startAt(_start).limit(_n).on("child_added", function(dataSnapshot) {

$scope.data.push(dataSnapshot.val());
$scope.$apply()
_start = dataSnapshot.child('id').val()
console.log("THE VALUE:"+$scope.data);

});

In this case your _start will always hold the last id and you may not worry about deleted elements.
Alternatively you may use a temporary variable to store last id value and use it in startAt

Sort data in Firebase Realtime Database in reverse chronological order

You need to store your date/time as a timestamp, i.e. the time since the Unix epoch, in milliseconds.

With the Realtime Database you can use ServerValue.TIMESTAMP, as follows:

var messagesRef = firebase.database().ref(....);
messages.push({
name: .....,
....
timestamp: firebase.database.ServerValue.TIMESTAMP
});

However, since you want to sort your messages with the "latest (message) displayed on top" (i.e. in reverse chronological order) you need to store the value of the timestamp multiplied by -1.

To do so you cannot use ServerValue.TIMESTAMP because you cannot make any math operation on this value that is populated in the back-end by the server. You should then use some standard JavaScript, as follows:

var messagesRef = firebase.database().ref(....);
var nowTimestamp = (new Date().getTime()) * -1;
messages.push({
name: .....,
....
timestamp: nowTimestamp
});

Then, to display the messages in the correct order in your app, you have to use orderByChild() as explained in the documentation, here: https://firebase.google.com/docs/database/web/lists-of-data#sort_data

How to order Firebase IDs chronologically using Javascript

You're confusing two concepts here:

  • Firebase Authentication User IDs (also known as UIDs), which are just a sequence of random characters that identify a user.

  • Firebase Database push IDs (created by calling push() or childByAutoId()), which are auto-generate, continuously increasing keys.

The push IDs of the database are inherently ordered. By UIDs from authentication are not.

If you want to have a list of Firebase Authentication users in the order in which they were created, you should store the user information in the database. While you could store them under a push ID to get them chronologically, it is more idiomatic to store the users under their UID. If you'd add a timestamp to each user though, you could still get them listed in creation order.

Samples JSON:

Users
uidOfEmily:
creationTimestamp: 1484320868847
name: "Emily"
uidOfPuf:
creationTimestamp: 1452784908658
name: "Frank"

Code to order by creation time:

ref.child("Users").orderBy("creationTimestamp").on("child_added"...

How do i order by Date Firebase

You can't fix this in code while depending on the database to perform the query. In Realtime Database, strings will always sort lexicographically - by their natural string order. You can't make it interpret the strings as something other than a normal string.

You will have to instead store an integer value that you can sort chronologically. In JavaScript, you can simply create a Date object and use its getTime() method field to get the Date's representation in millis since the unix epoch.

If you can't store a proper timestamp, then your only option is to read all the data and sort it yourself in your app, which will not scale well.

Firebase sort data by date

Your data is being sorted by the key (date) but the problem is that your key is a string and strings are sorted alphabetically. So you're going to get all the Fridays first, then Mondays, and so on.

To sort chronologically you need to use a format that represents dates as ascending numbers, ie a timestamp. Firebase has a nifty shortcut to auto-populate timestamps in a standard format when they're written to the database, but unfortunately that only applies when you need the time at which the data is created. However it's not difficult to produce a timestamp for any arbitrary date and time, and there are plenty of libraries that will convert that back and forth to any human-readable format you like in your UI. If you're only interested in the date you can probably get away with a simple sortable format like ISO-8601:

{
"events": {
"2016-05-06" : [ null, {
"Id" : 3,
"LogoImage" : "image_url",
"MainImage" : "image_url",
"Name" : "event_name"
}, {
"Id" : 4,
"LogoImage" : "image_url",
"MainImage" : "image_url",
"Name" : "event_name"
} ],
"2016-04-08" : [ null, {
"Id" : 1,
"LogoImage" : "image_url",
"MainImage" : "image_url",
"Name" : "event_name"
}, {
"Id" : 2,
"LogoImage" : "image_url",
"MainImage" : "image_url",
"Name" : "event_name"
} ]
}
}

It's also highly inadvisable what you're doing with arrays in the data, but that's for another topic.

How to sort data in Firebase?

There's a lot of code in the question and sometimes, simpler is better. So let's take a Post class, load the posts, get the associated user name and store it in an array. Then when complete, sort and print the posts in reverse chronological order.

A class to hold the post data and the user name

class PostClass {
var post = ""
var timestamp: Int! //using an int for simplicity in this answer
var user_name = ""

init(aPost: String, aUserName: String, aTimestamp: Int) {
self.post = aPost
self.user_name = aUserName
self.timestamp = aTimestamp
}
}

Note that if we want to have have both post data and user data we could do this

class PostUserClass {
var post: PostClass()
var user: UserClass()
}

but we're keeping it simple for this answer.

Then an array to store the posts

var postArray = [PostClass]()

and finally the code to load in all of the posts, get the associated user name (or user object in a full example).

let postsRef = self.ref.child("posts")
let usersRef = self.ref.child("users")
postsRef.observeSingleEvent(of: .value, with: { snapshot in
let lastSnapIndex = snapshot.childrenCount
var index = 0
for child in snapshot.children {
let childSnap = child as! DataSnapshot
let uid = childSnap.childSnapshot(forPath: "uid").value as! String
let post = childSnap.childSnapshot(forPath: "post").value as! String
let timestamp = childSnap.childSnapshot(forPath: "timestamp").value as! Int
let thisUserRef = usersRef.child(uid)

thisUserRef.observeSingleEvent(of: .value, with: { userSnap in
index += 1
//for simplicity, I am grabbing only the user name from the user
// data. You could just as easily create a user object and
// populate it with user data and store that in PostClass
// that would tie a user to a post as in the PostUserClass shown above
let userName = userSnap.childSnapshot(forPath: "Name").value as! String
let aPost = PostClass(aPost: post, aUserName: userName, aTimestamp: timestamp)
self.postArray.append(aPost) //or use self.postUserArray to store
// PostUserClass objects in an array.
if index == lastSnapIndex {
self.sortArrayAndDisplay() //or reload your tableView
}
})
}
})

and then the little function to sort and print to console

func sortArrayAndDisplay() {
self.postArray.sort(by: {$0.timestamp > $1.timestamp})

for post in postArray {
print(post.user_name, post.post, post.timestamp)
}
}

Note that Firebase is asynchronous so before sorting/printing we need to know we are done loading in all of the data. This is handled via the lastSnapIndex and index. The index is only incremented once each user is loaded and when all of the posts and users have been loaded we then sort and print as the data is complete.

This example avoids messy callbacks and completion handlers which may be contributing to the issue in the question - this piece of code is suspect and probably should be avoided due to the asynchronous nature of Firebase; the sort function is going to be called well before all of the users are loaded.

UserApi.shared.observeUserToPost(uid: userUid) { (user) in
self.postUser.append(user)
}
self.postUser.sort(by: {$0.postDate! > $1.postDate!})

*please add error checking.

Order Firestore data by TimeStamp in Ascending order

You cannot use a String (timeStamp) when querying your database instead of a Date (date) and expect to behave as it was a date. So to solve this, please change the following line of code:

firestoreDb.collection("ideas")
.orderBy("timeStamp", Query.Direction.ASCENDING)

to

firestoreDb.collection("ideas")
.orderBy("date", Query.Direction.ASCENDING)

To make it work, this kind of query requires an index. To create one, please check my answer from the following post:

  • Firestore whereEqualTo, orderBy and limit(1) not working


Related Topics



Leave a reply



Submit