Sort firestore documents by timestamp [duplicate] - javascript

I'm looking to order the docs that are received from Firestore from oldest to newest.
I'm using JS getTime() to set a field within the docs I'm trying to order and im using this code:
onSnapshot(collection(db, 'guides'), orderBy('timeStamp'), (snapshot) => { console.log(snapshot.docs); });
I feel like im doing everything right and have spend ages trying to figure this out.
What am i doing wrong?

The following should do the trick:
const q = query(collection(db, 'guides'), orderBy('timeStamp'));
onSnapshot(q, (querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(JSON.stringify(doc.data()));
});
});
Note that we pass only two parameters to onSnapshot() (including the callback) while you pass three.

Related

startAfter function always display the same data

I'm beginner to firestore.
My query always give the same data independently of the value i pass into startAfter() function.
here's code:
const fetchMore = async () => {
const q = query(
collection(db, 'chat-messages'),
orderBy('createdAt', 'asc'),
startAfter(23),
limit(5),
);
const querySnapshot = await getDocs(q)
querySnapshot.forEach(doc => {
console.log(doc.data())
})
}
Thanks for reading and helping!
Have a nice day
here's the console.log
I'm not sure what type the data in your startAt field is, but I guess it's a date or a Firestore Timestamp.
Pagination in Firestore is not based on numeric offsets, as you may be used to from other databases, but instead is based on so-called cursor objects: knowing the values and/or ID of the document you want to start after.
So you'll need to pass in the timestamp that you want to start after, not the number of documents you want to skip. So for example, to get all documents with a createdAt after now, you could do:
startAfter(Timestamp.now())
For more on this way of pagination, see the documentation on paginating data with query cursors.

How to use Promise.all with multiple Firestore queries

I know there are similar questions to this on stack overflow but thus far none have been able to help me get my code working.
I have a function that takes an id, and makes a call to firebase firestore to get all the documents in a "feedItems" collection. Each document contains two fields, a timestamp and a post ID. The function returns an array with each post object. This part of the code (getFeedItems below) works as expected.
The problem occurs in the next step. Once I have the array of post ID's, I then loop over the array and make a firestore query for each one, to get the actual post information. I know these queries are asynchronous, so I use Promise.all to wait for each promise to resolve before using the final array of post information.
However, I continue to receive "undefined" as a result of these looped queries. Why?
const useUpdateFeed = (uid) => {
const [feed, setFeed] = useState([]);
useEffect(() => {
// getFeedItems returns an array of postIDs, and works as expected
async function getFeedItems(uid) {
const docRef = firestore
.collection("feeds")
.doc(uid)
.collection("feedItems");
const doc = await docRef.get();
const feedItems = [];
doc.forEach((item) => {
feedItems.push({
...item.data(),
id: item.id,
});
});
return feedItems;
}
// getPosts is meant to take the array of post IDs, and return an array of the post objects
async function getPosts(items) {
console.log(items)
const promises = [];
items.forEach((item) => {
const promise = firestore.collection("posts").doc(item.id).get();
promises.push(promise);
});
const posts = [];
await Promise.all(promises).then((results) => {
results.forEach((result) => {
const post = result.data();
console.log(post); // this continues to log as "undefined". Why?
posts.push(post);
});
});
return posts;
}
(async () => {
if (uid) {
const feedItems = await getFeedItems(uid);
const posts = await getPosts(feedItems);
setFeed(posts);
}
})();
}, []);
return feed; // The final result is an array with a single "undefined" element
};
There are few things I have already verified on my own:
My firestore queries work as expected when done one at a time (so there are not any bugs with the query structures themselves).
This is a custom hook for React. I don't think my use of useState/useEffect is having any issue here, and I have tested the implementation of this hook with mock data.
EDIT: A console.log() of items was requested and has been added to the code snippet. I can confirm that the firestore documents that I am trying to access do exist, and have been successfully retrieved when called in individual queries (not in a loop).
Also, for simplicity the collection on Firestore currently only includes one post (with an ID of "ANkRFz2L7WQzA3ehcpDz", which can be seen in the console log output below.
EDIT TWO: To make the output clearer I have pasted it as an image below.
Turns out, this was human error. Looking at the console log output I realised there is a space in front of the document ID. Removing that on the backend made my code work.

Firebase Firestore collection could not retrieve - react native

Context: I have a list of doctors on Firebase Firestore and I want to retrieve data related to them.
Problem: App is behaving like collection is empty but it currently contains three doctors. I want to retrieve data in list of objects so I can access them in app. Also, if you have any other idea of retrieving the data let me know, since I haven't found any that works for me.
Here is the problematic part of code:
componentDidMount(){
firebase.firestore().collection('doctors')
.get()
.then((querySnapshot
) => {
querySnapshot.forEach(snapshot => {
const doctors = []
snapshot.forEach(doc => {
const data = doc.data()
data.id = doc.id;
doctors.push(data)
})
this.setState({doctors :doctors})
})})
}
Here is how my Firebase looks like:
I found this snippet which I believe it's your exact use case:
// retrieve a collection
db.collection('documents')
.get()
.then(querySnapshot => {
const documents = querySnapshot.docs.map(doc => doc.data())
// do something with documents
})
You may find this example, and others here. Under each snippet, you can find the original source in case you want more context for each snippet.

How to (using React JS web) and Firestore, can you find out when a chatRoom (on the Firestore Database) receives new messages?

I am trying to build an app using FireStore and React JS (Web)
My Firestore database basically has:
A collection of ChatRooms ChatRooms
Every chat-room has many messages which is a subcollection, for example:
this.db.collection("ChatRooms").doc(phone-number-here).collection("messages")
Also, every chat-room has some client info like first-name, last-name etc, and one that's very important:
lastVisited which is a timestamp (or firestamp whatever)
I figured I would put a React Hook that updates every second the lastVisited field, which means to try to record as accurately as possible on Firestore the last time I left a chat-room.
Based on that, I want to retrieve all the messages for every customer (chat-room) that came in after the last visit,
=> lastVisited field. :)
And show a notification.
I have tried from .onSnapshot listener on the messages subcollection, and a combination of Firestore Transactions but I haven't been lucky. My app is buggy and it keeps showing two, then one, then nothing, back to two, etc, and I am suffering much.
Here's my code!
Please I appreciate ANY help!!!
unread_messages = currentUser => {
const chatRoomsQuery = this.db.collection("ChatRooms");
// const messagesQuery = this.db.collection("ChatRooms");
return chatRoomsQuery.get().then(snapshot => {
return snapshot.forEach(chatRoom => {
const mess = chatRoomsQuery
.doc(chatRoom.id)
.collection("messages")
.where("from", "==", chatRoom.id)
.orderBy("firestamp", "desc")
.limit(5);
// the limit of the messages could change to 10 on production
return mess.onSnapshot(snapshot => {
console.log("snapshot SIZE: ", snapshot.size);
return snapshot.forEach(message => {
// console.log(message.data());
const chatRef = this.db
.collection("ChatRooms")
.doc(message.data().from);
// run transaction
return this.db
.runTransaction(transaction => {
return transaction.get(chatRef).then(doc => {
// console.log("currentUser: ", currentUser);
// console.log("doc: ", doc.data());
if (!doc.exists) return;
if (
currentUser !== null &&
message.data().from === currentUser.phone
) {
// the update it
transaction.update(chatRef, {
unread_messages: []
});
}
// else
else if (
new Date(message.data().timestamp).getTime() >
new Date(doc.data().lastVisited).getTime()
) {
console.log("THIS IS/ARE THE ONES:", message.data());
// newMessages.push(message.data().customer_response);
// the update it
transaction.update(chatRef, {
unread_messages: Array.from(
new Set([
...doc.data().unread_messages,
message.data().customer_response
])
)
});
}
});
})
.then(function() {
console.log("Transaction successfully committed!");
})
.catch(function(error) {
console.log("Transaction failed: ", error);
});
});
});
});
});
};
Searching about it, it seems that the best option for you to achieve that comparison, would be to convert your timestamps in milliseconds, using the method toMillis(). This way, you should be able to compare the results better and easier - more information on the method can be found in the official documentation here - of the timestamps of last message and last access.
I believe this would be your best option as it's mentioned in this Community post here, that this would be the only solution for comparing timestamps on Firestore - there is a method called isEqual(), but it doesn't make sense for your use case.
I would recommend you to give it a try using this to compare the timestamps for your application. Besides that, there is another question from the Community - accessible here: How to compare firebase timestamps? - where the user has a similar use cases and purpose as yours, that I believe might help you with some ideas and thoughts as well.
Let me know if the information helped you!

How to write data to cloud Firestore using cloud Functions

For a scheduling app I'm building in Flutter I'm trying to write data to my cloud Firestore database with cloud functions and cron jobs to make my app more self-sustaining. I'm stuck at the first step, which is trying to get my cloud function to write data to my cloud Firestore database.
Below I've included links to pictures on github of how my current data is structured in Firestore. What I want is to add named documents to the collection 'days' with a subcollection of 'hours', in which there are more named documents with the fields 'hour' and 'reserved'.
Picture 1: https://github.com/winckles/rooster/blob/master/Schermafbeelding%202019-11-07%20om%2014.27.55.png?raw=true
Picture 2: https://github.com/winckles/rooster/blob/master/Schermafbeelding%202019-11-07%20om%2014.28.26.png?raw=true
Below I have also included my try on getting data in Firestore.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// admin.firestore().collection('days').add({original: original}). then(writeResult => {
//
// });
exports.updateData = functions.https.onCall((data, context) => {
const days = admin.firestore().collection('days');
return days.doc(data['2019-11-08/hours/001']).set({
hour: '08:00-09:00',
reserved: '',
});
});
Ideally I would want the cloud function to add 14 documents (eg. 2019-11-06 to 2019-11-19) to the collection 'days' at the same time. Those documents would then each have a subcollection 'hours' with the data from the second screenshot (so the documents 001, 002, 003 etc. with the fields hour and reserved).
I read the documentation on cloud functions and mostly found triggers when data is written to Firestore, but this is not what I want. I also tried the quickstart function samples but with no success. It shouldn't be too hard but I can't seem to figure it out. I'm using Javascript to write the cloud function in. Any help/tips would be greatly appreciated!
You didn't specify exactly what wasn't working for you. However, the first thing I notice is that you're not calling initilizeApp properly from the cloud functions context. For cloud functions, you don't need any parameters, as the default credentials should work for you (unless you have done something very unusual already).
Here is a cloud function that will model the behavior you want. It does not do the full behavior, as I found that writing the date handling code would likely distract from the main part of the problem you are asking about, which is the firestore and functions code itself.
Likewise, this uses an https function (as it is a bit easier for me to test :), to use a callable function (or any other function type, e.g. a scheduled function if you're using a cron job) you would need to adjust it slightly (e.g., for a callable function you would need to change the declaration back to onCall and changing the final .then() call to return the value you want to return).
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.doIt = functions.https.onRequest((request, response) => {
const days = db.collection('days');
const writePromises = [];
['2019-11-08', '2019-11-09'].forEach((day) => {
const documentReference = days.doc(day);
writePromises.push(documentReference.set({reserved: ''}));
const hoursReference = documentReference.collection('hours');
const dataMap = { '001': '08:00-09:00',
'002': '09:00-10:00',
'003': '10:00-11:00',
'004': '11:00-12:00' };
Object.keys(dataMap).forEach((hour) => {
writePromises.push(hoursReference.doc(hour).set({
hour: dataMap[hour],
reserved: ''
}))
});
});
return Promise.all(writePromises)
.then(() => { response.send('ok'); })
.catch((err) => { console.log(err); });
});
Note that when you write this entire structure, you will be billed for a write to each document. There isn't really a way to avoid that though, as writes are billed per document, not per request.
Likewise, you may want to consider doing this as a batched write -- the above example is just showing a basic approach to writing. A batched write would put all of the documents into the database atomically. However, a batched write is limited to 500 updates, which you would hit at around 21 days of data (21 days * 24 hours). This would look very similar to the above (below is just the content of the function itself:
const days = db.collection('days');
const batch = db.batch();
['2019-11-08', '2019-11-09'].forEach((day) => {
const documentReference = days.doc(day);
batch.set(documentReference, {reserved: ''});
const hoursReference = documentReference.collection('hours');
const dataMap = { '001': '08:00-09:00',
'002': '09:00-10:00',
'003': '10:00-11:00',
'004': '11:00-12:00' };
Object.keys(dataMap).forEach((hour) => {
batch.set(hoursReference.doc(hour), {
hour: dataMap[hour],
reserved: ''
});
});
});
return batch.commit()
.then(() => { response.send('ok'); })
.catch((err) => { console.log(err); });
I do wonder a small bit about why you need to do this in a cloud function, rather than via a Firestore call directly from your app. Regardless, the above should allow you to get started.
if you wanna do a cron in firebase maybe its better if you use Google Cloud Scheduler, but be careful this approach have a different kind of facturation
exports.scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
console.log('This will be run every 5 minutes!');
return null;
});
You can learn more about this in:
https://firebase.google.com/docs/functions/schedule-functions

Categories

Resources