I've tried many methods such as
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
const docRef = db.collection("users").doc(dynamicDocID).get()
const docRef = db.collection("users").doc(dynamicDocID)
as well as many other and keep getting undefined or a promise that never seems to be resolved
Cant seem to find proper docs on this if anything
Since Cloud Functions for Firebase are written in Node.js, have a look at the Node.js examples in the Firestore documentation.
Based on that:
const docRef = db.collection("users").doc(dynamicDocID)
const document = await docRef.get()
console.log(document.id, document.data())
Or if you can't use await:
const docRef = db.collection("users").doc(dynamicDocID)
return docRef.get().then((document) => {
console.log(document.id, document.data())
})
Related
Hi,
I have a problem with downloading all collections from the document. I would like after finding the id (userUid) document to be able to download all its collections, I need the id of each of these collection
export const getAllMessagesByUserId = async (userUid) => {
const result = await firebase
.firestore()
.collection('messages')
.doc(userUid)
.onSnapshot((snapshot) => {
console.log(snapshot);
});
};
I wrote an article which proposes solutions to this problem: How to list all subcollections of a Cloud Firestore document? As a matter of fact, "retrieving a list of collections is not possible with the mobile/web client libraries" as explained in the Firestore documentation.
I would suggest you use the second method proposed in the article, using a Cloud Function.
Here is the code copied from the article.
Cloud Function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.getSubCollections = functions.https.onCall(async (data, context) => {
const docPath = data.docPath;
const collections = await admin.firestore().doc(docPath).listCollections();
const collectionIds = collections.map(col => col.id);
return { collections: collectionIds };
});
Example of calling the Cloud Function from a web app:
const getSubCollections = firebase
.functions()
.httpsCallable('getSubCollections');
getSubCollections({ docPath: 'collectionId/documentId' })
.then(function(result) {
var collections = result.data.collections;
console.log(collections);
})
.catch(function(error) {
// Getting the Error details.
var code = error.code;
var message = error.message;
var details = error.details;
// ...
});
I was using the chaining mode of the Firestore Web 8, but I'm in the way of updated it to Module 9 and have been a hard time trying to figure out how to get all the content of my subcollection (collection inside my collection).
My older function is like this and works fine:
function getInfo(doc_name) {
let infoDB = db
.collection("collection_name")
.doc(doc_name)
.collection("subcollection_name")
.get();
return alunoHistorico;
}
so with the module way I tried this code
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const docRef = doc(db, "collection_name", "doc_name");
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!");
}
but the function doc() expects a even arguments (not counting the db argument) so if I try to use with 3 arguments like this, I get a error:
const docRef = doc(db, "collection_name", "doc_name", "subcollection_name");
to it work I have to pass the exactly document that is inside the subcollection
const docRef = doc(db, "collection_name", "doc_name", "subcollection_name", "sub_doc");
but it doesn't work for me because I have a list os docs inside the subcollection, that I want o retrieve.
So how can I get all my docs inside my subcollection?
Thanks to anyone who take the time.
You need to use collection() to get a CollectionReference instead of doc() which returns a DocumentReference:
const subColRef = collection(db, "collection_name", "doc_name", "subcollection_name");
// odd number of path segments to get a CollectionReference
// equivalent to:
// .collection("collection_name/doc_name/subcollection_name") in v8
// use getDocs() instead of getDoc() to fetch the collection
const qSnap = getDocs(subColRef)
console.log(qSnap.docs.map(d => ({id: d.id, ...d.data()})))
I wrote a detailed answer on difference between doc() and collection() (in V8 and V9) here:
Firestore: What's the pattern for adding new data in Web v9?
If someone want to get realtime updates of docs inside sub collection using onSnapshot in Modular Firebase V9, you can achieve this like:
import { db } from "./firebase";
import { onSnapshot, collection } from "#firebase/firestore";
let collectionRef = collection(db, "main_collection_id", "doc_id", "sub_collection_id");
onSnapshot(collectionRef, (querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log("Id: ", doc.id, "Data: ", doc.data());
});
});
When I use Firebase Cloud Functions in my Flutter app to create a document inside a collection it works:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.onCreatePost = functions.firestore
.document("/posts/{postId}")
.onCreate(async (snap, context) => {
const doc = snap.data()
const creatorId = doc.creatorId
admin.firestore().collection('feeds').doc(creatorId).set({
Id: creatorId,
isRead: false,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
});
But when I try to add the same document inside a subcollection in that document, it does not work:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.onCreatePost = functions.firestore
.document("/posts/{postId}")
.onCreate(async (snap, context) => {
const doc = snap.data()
const creatorId = doc.creatorId
admin.firestore().collection('feeds').doc(creatorId).collection('feedItems').doc(context.params.postId).set({
Id: creatorId,
isRead: false,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
});
What am I doing wrong? I do see that the cloud function was completed successfully in the logs, but the docment is not created in my Cloud Firestore.
I would expect neither function to work reliably, because you aren't returning a promise that resolves after the asynchronous work is complete. If you don't return a promise, then Cloud Functions might terminate your function before it's done.
Minimally, you should return the promise returned by set().
return admin.firestore()
.collection('feeds')
.doc(creatorId)
.collection('feedItems')
.doc(context.params.postId)
.set(...)
You should also check the Cloud Functions log for errors. Errors will not show up in your app since the code is running completely outside of it.
I suggest also reviewing the documentation on this.
I have a problem that's bugging me for days. I am trying to create a Firebase Cloud function that reads from the Firestore database.
My Firestore DB looks like this:
Problem is that I cannot list users like this:
db.collection('users').get().then((snapshot) => snapshot.forEach(...));
If I try to do this I get empty response, like there are no users in my users collection.
But I try to access user directly it works:
await db.collection('users/5CZxgu8nmNXu2TgplwOUdOIt8e33/receipts').get()
My complete code:
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.cat = functions.https.onRequest(async (req, res) => {
const receiptList: any = [];
const db: Firestore = admin.firestore();
const usersRef = await db.collection('users').get();
console.log(usersRef.empty); // Returns true
const receiptsRef = await db
.collection('users/5CZxgu8nmNXu2TgplwOUdOIt8e33/receipts')
.get();
receiptsRef.forEach((receipt: any) => {
console.log(receipt);
receiptList.push(receipt);
// Here I can access data
});
res.send(receiptList);
return '';
});
Does anyone have any idea what I'm doing wrong? Thank you!
Your users collection is actually empty. See how the document IDs are shown in italics? That means there is not actually a document in its place, however, there are subcollections with documents organized underneath them.
When you query a collection, you only get the documents that are immediately within that collection. A query will not pick up documents organized in subcollections. In this respect, queries are said to be "shallow". As you've seen, you need to reach deeper into the subcollection to get its documents.
Bottom line is that the queries you're showing are doing exactly what they're supposed to do.
Thanks again Doug for your help.
I manage to solve my problem. Here is my complete solution.
import * as functions from 'firebase-functions';
import {
Firestore
} from '#google-cloud/firestore';
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.cat = functions.https.onRequest(async (req, res) => {
const receiptList: any = [];
const db: Firestore = admin.firestore();
const receipts = await db.collectionGroup('receipts').get();
receipts.forEach((doc: any) => {
console.log(doc.id, ' => ', doc.data());
receiptList.push(doc.data());
});
res.send(receiptList);
return '';
});
.get() gets all documents. In your case those documents are empty therefore .get() doesn't consider them.
The simplest solution that I found for this is to replace .get() with .listDocuments(). Now you could read each doc entry like you would a doc.
I am following a tutorial where I am adding some Firebase Cloud Functions to my project (step 5). I have successfully deployed my cloud function to firebase but nothing happens when I add a new product manually in the Firebase Database console. I discovered that the Firebase cloud function is triggered but it is getting an error: "TypeError: Cannot read property 'productId' of undefined"
What am I doing wrong?
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate(event => {
const docId = event.params.productId; // <-- error here
const name = event.data.data().name;
const productRef = admin.firestore().collection('products').doc(docId)
return productRef.update({ message: `Nice ${name}! - Love Cloud Functions`})
});
That tutorial must be out of date. Some things have changed in the Functions SDK when it released version 1.0. You can read about those changes here.
Database triggers are now passed two parameters instead of one. The new context parameter contains the value of wildcards in the reference path:
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate((snapshot, context) => {
const docId = context.params.productId;
If you want to continue with that tutorial, you'll have to manually convert all of its old stuff to new stuff.
OK. So thanks to Dough Stevensson's answer notifying me that the syntax was old I have now a solution:
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();
exports.sendMessage = functions.firestore
.document('products/{productId}')
.onCreate((snapshot, context) => {
const docId = context.params.productId;
const productRef = db.collection('products').doc(docId)
return productRef.update({ message: `Nice ${name}!`})
});