How to change values inside new Firebase onUpdate or onWrite triggers? - javascript

Before version (<= v0.9.1) it was possible to set data like that and now not anymore...
event.data.ref.child('thisname').set("error");
edit: setter are not mentioned here! Only how to receive values from onUpdate or onWrite
https://firebase.google.com/docs/functions/beta-v1-diff
Solution is posted below

Firebase: (supports version 1.0 or 2.0)
If someone else has problems changing / setting / updating values in his cloud functions inside an onUpdate or onWrite trigger, then this might help u...
First this is how my data tree looks like:
"users" : {
"4h23u45h23509hu346034h5943h5" : {
"address" : "Backouse 2",
"city" : "Los Angeles",
"name" : "Joseph",
...
},
"23u4g24hg234h2ui342b34hi243n" : {
"address" : "Streetouse 13",
"city" : "Los Angeles",
"name" : "Stefan",
...
Now to the cloud functions:
Before (<= v0.9.1)
exports.updatingUser = functions.database.ref('/users/{pushId}')
.onUpdate(event => {
var address = event.data.child('address');
var city = event.data.child('city');
if (address.changed() || city.changed()) {
//generateThisname()
if (thisname == null) {
event.data.ref.child('name').set("error"); //Important Part
}
else {
event.data.ref.child('name').set(thisname); //Important Part
}
...
Now (>= v1.0.0)
exports.updatingUser = functions.database.ref('/users/{pushId}')
.onUpdate((change, context) => {
var addressBefore = change.before.child('address').val();
var addressAfter = change.after.child('address').val();
var cityBefore = change.before.child('city').val();
var cityAfter = change.after.child('city').val();
//create reference from root to users/{pushId}/
var rootSnapshot = change.after.ref.parent.child(context.params.pushId)
if ((addressBefore !== addressAfter) || (cityBefore !== cityAfter)) {
//generateThisname()
if (thisname === null) {
rootSnapshot.child('name').set("error");
}
else {
rootSnapshot.child('name').set(thisname);
}
...
So before you can set a value, you first have to make a reference from the root of your database and then go all the way down to you value and call set()

Related

Merge Multiple Objects (es5)

I have a requirement to merge two (or Multiple) Objects as a single object. While keeping each Object name as the parent key.
var event =
{ id : '45243'
, name : 'Cardiff locè'
, loc : 'Cardiff'
}
var alert =
{ node : 'sdwan edge'
, severity : 'critical'
}
The output should be like this:
var mergedObject =
{ event :
{ id : '45243'
, name : 'Cardiff loc'
, loc : 'Cardiff'
}
, alert:
{ node : 'sdwan edge'
, severity : 'critical'
}
}
mergeObject.event = event;
mergeObject.alert = alert;
That's not really merging. The existing objects are unchanged. You are just creating a new object.
var mergedObject = {
"event": event,
"alert": alert
};
var event = {
"id": "45243",
"name": "Cardiff loc",
"loc": "Cardiff"
}
var alert = {
"node": "sdwan edge",
"severity": "critical"
}
var mergedObject = {
event,
alert
};
console.log(mergedObject)

Retrieving single record from firebase while using refs

I'm currently working with the node library firebase and I want to search for a single value by using database refs.
I've been able to search for every single record on the realtime-database by returning the ref itself from my CharacterService's getAll method, but I can't seem to find a way to do it for jus a single record.
Firebase (Configuration file)
import firebase from 'firebase';
import config from '../config';
if (firebase.apps.length === 0) firebase.initializeApp(config.firebase);
const database = firebase.database();
export default database;
CharacterService
import firebase from 'config/firebase';
const db = firebase.ref('/character');
class CharacterService {
getAll () {
return db;
}
create (character) {
return db.push(character);
}
update (key, value) {
return db.child(key).update(value);
}
delete (key) {
return db.child(key).remove();
}
deleteAll () {
return db.remove();
}
}
export default new CharacterService();
Database records
{
"character" : {
"-MLEm-8ehLQ4repmeKtl" : {
"age" : 36,
"exp" : {
"current" : 0,
"toLvlUp" : 125,
"total" : 0
},
"gender" : "Male",
"level" : 1,
"name" : "Aleksandr Brightwood"
},
"-MLEm-7fhLQ5rehltKjk" : {
"age" : 22,
"exp" : {
"current" : 0,
"toLvlUp" : 125,
"total" : 0
},
"gender" : "Female",
"level" : 1,
"name" : "Sarah Autumn"
}
}
}
You can certainly get a child node using its key.
const childRef = firebase.ref('/character').child(key);
Now you have a Reference you can use to get only the data under that nested child.
I suggest reviewing the documentation for more information.

Increment counters of node and ALL parents using firebase realtime db cloud functions

I'm writing cloud functions for my firebase realtime database (not firestore) using js sdk. These functions increment counters that count the number of users per area. The areas I'm using are geographical and nested (Continent>Country>Region>City). The functions are triggered by the insertion of a user into the users list of a given area (.onCreate). If a user is inserted into the users list of let's say a country, it will automatically be added to the users list of the parent continent.
After tinkering for some time I was only able to come up with a "low-level" non-scalable solution (which by the way works). This means that I need to write additionnal code if I want to add a child area to city for example.
This is a problem because I expect to add child areas in the future.
Question: Is there a way to write a generic cloud function that increments the counters of a node and the counters of all its parent nodes?
I'm quite new to firebase and any help would be really appreciated
Json structure of firebase realtime db
{
"groups" : {
"location" : {
"continents" : {
"EU" : {
"countries" : {
"DE" : {
"created" : "2019-03-25--20:03:34",
"flag" : "/flags/de.svg",
"label" : "Germany",
"level" : 2,
"regions" : {
"BE" : {
"cities" : {
"Berlin" : {
"created" : "2019-03-25--20:03:35",
"label" : "Berlin",
"level" : 3,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:55",
"label" : "Anonymous"
}
}
}
},
"created" : "2019-03-25--20:03:35",
"label" : "Land Berlin",
"level" : 3,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:52",
"label" : "Anonymous"
}
}
}
},
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:49",
"label" : "Anonymous"
}
}
}
},
"created" : "2019-03-25--20:03:33",
"label" : "Europe",
"level" : 1,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:46",
"label" : "Anonymous"
}
}
}
}
}
}
}
My cloud functions for user insertion - still need to write for user deletion
exports.onUserAddToContinent = functions.database
.ref('/groups/location/continents/{continentID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToCountry = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToRegion = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/regions/{regionID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const regionId = context.params.regionID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/regions/' + regionId +'/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToCity = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/regions/{regionID}/cities/{cityID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const regionId = context.params.regionID
const cityId = context.params.cityID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/regions/' + regionId +'/cities/'+ cityId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
I feel 'im missing something. There has to be a generic way of doing this.

Trouble filtering data in firebase database

I'm trying to filter some data from firebase database in a cloud function.
That data looks like this :
"items": {
"id1": {
"status": {
"creation": timestampValue,
"status": "initialized"
},
"data" : data1
}
"id2": {
"status": {
"status": "loaded"
},
"data" : data2
},
"id2": {
"status": {
"creation": timestampValue,
"status": "loaded"
},
"data" : data
},
"id3": {
"status": {
"creation": timestampValue,
"status": "ended"
},
"data" : data3
}
}
I want to use a filter based on the creation field.
The field is not always present.
My code is inspired by this one :
https://github.com/firebase/functions-samples/blob/master/delete-old-child-nodes/functions/index.js
here's the code I wrote :
const CUT_OFF_TIME = 24 * 60 * 60 * 1000; // 24 Hours in milliseconds.
exports.cleanAfter24h = functions.database.ref('/items/{itemId}').onUpdate((change, event) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const cutoff = now - CUT_OFF_TIME;
const oldItemsQuery = ref.orderByChild('creation').endAt(cutoff);
return oldItemsQuery.once('value').then((snapshot) => {
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(child => {
let childData = child.val();
if (childData.status.creation) {
let elapsed = childData.status.creation - cutoff;
if (elapsed <= 0) {
updates[child.key] = null;
}
} else {
console.log(child.key + ' does not have a creation date');
}
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
When the code is run, all items are retrieved, even those that have a timestamp smaller than the cutoff and those that do not have a creation field.
Any suggestions how to fix that?
I tried removing the items that have no creation field by adding a startAt("") before the endAt as suggested here :
Firebase, query with "a child exists" as a condition?
const oldItemsQuery = ref.orderByChild('creation')startAt("").endAt(cutoff);
Doing this I have no results in the query response.
Change this:
const oldItemsQuery = ref.orderByChild('creation').endAt(cutoff);
into this:
const oldItemsQuery = ref.orderByChild('creation').equalTo(cutoff);
equalTo
Creates a Query that includes children that match the specified value.
Using startAt(), endAt(), and equalTo() allows us to choose arbitrary starting and ending points for our queries.

Filter Firebase query with javascript

The JSON of my firebase database looks like this:
{
"Films" :
[
{
"Title" : "Rear Window",
"Actors" : [ "James Stewart", "Grace Kelly", "Raymond Burr" ],
"Year" : "1954",
"Writer" : [ "John Michael Hayes (screenplay)", "Cornell Woolrich (short story)" ]
},
{
"Title" : "The Ring",
"Actors" : [ "Carl Brisson", "Lillian Hall-Davis", "Ian Hunter" ],
"Year" : "1927",
"Writer" : [ "Alred Hitchcock" ]
},
{
"Title" : "The Farmer's Wife",
"Actors" : [ "Jameson Thomas", "Lillian Hall-Davis", "Gordon Harker" ],
"Year" : "1928",
"Writer" : [ "1928" ]
}
]
}
I am building out my html based on the initial query from firebase like so:
// Initialize FireBase Database
firebase.initializeApp(config);
// Query the root of FireBase
var query = firebase.database().ref();
// Get a snapshot of all of the data
query.once("value")
.then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var childData = childSnapshot.val();
buildFilms(childData);
});
});
// Build out the films
function buildFilms(films) {
for ( i = 0; i < films.length; i ++ ) {
var numOfActors = films[i].Actors.length,
filmDiv = document.createElement('div'),
filmTitle = document.createElement('h1');
filmTitle.innerHTML = films[i].Title;
filmDiv.setAttribute("id", "film" + i);
filmDiv.setAttribute("class", "film");
document.getElementById("wrapper").appendChild(filmDiv);
document.getElementById("film" + i).appendChild(filmTitle);
listActors(films, numOfActors);
showYear(films);
}
}
// List out Actors per film
function listActors(films, numOfActors) {
for ( subindex = 0; subindex < numOfActors; subindex ++ ) {
var actorTitle = document.createElement('h2');
actorTitle.innerHTML = films[i].Actors[subindex];
document.getElementById("film" + i).appendChild(actorTitle);
}
}
// List years per film
function showYear(films) {
var yearSpan = document.createElement('h4');
yearSpan.innerHTML = films[i].Year;
document.getElementById("film" + i).appendChild(yearSpan);
}
I would then like to use a basic html text input to repaint the dom on submit using Firebase's built in query, so something like this:
function runSearch(e) {
e.preventDefault();
var title = $('#title').val().toLowerCase();
$("#wrapper").empty();
setTimeout(function() {
query.once("value")
.then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
// Need to narrow the films to the matching search input here
buildFilms(newFilteredData);
});
});
}, 1000)
}
I cannot figure out how to use the input to query my firebase data by "Title". Any help is much appreciated.
I hope firebase query orderBy startAt endAt will solve your requirement.
Note: This wont help you if you try to orderBy multiple fields, you'll have to write some other logic to achieve multiple fields orderBy.
Try the below code snippet. It'll filter the data for you!!
Hope this helps you!
var title = 'movie';
query
.orderByValue()
.startAt(title).endAt(title)
.on('value', function(snapshot) {
var movie = snapshot.val();
//do whatever you want.
});
For complete information on the Query related, please visit the below link
https://firebase.google.com/docs/reference/js/firebase.database.Query
This should work:
// Get the root of FireBase
var root = firebase.database().ref();
// Query the Films node under it
var query = root.child("Films");
// Get a snapshot containing the matching children from under Films
query.once("value")
.then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val().Title);
});
});

Categories

Resources