How to get document ID from Firestore upon a snapshot - firebase

I am trying to get the documentId from a Firestore database.
My Firestore database looks like this:
- chatMessages
- chatId
- messageId // <-- I want to get this
- Object: { text, user, sentAt }
I am retrieving the data as follows, which works, but am unable to figure out how to get the documentId:
firebase.firestore().collection("chatMessages").doc("chatMessages").collection(chatId).get()
.then((snapshot) => {
if (!snapshot.empty) {
const data = snapshot.docs.map((doc) => {
// const id = doc.getId(); // <-- how can I get messageId here?
// return id;
});
}
})
However, I can't figure out how to get the id (messageId) for each document. I have tried the following, none of which have worked:
doc.getId()
doc.documentId
doc.documentID
How can I get the document ID of the documents I am returning as shown above?

This is pretty easy. It's just doc.id

Related

How to retrieve data based on latest timestamp

I'm currently studying how to retrieve data using Firebase specifically using Cloud Firestore. I have created a sample database like this
So the question is how we retrieve the announcements fields based on created timestamp which is created_at field in descending order?
I have followed this Order and limit data with Cloud Firestore but the syntax does not include document reference which means it only can order by document id in the collection but not by document's fields right?
Here is my current code looks like
final announcementList = <AnnouncementModel>[].obs;
getAnnouncements() async {
announcementList.value = [];
final snapshot = await firestore.collection("announcements").doc("announcements").get();
final data = snapshot.data()!['announcements'] as List<dynamic>;
for (var i = 0; i < 5; i++) {
announcementList.add(AnnouncementModel(
title : data[i]['title'],
desc : data[i]['desc'],
logo : data[i]['logo'],
link : data[i]['link'],
createdAt : data[i]['created_at'],
));
}
announcementList.refresh();
}
The expected output that I want is to retrieve only top 5 latest created announcements.
The code that I have tried but returns nothing.
test() async{
final snapshot = await firestore.collection('announcements').orderBy('created_at',
descending: true).limit(5).get();
final data = snapshot.docs.map((e) => e.data());
print('Output $data');
}
//OUTPUT
Output ()
you can try orderby with desc
take refrence from the below code
db.collection("devs")
.orderBy("timestamp", "desc")
.onSnapshot((snapshot) => {
/_ your magic goes here _/;
});
this.subscribe = firebase
.firestore()
.collection('orders')
.where('restaurant_code', '==', this.state.restaurantCode)
.orderBy('timestamp', 'desc')
.onSnapshot((docSnapshot) => {
const dataSource = [];
docSnapshot.forEach((doc) => {
dataSource.push(doc.data());
});
if (this._isMounted) {
this.setState({ dataSource });
}
});
here is link to follow might help
Firestore orderBy Timestamp DESC

Query array of object in firestore

i have a collection that is called employees, which includes documents and each document contains some data, and an array of objects that is called orgsanizations, for instance:
orgsanizations: [
{
orgId: 'org1',
registrationDate: '08/05/2021',
status: 'pending'
},
{
orgId: 'org2,
registrationDate: '12/01/2021',
status: 'approved'
}
];
I am trying to retrieve all the documents in employees that contains orgId === org1 in the orgsanizations, here is what i tried to do but keeps returning empty array.
const allEmployees = async () => {
const employeesList = db.collection('employees');
const snapshot = await employeesList
.where('orgsanizations', 'array-contains', { orgId: 'org1' })
.get();
if (snapshot.empty) {
console.log(snapshot.empty);
} else {
snapshot.forEach((doc) => {
console.log(doc.data());
});
}
};
};
Is there a solution for this or should start considering changing the structure to something else?
Thanks in advance
You can't check for the contents of a map, using array-contains. There are a couple of solutions for this...
Create a second array called orgIds, which contains only the orgId strings. You can then find any documents which contain these orgIds. To achieve this, you will need to write the orgId into the map AND the orgIds array.
Create an organizations sub-collection of your employee document and use a collectionGroup query.
{
organizations: [
{orgId: 'org1', registrationDate: '08/05/2021', status: 'pending'},
{orgId: 'org2', registrationDate: '12/01/2021', status: 'approved'}
],
orgIds: ['org1', 'org2']
}
const employeesList = db.collection('employees');
const snapshot = await employeesList
.where('orgIds', 'array-contains', 'org1')
.get();
You may also want to change your registrationDate to either a Timestamp or an ISO8601 string, so that you can sort them (if needed).

How to access all documents, my all documents only have sub collection in firestore

I have create document like this in react native, I am using rnfirebase library
firestore()
.collection('WaterCanData')
.doc(EntryDate)
.collection('Entries')
.doc(values.customerName)
.set({
CustomerName: values.customerName,
CansOut: values.cansOut,
JarsOut: values.jarsOut,
EmptyCansIn: values.emptyCansIn,
JarsIn: values.jarsIn,
Bottles: values.bottles,
Ice: values.ice
})
.then(() => {
console.log('Entry added!!!!!!!!!');
})
When I try to retrieve EntryDate from WaterCanData Coellection I am not able to fetch it(Document name appears in italic font), So how should I retrive this document which contains a subcollection, Below I have attached my ss of data structure
Data structure
Data structuree
The reason your document appears in italics is because it doesn't currently exist. In Cloud Firestore, subcollections can exist without requiring their parent document to also exist.
Non-existant documents will not appear in queries or snapshots in the client SDKs as stated in the Firebase Console.
This document does not exist, it will not appear in queries or snapshots
If you want to be able to get your entry dates, you need to create the document (which can be empty).
firebase.firestore()
.collection('WaterCanData')
.doc(EntryDate)
.set({}); // an empty document
To create the document at the same time as an entry on it's subcollection, you can use a batched write like so:
const db = firebase.firestore();
const batch = db.batch();
// get references to the relevant locations
const entryDateRef = db
.collection('WaterCanData')
.doc(EntryDate);
const customerRef = entryDateRef
.collection('Entries')
.doc(values.customerName);
// queue the data to write
batch.set(entryDateRef, {});
batch.set(customerRef, {
CustomerName: values.customerName,
CansOut: values.cansOut,
JarsOut: values.jarsOut,
EmptyCansIn: values.emptyCansIn,
JarsIn: values.jarsIn,
Bottles: values.bottles,
Ice: values.ice
})
// make changes to database
batch.commit()
.then(() => {
console.log('Entry added!!!!!!!!!');
});
This will then allow you to list all of the entry dates in your database using something like:
firebase.firestore().collection('WaterCanData')
.get()
.then((querySnapshot) => {
querySnapshot.forEach(doc => {
const entryDate = doc.id;
// const customerEntriesRef = doc.ref.collection('Entries');
console.log('Entry date found: ' + entryDate);
}
});
If (as an example) you wanted to also find how many entries were linked to a given date, you would need to also query each subcollection (here the code gets a little more confusing).
firebase.firestore().collection('WaterCanData')
.get()
.then((querySnapshot) => {
const fetchSizePromises = [];
// for each entry date, get the size of it's "Entries" subcollection
querySnapshot.forEach(doc => {
const entryDate = doc.id;
const customerEntriesRef = doc.ref.collection('Entries');
// if this get() fails, just store the error rather than throw it.
const thisEntrySizePromise = customerEntriesRef.get()
.then(
(entriesQuerySnapshot) => {
return { date: entryDate, size: entriesQuerySnapshot.size }
},
(error) => {
return { date: entryDate, size: -1, error }
}
);
// add this promise to the queue
fetchSizePromises.push(thisEntrySizePromise)
}
// wait for all fetch operations and return their results
return Promise.all(fetchSizePromises);
})
.then((entryInfoResults) => {
// for each entry, log the result
entryInfoResults.forEach((entryInfo) => {
if (entryInfo.error) {
// this entry failed
console.log(`${entryInfo.date} has an unknown number of customers in its Entries subcollection due to an error`, entryInfo.error);
} else {
// got size successfully
console.log(`${entryInfo.date} has ${entryInfo.size} customers in its Entries subcollection`);
}
}
});
Using below code you can console every document id inside waterCanData collection. In your database you have only one document, then it will console your document id. (10042021)
firestore()
.collection('WaterCanData')
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(doc.id)
});
})

Get subcollection from firestore from same object - React typescript

My interface looks like this:
Interface react:
export interface UserTask {
assignee: String;
date: String;
}
export interface TaskData {
id: String;
taskName: string;
taskIcon: string;
taskLog: UserTask[]
}
Firebase Data model (Structure):
- taskData (Main Collection)
- taskIcon (fields)
- taskName (fields)
- taskLog (sub-collection)
- assignee (fields)
- date (fields)
React code to get firestore data:
import { firestore } from '../firebase';
...
function toTaskData(doc): TaskData {
if (!doc.exists) {
throw new Error("TaskData not found!");
} else {
return { id: doc.id, ...doc.data() } as TaskData;
}
}
const getTaskData = async() => {
const taskDataRef = firestore.collection('taskData');
await taskDataRef.get().then(({docs}) => {
setTaskData(docs.map(toTaskData));
});
}
Response JSON from firestore:
I am only getting an response which contains an array of the taskName and taskIcon and not the subcollection taskLog, my question is how to I retrieve the subcollection or map it to UserTask array?
Firestore queries are shallow: when you fetch a document you don't fetch the the data contained in any of its linked subcollections.
If you want to get a Firestore Document together with all the documents from one of its subcollection, you need to do two fetches: one fetch for the "parent" doc and one for all the docs of the subcollection.
For example, you can do something along the following lines, using Promise.all():
const taskDataRef = firestore.collection('taskData');
const querySnapshot = await taskDataRef.get();
const promises = querySnapshot.docs.map(doc => doc.ref.collection('taskLog').get());
const querySnapshotsArray = Promise.all(promises);
querySnapshotsArray.forEach(querySnapshot => {
// Do something with the querySnapshot
// e.g. querySnapshot.docs....
// or querySnapshot.forEach(...)
});
If you want to combine the data of one parent (a taskData doc) together with all the children taskLog docs, note that Promise.all() returns a single Promise that resolves to an array of the results of the input promises which is in the same order than the input Array. In other words, querySnapshotsArray has the same order than promises.
Note that doing so will cost the read of ALL the taskData docs and ALL the taskLog documents from ALL the taskLog subcollections. It may be less exepensive to organize your front end in such a way the subcollections are only fetched on demand (e.g. if the user clicks on a button or expands a section, etc...)

Retrieve the document ids of a nested subcollection in Firestore

I have the following structure in my Firebase Firestore:
Is it possible to query just the document ids shown in the middle column of the second image? This is my current query, which seems to me the most logical way to do it. The console.log returns the success message, but the array is empty:
async function getListofPosts() {
try{
return await useFireStore
.collection('chats')
.doc(`${postId}`)
.collection('chat')
.get().then(res => {
let theMessageList = [];
res.forEach(doc => {
theMessageList.push(doc.data(), doc.id )
});
setMessageList(theMessageList);
});
}
catch {
console.log('error on getListofPosts')
}
finally {
console.log('getListofPosts worked')
}
}
Any help very much appreciated.
Cheers, Matt
Related issue - Firestore Cloud Function empty collection
It seems like your documents under collections chats and chat are empty (document IDs are shown in italics). So to solve this you shall use collectionGroup query.
return await useFireStore
.collectionGroup('chat')
.get().then(res => {
let theMessageList = [];
res.forEach(doc => {
theMessageList.push(doc.data(), doc.id )
});
setMessageList(theMessageList);
});
Before going with collectionGroup query, I recommend you to read this query and it's limitations from here - https://firebase.google.com/docs/firestore/query-data/queries#collection-group-query

Resources