collection name user and a collection name query.
each user document holds a map name seenQueries with string (queryID) : number.
given a user and a queryID (key) i wish to update its number (the value). the rest of the documents fields should remain untouched.
function updateQuerySeen(userID, queryId) {
var userRef = db.collection("users").doc(userID);
var queryRef = db.collection("query").doc(queryID);
const promise1 = userRef.get();
const promise2 = queryRef.get();
Promise.all([promise1, promise2]).then((values) => {
doc1 = values[0];
doc2 = values[1];
curMsgArrayLen = doc2.data().msgArrayLen;
// Uupdate seenQueries map key:queryID
userRef.update({
seenQueries.queryID: curMsgArrayLen; // DOESN'T SEEM TO WORK
}).then(function () {
//console.log("setDocument);
})
.catch(function (error) {
console.error("UPDATE user's seen query failed ", error);
});
});
looked at google API, but it doesn't seem to work https://firebase.google.com/docs/reference/rules/rules.Map
saw this answer Update individual map in cloud firestore document but wasn't sure which one fits my objective, and this one How to update fields in Firestore map didn't work
thank you!
Related
Document Reference is used to get document field and its collections from firestore. Following are some examples:
1. Function to read data from field which is having docRef
[Firestore schema]: https://i.stack.imgur.com/pEPK5.png
Here in collection people there is doc named user1 which have a field named hlpr which have docRef for user2 so if i want to access that docRef data i will use following code:
function foo4() {
var user1 = db.collection('people').doc('user1')
user1.get().then((data) => { //get user1 whole doc using get()
var t = data.get('hlpr') //choose field 'hlpr'
t.get().then((doc) => { //get 'hlpr' data() from get()
var user2 = doc.data() //get whole doc from user2
console.log(user2.name) //output field 'name' from user 2
})
})}
2. Read data from array from of docRef. In previous image you can see field named 'cts' which is having array of docRef. Code:
function foo3() { //function to get data from array of docRef
var user1 = db.collection('people').doc('user1'); //choose user1 doc
user1.get().then((doc) => { //get data() of user1 using get()
var contacts = doc.get('cts'); //set var k to field 'cts'
contacts.forEach((data) => { //for each item in array field cts
var userx = data.get(); //read data() using get()
userx.then((doc) => {
var frnd = doc.data(); //set frnd to each data we get from doc
console.log(frnd.name) //output their any field value here i have chosen name
});
});
})}
NOTE: Above code works correctly and you can also use data to put into array also above code might not be best way to get data, but i am a beginner so this is the best i could do.
You can break your code down to a chain of awaits per the comments but a promise chain if processing one item can be fairly clean.
async function foo4() {
var user2 = await db.collection('people').doc('user1').get()
.then(data => data.get('hlpr'))
.then(doc=> doc.data())
.finally(result => result)
.catch(console.log());
you can do some clean code when you nest promise chains.
The second code block has potential errors, if your client exceeds pending 50 documents the client modules will throw errors and fail reads.
I have a problem like in the title. When I want to download my 'hives' using collectionGroup, this error pops up and I don't know what I can do to make it work.
enter image description here
And my code:
async getHives() {
await fb.usersCollection
.doc(fb.auth.currentUser.uid)
.collectionGroup('hives')
.onSnapshot(snapshot => {
let hivesArray = [];
snapshot.forEach(doc => {
let hive = doc.data();
hive.id = doc.id;
hivesArray.push(hive);
});
store.commit('setHives', hivesArray);
});
},
A collection group query can't be contained within a specific document - there is no API for that. It can only be performed across the entire database:
firebase.firestore().collectionGroup("hives")
If you want to filter all those documents to only receive certain ones, you can do that just like any other query.
firebase.firestore().collectionGroup("hives")
.where("someField", "==", true)
I am faced with the problem of retrieving two data values of a single node from my firebase database and reference it in my javascript file but don't know how to go about it. I have been able to retrieve just one data value from a node (in this case "message") but I would like to add "from" as well. Most tutorials just reference one so I am really confused. So how do I get multiple data values?
This is my code...
JS file
exports.sendNotification7 = functions.database.ref('/GroupChat/{Modules}/SDevtChat/{SDevtChatId}/message')
.onWrite(( change,context) =>{
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
var str = "New message from System Development Group Chat: " + eventSnapshot;
console.log(eventSnapshot);
var topic = "Management.Information.System";
var payload = {
data: {
name: str,
click_action: "Student_SystemsDevt"
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToTopic(topic, payload)
.then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return;
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
You can read from however many nodes you want in a Cloud Function. However, only one can trigger the function to run.
To read from your database use the following code:
admin.database().ref('/your/path/here').once('value').then(function(snapshot) {
var value = snapshot.val();
});
You will probably want to read from the same place that the Cloud Function was triggered. Use context.params.PARAMETER to get this information. For the example you posted your code would turn out looking something like this:
admin.database().ref('/GroupChat/'+context.params.Modules+'/SDevtChat/'+context.params.SDevtChatId+'/from').once('value').then(function(snapshot) {
var value = snapshot.val();
});
Just trigger your function one level higher in the JSON:
exports.sendNotification7 =
functions.database.ref('/GroupChat/{Modules}/SDevtChat/{SDevtChatId}')
.onWrite(( change,context) =>{
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
console.log(eventSnapshot);
var str = "New message from System Development Group Chat: " + eventSnapshot.message;
var from = eventSnapshot.from;
...
For context: I have a cron-job.org that fires an https function in my firebase project.
In this function, I have to go through all docs inside a collection and update a counter (each doc might have a different counter value). If the counter reaches a limit, I'll update another collection (independent from the first one), and delete the doc entry that reached the limit. If the counter is not beyond the limit, I simply update the doc entry with the updated counter value.
I tried adapting examples from the documentation, tried using transactions, batch, but I'm not sure how to proceed. According to transactions' description, that's the way to go, but examples only show how to edit a single doc.
This is what I have (tried adapting a realtime db sample):
function updateCounter() {
var ref = db.collection('my_collection_of_counters');
return ref.get().then(snapshot => {
const updates = {};
snapshot.forEach(child => {
var docData = child.data();
var newCounter = docData.counter+1;
if (newCounter == 10) {
// TO-DO: add to stock
updates[child.key] = null;
} else {
docData.counter = newCounter;
updates[child.key] = docData;
}
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
}
It doesn't work, collections don't have an update method. What is the best approach to updating each doc in a collection? One-by-one? Transaction? Is there an example?
PS: updateCounter is a function being called by the https trigger. Cron+trigger is working fine.
EDIT
When an item reaches the threshold, I want to update another collection, independent from the counter one. Is nested transactions a good solution?
Modified code:
function updateCounter() {
var ref = db.collection('my_collection_of_counters');
var transaction = db.runTransaction(t => {
return t.get(ref)
.then(snapshot => {
let docs = snapshot.docs;
for (let doc of docs) {
var item = doc.data();
var newCounter = item.counter + 1;
if (newCounter == 10) {
console.log("Update my_stock");
// ADD item.quantity to stock collection
}else{
t.update(doc.ref, {counter: newCounter});
}
}
});
})
.then(result => {
console.log('Transaction success');
})
.catch(err => {
console.log('Transaction failure:', err);
});
}
As you already noted yourself, you'll want to do this in a transaction to ensure that you can update the current counter value in a single operation. You can also create the new document, and delete the existing one, in that same transaction once your counter reaches its threshold. I don't see any benefit of doing this for all documents in a single transaction, since the operation on each doc seems unrelated to the others.
In a Firestore transaction, you perform the operations on a Transaction object as shown in the documentation. In your case you'd:
Get the current document with transaction.get().
Get the counter from the document.
Increment the counter.
If the new value is below your threshold:
Call transaction.update() to write the new counter value into the database
If the new value if above your threshold:
Call transaction.create on the new collection to create the document there.
Call transaction.delete on the existing document, to delete it.
For more, I recommend scanning the reference documentation for the Transaction class.
I am trying to create a field ("artistName") in a Firestore collection ("shows") that is pulling from a field ("name") in another Firestore collection ("artists"). The "shows" collection has a reference field ("artist") that points to the a document in the "artists" collection. To create the field, Im using Google Cloud Functions. Here is my code:
exports.addReferenceDataToCollection = functions.firestore
.document('shows/{showId}').onWrite(event => {
var newValue = event.data.data();
var artistId = newValue.artist.id;
var artistRef = firestore.collection('artists').doc(artistId);
return event.data.ref.set({
artistName: artistRef.get().then(doc => {
if (!doc.exists) {
console.log('No such document!');
} else {
console.log('Document data:', doc.data());
var artistName = doc.data().name;
console.log('Artist Name:', artistName);
return Promise.resolve(artistName);
}
})
}, {merge: true});
});
I cant seem to get the data out of the promise.
You need to do artistRef.get() first, then after you have the document you need, use its data in event.data.ref.set(). As you've written it now, you're assigning a promise object to the artistName property.
The general form of this type of pattern looks like this:
// First get the artist doc
return artistRef.get().then(doc => {
return event.data.ref.set({
// use the properties of the doc in here
})
})