Firebase JS9 access subcollection with DocumentSnapshot - javascript

Just quick question.
How to do something like this in firebase javascript v9? userDoc is a DocumentSnapshot .
const postRef = userDoc.ref.collection('posts').doc(slug);
I need to access sub-collection with doc snapshot, but I am totally clueless.

You can pass the document reference to the collection function, and the collection ref that gives to the doc function:
const postRef = doc(collection(userDoc.ref, 'posts', slug));
Alternatively, you can skip the collection call here as both collection and doc accept multiple arguments:
const postRef = doc(userDoc.ref, 'posts', slug);

Related

explanation of firebase docs about query(query, queryConstraints)?

I was looking about more details about the query function in firestore in docs reference of firestore and I found this pseudo syntax :
query(query, queryConstraints)
but in all firestore examples about this function they used in the first parameter a collection reference not query
form firestore examples :
// Create a reference to the cities collection
import { collection, query, where } from "firebase/firestore";
const citiesRef = collection(db, "cities");
// Create a query against the collection.
const q = query(citiesRef, where("state", "==", "CA"));
Is it the same function or this is another Signature for it ?
A collection reference is a subclass of a query, so wherever the API expects a Query you can also pass a CollectionReference. So you can build a new query by passing a CollectionReference to the query function, or add conditions to an existing query by passing a Query to it.

How do you create a new Firestore document within a transaction

I'm trying to log changes to a collection of customer records. In order to keep things watertight,the log records should obviously be created within a Firestore transaction. I have no problems using transaction.set to apply the customer document changes, but every such change needs to be accompanied by the creation of a new document within my transactionLogs collection. Here, things are going badly wrong The log documents are identified by a timestamp field and when I run the following code: ...
import { initializeApp } from 'firebase/app';
import {
getFirestore, collection, query, getDoc,
getDocs, where, orderBy, addDoc, doc, updateDoc, deleteDoc, runTransaction
} from 'firebase/firestore';
var firebaseApp = initializeApp(firebaseConfig);
var db = getFirestore(firebaseApp);
// code to read and validate update to customer documents and to call the following asynch function
function performCustomerUpdate(parameters) {
await runTransaction(db, async (transaction) => {
// update Customer document and build a transactionLog object
const newLogRef = collection(db, 'transactionLogs', transactionLog.timeStamp);
await transaction.set(newLogRef, transactionLog);
});
}
... the transaction.set instruction fails saying something like "Invalid collection reference. Collection references must have an odd number of segments, but transactionsLogs/1645451217221 has 2." In this particular instance, 1645451217221 would have been the value of transactionLog.timesStamp.
Does anyone have advice on what is going on here and how to fix the error? My understanding is that transaction.set will create a new record if the supplied reference doesn't exist, so I ought to be on the right lines. But why does Firestore think that I want to create it in a collection called transactionsLogs/1645451217221? How do I get it the create a reference for a document identified by the string '1645451217221' in a collection called 'transactionsLogs'?
If you are specifying the document ID (and not using the auto-generated IDs) then you must use doc() instead of collection() to create a DocumentReference:
const newLogRef = doc(db, 'transactionLogs', transactionLog.timeStamp);
The collection() function is used create a CollectionReference.
Also checkout: Firestore: What's the pattern for adding new data in Web v9?
My understanding is that transaction.set will create a new record if the supplied reference doesn't exist
If the documents exists, the it'll overwrite the existing document.
With regard to the wider question of how you add records within a transaction, the answer is that you would use pretty much the same code as you'd use outside a transaction.
So whereas outside a transaction you would create a new document with a data item as its identifier with the following Web Version 9 code:
const myDocRef = doc(db, "myCollection", myDocId);
await setDoc(myDocRef, myDocData);
Inside a transaction, you use exactly the same pattern with the one exception that setDoc() is replaced by transaction.set() and the whole operation (obviously) is surrounded by a runTransaction(db, async (transaction) => { instruction:
await runTransaction(db, async (transaction) => {
const myDocRef = doc(db, "myCollection", myDocId);
await transaction.set(myDocRef, myDocData);
}).catch((error) => {
alert(`Oops - transaction failed - error is : ${error}`);
});
Similarly, the pattern used to create a document with an automatically-generated id:
const myCollectionRef = collection(db, "myCollection");
const myDocRef = doc(myCollectionRef)
await setDoc(myDocRef, myDocData);
is replaced within a transaction by
const myCollectionRef = collection(db, "myCollection");
const myDocRef = doc(myCollectionRef)
await transaction.set(myDocRef, myDocData);
Note that if you find you need to find the value that's be assigned to your automatically-generated id, this is available as myDocRef.id

Cloud functions cron job: How to manage the nested collection's data

I want to trigger the scheduled task to the nested collection data using cloud functions cron job.
What I want to achieve is, if the timestamp value in "limit" field is later than the current time, "status" field will be added with the value "expired" in the nested collection.
The parent collection name is "OrdersUser" and the child collection name is "Orders".
And I want to manage the documents in "Orders" collection.
The scheduled task runs every one minute.
After I deployed, I got an error in the firebase console.
id is not defined.
I thought I could use wild card in the cloud functions, so I used {id} to refer the nested documents. But I couldn't.
I want to query all the data in ”Orders” collection.
How can I fix this issue?
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()
const db = admin.firestore()
const ref = functions.firestore
exports.statusOrdersUser = functions.runWith( { memory: '2GB' }).pubsub.schedule('* * * * *').onRun((context) => {
// Consistent timestamp
const now = admin.firestore.Timestamp.now();
// Query all documents ready to perform
//id is not defined.
const queryOrdersUser = db.collection('OrdersUser').doc({id}).collection('Orders').where('limit', '<=', now)
return queryOrdersUser.get()
.then(querySnapshot => {
if (querySnapshot.empty) {
return null;
} else {
const promises = []
querySnapshot.forEach(doc => {
promises.push(doc.ref.update({ status: 'expired' }));
});
return Promise.all(promises);
}
});
})
Firestore does not support wildcard operations for document IDs. If you want to query a subcollection, you need to provide the specific ID of the document where it is nested. It's not clear how your function is supposed to know exactly which subcollection to query, since it doesn't receive any arguments or have any context.
If you want to query all of the subcollections named "Order", no matter where they are nested anywhere in your database, you can use a collection group query for that.

Firebase realtime database get parent node value .val()

I have the below code that looks for a specific update in my /users object tree.
This works fine, but seems a bit redundant, since I already have a reference to a child of the user node that I want.
exports.onUserKeyHelloCreate = functions
.region('europe-west1')
.database
.ref('users/{userId}/hello')
.onCreate(async (snapshot, context) => {
const userId = context.params.userId
let userData = null
await database.ref(`users/${userId}`).once('value', (userSnapshot)=>{
userData = userSnapshot.val()
})
console.log('userData', userData)
Is there a more elegant way to access the entire user node that does not require an extra roundtrip to .once('value')?
Kind regards /K
If you need the entire contents of your database at "users/${userId}", what you're doing now is really the only way to do it.
Though you shouldn't really combine use of the callback along with the promise returned by once(). It's sufficient to just use the promise.
const snapshot = await database.ref(`users/${userId}`).once('value');
const userData = snapshot.val();
console.log('userData', userData)

Creating a Firestore document with a reference in cloud functions on update from updated item

I have this function that listens to updates on Firestore Document via functions.firestore.document().onUpdate().
Here is a snippet of how I'd like to approach it:
exports.createItem = functions.firestore
.document('list/{itemId}')
.onUpdate((change, context) => {
const ref = REFERENCE_TO_DOCUMENT_CHANGED; // how to do this?
const changedData = change.after.data();
const changedValueId = changedData.id;
const create = admin.firestore().doc(`newlist/${changedValueId}`);
create.set({
item: ref,
...changedData
});
});
From the Cloud Functions documentation on Firestore onUpdate trigger it seems that the updated document is available in change.after. The DocumentReference for that document is thus available as change.after.ref (or change.before.ref, since change.before and change.after refer to different snapshots of the same document).

Categories

Resources