I’m using Firestore and querying data with orderBy
set to updatedAt
in descending
order, and limiting the results to 2 items per page (for testing purpose). However, I’m encountering unexpected behavior when updating data.
When I update the oldest item on the second page to have the latest updatedAt timestamp, it moves to the first position on the first page, effectively removing it from the second page. Additionally, the second item on the first page also gets removed.
Here’s a breakdown of the situation:
Initial state:
Page 1: ['item1', 'item2'] (no `startAfter` specified)
Page 2: ['item3', 'item4'] (starting after 'item2')
After updating item4 (updatedAt changed to latest):
Page 1: ['item4', 'item1']
Page 2: ['item3']
However, my expected outcome is:
Page 1: ['item4', 'item1']
Page 2: ['item2', 'item3']
// Query example
const query = firestore.collection('myCollection')
.where('tests', 'array-contains', `test`)
.orderBy('updatedAt', 'desc')
.limit(2)
.startAfter(startAfterVar)
.onSnapshot(snapshot => {
// Do something to store
});
Your assistance in resolving this issue is greatly appreciated. Thank you!
Updated 1:
Q: So you basically say that item2 is deleted from the database? Or isn’t just displayed?
A: Basically, item2 isn’t displayed, not deleted from DB.
Q: Can you please edit your question and add your database structure as a screenshot?
A: Data Structure:
myCollection (collection)
|
--- item1 (document)
|
--- tests: [array string]
--- updatedAt: [timestamp]
--- otherFields: ...
Updated 2:
I’m implementing infinite pagination with my query
Update 3:
Updated screenshot of Topics collection
My actual query is
// Query example
const query = firestore.collection('topics')
.where('members', 'array-contains', userID)
.orderBy('lastLoggedAt', 'desc')
.limit(2)
.startAfter(startAfterVar)
.onSnapshot(snapshot => {
// Do something to store
});
2
Answers
Disclaimer: pagination with realtime listeners is hard.
What you’re describing is the expected behavior. Starting with this data set:
When you set up the first listener, you read the two documents for
item1
anditem2
. Then you create the second listener to start afteritem2
, and that then readsitem3
anditem4
.Now you change
item4
to become the first one in the sort order. So it becomes:So the first listener now sees
item4
anditem1
. But the second listener still starts afteritem2
, so it only seesitem3
and you effectively hideitem2
.Since moving an item changes the cursor of all pages after it, you will need to reanchor all listeners to their new cursor documents. So you will need to detach all listeners and recreate them, or create additional listeners for the documents that are falling in between the existing listeners.
This is precisely why the FirebaseUI library only supports pagination without realtime listeners: pagination with realtime data updates is hard.
If I’m reading your question correctly, the problem is the use
startAfter()
which excludes the referenced document from the results.The documentation states:
If you change
startAfter()
tostartAt()
it should return item2.