I followed the introduction of firebase to trigger if something has changed in my Database. I just want to log the new added values, but unfortunately it doesn't work. My Database structure looks like the following:
"Chat" : {
"-LGe_0ak8UOv9jFRukpZ" : {
"-LGe_3FgL6JkM-VQsg3K" : {
"Message" : "heiii na du",
"createdByUser" : "6dB5rWfSIwez3gO0N0ClBwFJKu53",
"ts" : 1530796459
},
"-LgeqadfFEA" : {
"Message" : "yo",
"createdByUser" : "asfdasdfasdfasdf",
"ts" : 123456677
}
},
So now the last item was added to my database and normally, I should receive a log with the new value, but the method was never started. I didn't receive any error as well. Here is my Method:
exports.makeUppercase = functions.database.ref('/Chat')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime
Database.
const original = snapshot.val();
console.log('ts', context.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a
Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a
Promise.
return snapshot.ref.parent.child('ts').set(uppercase);
});
The problem is that you have not correctly specified the realtime database path
You can use a wildcard to watch for changes within all 'chats' path, otherwise from your code, the function is watching for addition of actual key-value pair within your specified path whereas your intention is to watch for creation of a child
Example : functions.database.ref('/Chat/{pushId}').onCreate((snapshot, context) =>{
//Your execution
})
This tells cloud functions to watch creation of any object within the 'chat' path
You can further access the pushId variable through context.params.pushId
Happy coding
Related
Every hour, I want my firebase function to look through my database, read a value, calculate a new value from this old value, and then update it in the database. I am having trouble accessing a snapshot of the data. Specificically,
exports.scheduledFunction = functions.pubsub.schedule('every 1 hour').onRun((context) => {
const ref = functions.database.ref('/users/test_user/commutes');
ref.once('value',function(snapshot) {
// do new calculation here
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return null;
});
I am getting a : functions: TypeError: ref.once is not a function error.
How do I access a value from my firebase real time database and update it from a Firebase function?
You're trying to use the firebase-functions SDK to query the database. It can't do that. You will have to use the Firebase Admin SDK to make the query.
You will need to get started like this (not complete, but you should be able to see what you need to do). Import and initialize at the global scope:
const admin = require('firebase-admin')
admin.initializeApp()
Then in your function, use it. Be sure to work with promises correctly.
const ref = admin.database().ref('...')
return ref.once('value').then(snapshot => {
// work with the snapshot here, and return another promise
// that resolves after all your updates are complete
})
The firebase-functions is different from the client side. The ref() function according to the docs:
ref: function
ref(path: string): RefBuilder
Select Firebase Realtime Database Reference to listen to.
Path of the database to listen to.
Returns RefBuilder
The RefBuilder will contain the database triggers that you can call, onCreate(), onWrite(). To be able to use your database, then you need to use the admin sdk.
https://firebase.google.com/docs/reference/functions/providers_database_.refbuilder
I've been struggle this for a long time and i really have no idea why this happens.
I'm using Cloud functions to send notification base on change in status. this is my code :
exports.sample = functions.database.ref('users/{pushID}/status').onUpdate(Snap => {
const key = Snap.params.pushID;
var ref = admin.database().ref('users');
return ref.child(`${key}`).on("value", function(snap) {
const category = snap.child('category').val();
console.log("category",category);
var ref = admin.database().ref('/categories/')
return ref.child(`${category}`).on("value", function(snapshot){
const tokenID = snapshot.child('tokenID').val();
const payload = {
data: {
title: 'HELLO',
text: `HOW ARE YOU ?`
}
};
return admin.messaging().sendToDevice(tokenID, payload);
})
});
After i deploy the function i change the status using the dashboard.
When i do this i get notification! (and i get "sent" on the log)
but when i change the "status" again, i get two notifications! and now i got two times "sent" on the log and so on.
It seems like a loop but i don't understand why,
whatever i log i get only one value, i don't get multiple tokens or anything, it just sends the same notification to the same user.
Whenever i re-deploy the function the "counter" resets and then the first change send only one notification.
my database looks like this :
users
-->push id
---->status=""
---->category="test"
categories
-->test
Your listeners should be attached using once() instead of on(). on() leaves the listener attached.
I went through docs, github repositories but nothing worked for me yet.
My datastructure:
App {
posts : {
<post_keys> : {
auth_name : "name",
text : "some text" //and many other fields
}
}
}
1) Github repository : If I use this, I only get one field from one function, if I need all the fields, I would need to write separate functions for each, which is a bad approach.
2) Algolia Official Docs for Node.js : This cannot be deployed as a cloud function, but it does what I intend to do.
How can I write a function that can be deployed on Firebase and gets the whole object indexed with its key in Algolia?
Okay so I went ahead to create a Firebase Cloud function in order to index all objects in the Algolia index. This is the solution:
What you were doing is something like this:
exports.indexentry = functions.database.ref('/blog-posts/{blogid}/text').onWrite(event => {
What you should do is the following:
exports.indexentry = functions.database.ref('/blog-posts/{blogid}').onWrite(event => {
const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);
var firebaseObject = event.data.val();
firebaseObject.objectID = event.params.blogid;
return index.saveObject(firebaseObject).then(
() => event.data.adminRef.parent.child('last_index_timestamp').set(
Date.parse(event.timestamp)));
});
The difference is in the first line: In the first case, you only listen to text changes, hence you only get the data containing the text change.
In the second case, you get the whole object since you listen to changes in all of the blog object (notice how /text was removed).
I tested it and it works for me: whole object including author was indexed in Algolia.
So I'm aware how I should add any regular string/object to a JSON object - however, I need to add the following Mongo command to my JSON object:
$set : { "author" : req.body.name }
As you can see, it's not as simple as just doing:
myJsonObject.author = "$set..." etc.
Sorry if this is a dupe question - this isn't the easiest subject to Google without tons of unrelated answers popping up.
The reason I need to know this because I want to be able to build a JSON object based on what the user has changed in the form. So a new problem I have encountered is that even if I can build the JSON object with multiple items to change, MongoDB only updates the last item in the object. My example JSON object is here:
updateItem = {
$set : { "name" : "Squid bonobo" },
$set : { "author" : "Mardy Bum" }
};
and the snippet of code which issues the update command is as follows:
updateData.update(searchItem, updateItem, false, true, function(err, results) {
console.log(results);
db.close();
});
I found this solution at the following git site.
Can anybody help with either of these questions?
Thanks for your help :)
Cameron
You need to form your JSON in the below structure:
updateItem = {
$set : { "name":"Squid bonobo","author":"Mardy Bum"}
}
Javascript Objects can be declared with duplicate keys/properties, but their values get over-written, with the latest bound value for the key, in any order.
Here, $set is a key/property which has been set twice to the same object referenced by the variable updateItem, hence only one value, that is last encountered, will be associated to the key. In this case, the value that takes precedence is:
$set:{ "author":"Mardy Bum" }
and the final query that gets executed becomes,
updateItem = {
$set : { "author" : "Mardy Bum" }
};
I'm creating an application in Node that has some CRUD components. On one of my data objects, I have a save() method that is meant to update a record if the object has an id that is found in the collection, or upsert a new document if not. Additionally, if doing an upsert I'd like to get back the _id for the document generated by Mongo.
It seems that findAndModify would do this, and, indeed, it does return an _id value from the database. In my query clause, I am filtering by _id. If my data object doesn't have an id, Mongo correctly does an upsert, however, no matter what _id value it returns, in addition to the the keys I am setting in the update clause, it also sets the _id on the document based on what value I used in the query clause. Some code for clarity:
User.prototype.save = function(callback) {
var that = this;
var args = {
'query' : { _id: this.getId() }, //getId() returns empty string if not set
'update' : { $set : {
firstName : this.firstName,
lastName : this.lastName,
email : this.email
//_id : this.getId()
// which is blank, is magically getting added due to query clause
}},
'new' : true,
'upsert' : true,
'fields' : {'_id' : true}
};
this.db.collection(dbName).findAndModify(args, function(err, doc){
if(!that.getId()) {
that.setId(doc._id);
}
if (typeof(callback) === "function"){
callback.call(that);
}
});
}
I'm simply looking for the semantics of update that also happens to return a Mongo-generated _id on upsert. I do not want the values of the query clause to additionally be appended as if they were in the update map. Is there any way to achieve what I am getting at?
You can generate the _id client side, with new new require('mongodb').ObjectID()
Then you can just do a regular upsert (no need to do findandmodify) because you already have the _id.
However, if you are using findAndModify, keep in mind that the node driver accepts the arguments to this function positionally, not as an object (like in the regular mongo shell).
The correct format to do findandmodify with the node driver looks like this:
collection.findAndModify(criteria, sort, update[, options, callback])
(options and callback are optional params). Full docs here:
https://github.com/mongodb/node-mongodb-native/blob/master/docs/insert.md