I'm trying to create a cloud function to trigger every time a product on my project gets updated. Here is the idea.
I have 2 collections, stores and products.
Inside the stores collection, there is a sub-collection called products that contains all the products that the store sells. The data 'gets fed' by copying specific items from the main products root collection
The idea is the my project gets a good performance as well as cost effective.
In order for this to work, I need to create a cloud function to be triggered every time a product gets modified and query all the stores that has that same product id and update the data.
I'm having a really hard time with that. Can anybody shine a light here for me? This is my cloud function.
// Exporting the function
export const onProductChange = functions.firestore
.document('products/{productId}')
// Call the update method
.onUpdate(async (snap, context) => {
// Get the product ID
const productID = context.params.productID;
// Query for all the collections with the specific product ID.
const resultSnapshot = await db.collectionGroup('products')
.where('id', '==', productID).get();
// Filter for the collections with the 'products' root and return an array.
const snaphotsInStoreSubcollection = resultSnapshot.docs.filter(
(snapshot: any) => {
return snapshot.ref.parent === 'products';
});
const batch = db.batch();
// Takes each product and update
snaphotsInStoreSubcollection.forEach((el: any) => {
batch.set(el.ref, snaphotsInStoreSubcollection.product);
});
await batch.commit();
});
error on cloud function console
Error: Value for argument "value" is not a valid query constraint. Cannot use "undefined" as a Firestore value. at Object.validateUserInput (/srv/node_modules/#google-cloud/firestore/build/src/serializer.js:273:15) at validateQueryValue (/srv/node_modules/#google-cloud/firestore/build/src/reference.js:1844:18) at Query.where (/srv/node_modules/#google-cloud/firestore/build/src/reference.js:956:9) at exports.onProductChange.functions.firestore.document.onUpdate (/srv/lib/product-update.js:29:10) at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:131:23) at /worker/worker.js:825:24 at at process._tickDomainCallback (internal/process/next_tick.js:229:7)
I would suggest you take a look at this documentation and specially in Event Triggers.
Let me know if this helps.
I think the snapshotsInStoreSubcollection.product is undefined
batch.set(el.ref, snaphotsInStoreSubcollection.product);
A snapshot is a document and its data is snapshot.data()
You cannot set undefined as a value in firestore and you are attempting to
Related
I'm using flutter and firebase. I use pagination, max 5 documents per page. How do I know if there are more documents left to get from a firestore collection. I want to use this information to enable/disable a next page button presented to the user.
limit: 5 (5 documents each time)
orderBy: "date" (newest first)
startAfterDocument: latestDocument (just a variable that holds the latest document)
This is how I fetch the documents.
collection.limit(5).orderBy("date", descending: true).startAfterDocument(latestDocument).get()
I thought about checking if the number of docs received from firestore is equal to 5, then assume there are more docs to get. But this will not work if I there are a total of n * 5 docs in the collection.
I thought about getting the last document in the collection and store this and compare this to every doc in the batches I get, if there is a match then I know I've reach the end, but this means one excess read.
Or maybe I could keep on getting docs until I get an empty list and assume I've reached the end of the collection.
I still feel there are a much better solution to this.
Let me know if you need more info, this is my first question on this account.
There is no flag in the response to indicate there are more documents. The common solution is to request one more document than you need/display, and then use the presence of that last document as an indicator that there are more documents.
This is also what the database would have to do to include such a flag in its response, which is probably why this isn't an explicit option in the SDK.
You might also want to check the documentation on keeping a distributed count of the number of documents in a collection as that's another way to determine whether you need to enable the UI to load a next page.
here's a way to get a large data from firebase collection
let latestDoc = null; // this is to store the last doc from a query
//result
const dataArr = []; // this is to store the data getting from firestore
let loadMore = true; // this is to check if there's more data or no
const initialQuery = async () => {
const first = db
.collection("recipes-test")
.orderBy("title")
.startAfter(latestDoc || 0)
.limit(10);
const data = await first.get();
data.docs.forEach((doc) => {
// console.log("doc.data", doc.data());
dataArr.push(doc.data()); // pushing the data into the array
});
//! update latest doc
latestDoc = data.docs[data.docs.length - 1];
//! unattach event listeners if no more docs
if (data.empty) {
loadMore = false;
}
};
// running this through this function so we can actual await for the
//docs to get from firebase
const run = async () => {
// looping until we get all the docs
while (loadMore) {
console.log({ loadMore });
await initialQuery();
}
};
Basically the question again:
Suppose there is a collection called 'People'.
Firestore functions can detect changes to a particular document, but is there anyway of knowing when a document(say 'Person1') is added to the collection using firebase functions?
If not, is there any alternate way to get to know when a document is added?
There is a specific function type that only triggers when a document gets created. From the documentation comes this example of how to use it:
exports.createUser = functions.firestore
.document('users/{userId}')
.onCreate((snap, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = snap.data();
// access a particular field as you would any JS property
const name = newValue.name;
// perform desired operations ...
});
As described in firebase cloud functions documentation a trigger must always point to a document. But I want to create a trigger when any document is updated. Assume I don't know which document is updated, how I can add a trigger not knowing the document id?
Firebase cloud functions offers you the onUpdate() trigger. Just look at this post from the docs.
EDIT
This is a code example from the firebase docs. For more information look here.
exports.updateUser = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = change.after.data();
// ...or the previous value before this update
const previousValue = change.before.data();
// access a particular field as you would any JS property
const name = newValue.name;
// perform desired operations ...
});index.js
I have an Event Collection in the Firestore database like this:
I want to use cloud firestore triggers. when a user attends an event, the capacity of the event will be -1, and when this field is updated I want to automatically update another field ("rankPoint") +3
to implement this, I need to Trigger a function when a document is updated
from firestore documentation, it will be like this
exports.updateUser = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = change.after.data();
// ...or the previous value before this update
const previousValue = change.before.data();
// access a particular field as you would any JS property
const name = newValue.name;
// perform desired operations ...
});
for my case, it should be 'events/{eventId}' right? but how do I get that eventID in the wildcard? does it come from client side? I mean in iOS/Android I will write the code to update like
db.collection("eventss").document("someEventIDHere").setData(data)
is it from the client?
Your function will only be delivered the document that matched your function's pattern (users/{userId}) that was changed. Other documents are not available until you query for them. So, if you want a document from you events collection, you will have to write some code to access it, then decide what to do from there.
It sounds like you're expecting there to be an eventId wildcard, but there is not. There is just the userId wildcard that you defined in your function. Other values will need to be derived from the data you have available in your user document.
I am trying to figure out a solution for the Firestore realtime listeners. I know you can listen to changes and in onSnapshot see what was added, removed, changed.. but is there a way to just listen to additions?
I don't like how anytime there is a change in data, or a new document is added, the query retrieves every single piece of data.. Feels like unnescary data transfer.. especially if you were using the application on a 3G Network
Is that a legitimate concern? or is the query returning negligible data? I just want to get the "new" additions to the collection
Have you tried this?
exports.createUser = functions.firestore
.document('users/{userId}')
.onCreate((snap, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = snap.data();
// access a particular field as you would any JS property
const name = newValue.name;
// perform desired operations ...
});