yeap, if read a title you can think that it is simple action via collection - but no -> in you we have a button "Add collection": not clear how to add this collection via code or how to receive these created collections.
Please take a look at the structure of BD in the attachment:
.
I can receive ID of a document, but not clear how to receive collection(s)/data of these collections. here i just receive the main ID, not "1" collection and his data:
const listCollections = [];
await db
.collection(table)
.get()
.then((snapshot) => {
snapshot.docs.forEach((doc) => {
debugger
const dataCollectionObject = doc.data();
dataCollectionObject.id = doc.id;
listCollections.push(dataCollectionObject);
});
});
When you receive a document, you need another query to inspect its sub-collections.
Also, I suggest not mixing async/await with then.
A basic approach
const rootSnapshot = await db.collection(table).get();
const promises = rootSnapshot.docs.map(rootDoc => rootDoc.ref.collection("1").get());
const childrenDoc = await Promise.all(promises);
Using collection group queries
see the doc
const childrenDoc = await db.collectionGroup("1").get();
If you do not know the sub-collection name
Bad luck! With Firestore you are required to know the name of your collections. You could for exemple store it in another data member:
document {
someField: ...
collectionIds: ["1", "2"] // store the sub-collection ids
"1" // a sub-collection
"2" // another sub-collection
}
Then when you retrieve such a document, inspect its data and loop on collectionIds to query deeper.
Related
I'm trying to show data from a document inside a collection between "students" and their "courses"
I'm using html, javascript and this structure but as of now I can only retrieve the ID of the document but I'd like to also access the data inside this document
async function fetchCourses() {
const studentId = firebase.auth().currentUser.uid
const courseIds = await db.collection(`students/${studentId}/attending`).get();
const courseDocs = await Promise.all(
courseIds.docs.map(doc => db.doc(`courses/${doc.id},${studentId}`).get())
);
return courseDocs.filter(doc => doc.exists).map(doc => ({ id: doc.id, ...doc.data() }),
console.log(courseDocs));
}
My console.log displays all this:
Any help is gladly appreciate
Edit
This is the data structure inside my Firebase:
The students are stored in a collection indexed by their uid and each student document contains their name and more data, but most importantly a sub-collection named attending which contains empty documents indexed by courseId, refering to a document in the courses collection
The courses are stored in another collection indexed by courseId and like in students each course document contains a title and more data, and a sub-collection named attendees which contains empty documents indexed by uid, refering to a student.
import { doc, getDoc } from "firebase/firestore";
const docRef = doc(db, "cities", "SF");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
above is an example from firestore docs. firestore has really specific syntax.
you can only access data of request with doc.data() syntax. All other attempts will bring you nothing or some irrelevant object(at least in my experience)
I'm trying do display data from Firestore database in my component.
This is my function:
const getData = async () => {
const data = [];
const querySnapshot = await getDocs(
collection(databaseRef, "mK7DFNJgRAPmtvgrZh7X6AOj8cR2")
);
console.log(querySnapshot);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data().Title);
data.push({
About: doc.data().About,
Title: doc.data().Title,
Who: doc.data().Who,
});
});
setData(data);
};
Collection ID = Current logged in User.
I want to display every document.
Everything works fine but insteed of passing hard-coded string here:
collection(databaseRef, "mK7DFNJgRAPmtvgrZh7X6AOj8cR2")
I would like to pass variable where I store my UID.
Is there any way to do that?
Assuming your user is logged in, you should be able to access their UID via firebase.auth().currentUser.uid. This question's answers may be useful for more information on getting the current user's ID.
With that, you should be able to do:
const querySnapshot = await getDocs(
collection(databaseRef, firebase.auth().currentUser.uid)
);
to get the current user's documents.
https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#currentuser
here's the basic premise of what im trying to accomplish here. if a user ask a question about a product i want to send a notification to other users who currently own that product. basically saying "hey, so and so has a question about this product. maybe you can help since you own it already"
each userProfile collection has a subcollection called 'notify' where notifications are stored for various things. what i need to do is sort through the userProducts and find every user who owns the product and then create a notification post in only the notify sub-collections for those specific users who own that product.
here is the basic code. the first bit works in that it does return an array of userIDs who own that product. where im struggling now is getting it to create a new doc in the Notify sub-collection for just those specific users. is this possible to do?
exports.Questions = functions.firestore
.document("/userPost/{id}")
.onCreate(async (snap, context) => {
const data = snap.data();
if (data.question == true) {
const userProducts = await db
.collection("userProducts")
.where("product", "==", data.tag)
.get();
const userData = userProducts.docs.map((doc) => doc.data().userId);
await db
.collection("userProfile")
.where("userId", "in", userData)
.get()
.then((querySnapshot) => {
return querySnapshot.docs.ref.collection("notify").add({
message: "a user has asked about a product you own",
});
});
});
Your current solution is on the right track, but there are improvements that can be made.
Use a guard pattern for the data.question == true check.
You don't need to get userProfile/<uid> as you aren't using its contents.
When changing multiple documents at once, you should consider batching them together for simpler error handling.
ref.add(data) is shorthand for ref.doc().set(data) which you can use in the batched write to create new documents.
exports.Questions = functions.firestore
.document("/userPost/{id}")
.onCreate(async (snap, context) => {
const data = snap.data();
if (!data.question) {
console.log("New post not a question. Ignored.")
return;
}
const userProducts = await db
.collection("userProducts")
.where("product", "==", data.tag)
.get();
const userIds = userProducts.docs.map(doc => doc.get("userId")); // more efficient than doc.data().userId
// WARNING: Limited to 500 writes at once.
// If handling more than 500 entries, split into groups.
const batch = db.batch();
const notificationContent = {
message: "a user has asked about a product you own",
};
userIds.forEach(uid => {
// creates a ref to a new document under "userProfile/<uid>/notify"
const notifyDocRef = db.collection(`userProfile/${uid}/notify`).doc();
batch.set(notifyDocRef, notificationContent);
});
await batch.commit(); // write changes to Firestore
});
Note: There is no special handling here for when no one has bought a product before. Consider pinging the product's owner too.
I already read few post on the topic, but for some reason I can not fetch the docs which I need. I have a collection users with auto generated id and each doc contains name and email. Here is my collection:
Please note, the ids are auto generated.
Then, what I try to do in the code is the following:
firebase.firestore()
.collection("users")
.where(
"id",
"in",
ids
)
.get()
.then((querySnapshot) => {
const people = [];
querySnapshot.forEach(documentSnapshot => {
people.push({
...documentSnapshot.data(),
key: documentSnapshot.id
});
});
console.log("people: ", people);
});
My people arrays is empty. I am pretty sure that my ids array has the correct ids. I am not sure if this part is correct:
firebase.firestore()
.collection("users")
.where(
"id",
"in",
ids
)
.get()
Is "id" the correct name of the auto generated column?
To query a document by it's ID, you should make use of firebase.firestore.FieldPath.documentId() which returns a special value that can be used with the where() filter to search by a document's ID.
The following code has been tweaked from this documented gist (Typescript/JavaScript):
function chunkArr(arr, n) {
if (n <= 0) throw new Error("n must be greater than 0");
return Array
.from({length: Math.ceil(arr.length/n)})
.map((_, i) => arr.slice(n*i, n*(i+1)))
}
async function fetchDocumentsWithId(collectionQueryRef, arrayOfIds) {
// in batches of 10, fetch the documents with the given ID from the collection
const fetchDocsPromises = chunkArr(arrayOfIds, 10)
.map((idsInChunk) => (
collectionQueryRef
.where(firebase.firestore.FieldPath.documentId(), "in", idsInChunk)
.get()
))
return Promise.all(fetchDocsPromises)
.then((querySnapshotArray) => {
const allDocumentsArray = [];
for (let querySnapshot of querySnapshotArray) {
querySnapshot.forEach(doc => allDocumentSnapshotsArray.push({...doc.data(), key: doc.id}))
}
return allDocumentsArray;
});
}
const usersColRef = firebase.firestore()
.collection("users");
const ids = [ /* ... */ ];
const docDataArray = fetchDocumentsWithId(usersColRef, ids);
If you were to use the unedited version of the gist, you would instead use:
const usersColRef = firebase.firestore()
.collection("users");
const ids = [ /* ... */ ];
const docDataArray = [];
await fetchDocumentsWithId(usersColRef, ids, (doc) => docDataArray.push({ ...doc.data(), key: doc.id }))
console.log(docDataArray)
Note: I'd avoid using the term "key" for Firestore and instead use "id". If you are using "id" in your documents, you could always use "_id" instead.
In this situation I am assuming that ids is an array that you have in your client side code and are looking to query only for those ids in firestore?
Unfortunately, when using the .where() functionality in firestore, it does not query against the document ID's but rather the content of the document.
If you wanted to use the .where() indexing capability of firestore, you would need to add a uid field to the actual document data. Once you do that, you should be able to query for only the ID's you would like.
From the firestore docs
I am trying to get a post title from firestore but somehow I could not figure out how that could be done using async await.
async getVideo(id) {
var self = this;
const ref = this.$fire.firestore
.collection("posts")
.where("ytid", "==", id)
.orderBy("createdAt", "desc");
try {
let post = await ref.get();
console.log(post.data());
} catch (e) {
console.log(e);
}
}
I tried to console log post.data() but it says post.data() is not a function.
Any help would be appreciated.
When you call ref.get(), you will get a QuerySnapshot object. This object contains zero or more DocumentSnapshot objects that contain the data from the query results. QuerySnapshot does not have a method called data(). You will have to iterate the documents using the provided API to get the DocumentSnapshots:
const qsnapshot = await ref.get();
qsnapshot.forEach(doc => {
const data = doc.data();
console.log(data);
})
You are retrieving multiple documents, so post will be a snapshot of documents which does not have a data() method.
You'll need to iterate through the snapshot to access the individual documents.
See https://firebase.google.com/docs/firestore/query-data/get-data#get_multiple_documents_from_a_collection for a quick guide or https://googleapis.dev/nodejs/firestore/latest/QuerySnapshot.html for a full reference of the QuerySnapshot type.