Looping through firebase unique keys and adding data to each - firebase

I have firebase database in this form.
"items":{
"-Jp9VLYBwENfNVLKYyCG" : {
"-Jp9VMKkTUggcMrUCO8S" : {
"author" : "facebook:#########",
"created" : 1431474107638,
},
"-Jp9VobJP0qbSbbEdyW0" : {
"author" : "facebook:#########",
"created" : 1431474227548
}
},
"-JpEbifKeRj-_H7TWhQ2" : {
"-JpEbrnT6SFRFc6U_HWX" : {
"author" : "facebook:#########",
"created" : 1431559961683
},
"-JpEby5mZuQMsU-9YCpk" : {
"author" : "facebook:##########",
"created" : 1431559987495
}
}
}
I want to add the following object to each $id containing author and created key-value pairs.
upVote: {
active: true,
total: 0
},
downVote: {
active: true,
total: 0
}
I am first trying to fetch the values using DataSnapshot. But it doesn't print the keys in console. What is wrong here?
var ref = new Firebase(FBURL + "/items");
ref.once("value", function(snapshot) {
console.log("snapshot key: " + snapshot.key());
snapshot.forEach(function(childSnapshot){
var key = childSnapshot.key();
var val = childSnapshot.val();
console.log("child snapshot key: " + key);
childSnapshot.forEach(function(deepSnap){
console.log("deep shot key: " + deepSnap.key());
});
});
});
RESOLVED The problem was that I had some security rules set up with read and write permissions. Therefore wasn't allowing me to read the element from the user scope that I was calling the script. It was one gotcha like problems.
After this step, it was easy to update the database with values:
var deepRef = ref.child(key).child(deepSnap.key());
deepRef.update({upVote: {
active: true,
total: 0
},downVote: {
active: true,
total: 0
}});

Related

Flutter Firebase Cloud Functions deleting all items as soon as they are written

I'm trying to use the deleteOldItems Cloud Functions code for my project, when I use the code and simply change the 2 hours to 4 it deletes all my nodes as soon as they are written. Here is my Firebase functions code...
exports.deleteOldItems = functions.database.ref('messages/{pushId}').onWrite(async (change) => {
var ref = change.after.ref.parent; // reference to the items
var now = Date.now();
var cutoff = now - 4 * 60 * 60 * 1000;
var oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);
return oldItemsQuery.once('value', function(snapshot) {
// create a map with all children that need to be removed
var updates = {};
snapshot.forEach(function(child) {
updates[child.key] = null
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
And here is how I'm saving my timestamp in Flutter to
database/messages
with child 'timestamp'..
var time = DateTime.now().millisecondsSinceEpoch;
Here is my JSON # 'messages'
"messages" : {
"-M3e5Glv5JEaz12hDwBB" : {
"body" : "Hello",
"name" : "Charles",
"photo" : "https://media1.giphy.com/media/7wDEaTbCupuRq/giphy-downsized.gif",
"timeStamp" : 1585549220985,
"user" : "4eb8920a-e407-4488-bce4-c6f64f7b0891"
},
"-M3e5Jfx2cnbtMTNLPev" : {
"body" : "Example",
"name" : "Charles",
"photo" : "https://media1.giphy.com/media/7wDEaTbCupuRq/giphy-downsized.gif",
"timeStamp" : 1585549232893,
"user" : "4eb8920a-e407-4488-bce4-c6f64f7b0891"
},
"-M3e6oYk6_zTe6IAs0Fg" : {
"bodyGif" : "https://media3.giphy.com/media/8xomIW1DRelmo/giphy.gif",
"name" : "Charles",
"photo" : "https://media1.giphy.com/media/7wDEaTbCupuRq/giphy-downsized.gif",
"timestamp" : 1585549625584,
"user" : "4eb8920a-e407-4488-bce4-c6f64f7b0891"
},
"-M3hR_DrGcKhM8zpFbYA" : {
"body" : "Again",
"name" : "Charles",
"photo" : "https://media1.giphy.com/media/7wDEaTbCupuRq/giphy-downsized.gif",
"timeStamp" : 1585605399479,
"user" : "4eb8920a-e407-4488-bce4-c6f64f7b0891"
}
and my database security rules are as follows for 'messages'...
"messages": {
".indexOn": ["timestamp"]
}
How can I get the nodes to be removed as expected?

How to do filter by keyword in Firebase?

I have a Firebase database structure like this
{
"5ZhJG1NACDdG9WoNZWrBoYGkIpD3" : {
"Company" : {
"5ZhJG1NACDdG9WoNZWrBoYGkIpD3" : {
"authTime" : 1532061957,
"companyName" : "Scopic Software",
"contactName" : "Hoang Scopic",
"email" : "hoang.trinh#scopicsoftware.com",
"firebaseID" : "5ZhJG1NACDdG9WoNZWrBoYGkIpD3",
"isFirstLogin" : false,
"phoneNumber" : "1234567890",
"schoolName" : "MIT",
"teachers" : {
"aOpjnzHpDiZ7uwQQqJoinGvM9ZD3" : "0"
}
}
}
},
"AhZc9B02goOtZ6qBNhz9W0K6Esg2" : {
"Subscription" : {
"-LHlQ4OhijzzFY5HZOT4" : {
"firebaseID" : "-LHlQ4OhijzzFY5HZOT4",
"period" : {
"endAt" : "1533194625",
"startAt" : "1531985025"
},
"status" : "trial"
}
},
"Teacher" : {
"AhZc9B02goOtZ6qBNhz9W0K6Esg2" : {
"authTime" : 1532061932,
"email" : "hoang.trinhj#gmail.com",
"firebaseID" : "AhZc9B02goOtZ6qBNhz9W0K6Esg2",
"isFirstLogin" : false,
"name" : "Hoang Trinh",
"schoolName" : "HUST",
"subscriptions" : {
"-LHlQ4OhijzzFY5HZOT4" : "0"
}
}
}
},
"aOpjnzHpDiZ7uwQQqJoinGvM9ZD3" : {
"Subscription" : {
"-LHlWnpNZazBC5lpXLi0" : {
"firebaseID" : "-LHlWnpNZazBC5lpXLi0",
"period" : {
"endAt" : "1533196388",
"startAt" : "1531986788"
},
"status" : "trial"
}
},
"Teacher" : {
"aOpjnzHpDiZ7uwQQqJoinGvM9ZD3" : {
"Company" : {
"5ZhJG1NACDdG9WoNZWrBoYGkIpD3" : "0"
},
"authTime" : 1532060884,
"email" : "piavghoang#gmail.com",
"firebaseID" : "aOpjnzHpDiZ7uwQQqJoinGvM9ZD3",
"isFirstLogin" : false,
"subscriptions" : {
"-LHlWnpNZazBC5lpXLi0" : "0"
}
}
}
},
"ooR32SjABdOYEkWX6dzy4fE5Kym1" : {
"Admin" : {
"email" : "admin#test.com",
"firstName" : "Hoang",
"lastName" : "Trinh",
"password" : "123456xx"
},
"isAdmin" : true
}
}
This is data in an existing system, so I can't change its structure.
Now I need to write an API to list these records filtered by "email" attribute there (it's nested inside a key).
How can I do a search for this email?
There is one solution that I thought about. It's just getting the whole json back and process data in code (not using Firebase database query functions).
But I can't do that, because AFTER filtering, I need to do paging also.
In order to make the pagination, I need to use query functions like "sortByChild" or "limitToLast", so I have to use these query functions with filtering.
Thank you.
EDIT:
My current implementation for pagination:
getUsers: async (page, perPage, searchTerm) => {
const db = fbAdmin.db();
let dbRef = await db.ref();
if (searchTerm) {
// TODO: Add filter logic here
}
let totalItems = await dbRef.once('value');
let totalItemsNumber = await Object.keys(totalItems.toJSON()).length;
// Calculate for pagination
let lastItemId;
if ((page - 1) * perPage + 1 > totalItemsNumber) {
lastItemId = null;
} else {
let lastItems = await dbRef.orderByKey().limitToLast((page - 1) * perPage + 1).once('value');
lastItemId = Object.keys(lastItems.toJSON())[0];
}
// Do the pagination
let snap;
let data;
if (lastItemId) {
snap = await dbRef.orderByKey().endAt(lastItemId).limitToLast(perPage).once('value');
data = snap.toJSON();
} else {
data = {};
}
let users = Object.keys(data).map(key => {
if (data[key][KEY]) { // Check if it's an admin
return {
role: TYPE_ADMIN,
email: data[key][KEY].email,
name: data[key][KEY].firstName + " " + data[key][KEY].lastName
}
} else if (data[key][Company.KEY]) { // Check if it's a company member
return {
role: TYPE_COMPANY_MEMBER,
email: data[key][Company.KEY][key].email,
name: data[key][Company.KEY][key].name,
schoolName: data[key][Company.KEY][key].schoolName
}
} else if (data[key][Teacher.KEY]) { // Check if it's a teacher
if (data[key][Teacher.KEY][key][Company.KEY]) { // Check if it's a company teacher
return {
role: TYPE_COMPANY_TEACHER,
email: data[key][Teacher.KEY][key].email,
name: data[key][Teacher.KEY][key].name,
schoolName: data[key][Teacher.KEY][key].schoolName,
subscription: data[key][Subscription.KEY]
}
} else { // Check if it's an individual teacher
return {
role: TYPE_INDIVIDUAL_TEACHER,
email: data[key][Teacher.KEY][key].email,
name: data[key][Teacher.KEY][key].name,
schoolName: data[key][Teacher.KEY][key].schoolName,
subscription: data[key][Subscription.KEY]
}
}
} else {
return null;
}
});
return users || null;
},

Add a field to Firestore?

I have a field called jobsPosted as seen in the picture, so I want to add another job, I have dishwasher and waiter already. But I get an error with this query
db.collection("companies").doc("Tiradito").field("jobsPosted").set(postJobObject).then(function() {
console.log("Document successfully written!");
});
That's my postJobbject
var postJobObject = {
"position": this.state.selected,
"timeSchedule": this.state.timeSchedule,
"compensation" : this.state.compensation,
"experience" : this.state.experience,
"description" : this.state.description
}
pass the option to merge the new data with any existing document to avoid overwriting entire documents
var cityRef = db.collection('cities').doc('BJ');
var setWithMerge = cityRef.set({
capital: true
}, { merge: true });
source: https://firebase.google.com/docs/firestore/manage-data/add-data
Try
jobsPosted = {}
var postJobObject = {
"position": this.state.selected,
"timeSchedule": this.state.timeSchedule,
"compensation" : this.state.compensation,
"experience" : this.state.experience,
"description" : this.state.description
}
jobsPosted['newJob'] = postJobObject;
Then use update
db.collection("companies").doc("Tiradito").update(jobsPosted).then(function() {
console.log("Document successfully written!");
});

How to orderByChild in nested Child in angularfire?

My firebase database structure is as below. I would like to search with a particular "Info/Email", let's say "abc#abc.com".
{
"-KhWrBcYyMluJbK7QpnK" : {
"Info" : {
"Email" : "xyz#gmail.com"
},
"Settings" : {
"Status" : "Accepted"
}
},
"-KhX0tgQvDtDYqt4XRoL" : {
"Info" : {
"Email" : "abc#abc.com"
},
"Settings" : {
"Status" : "Accepted"
}
},
"-KhX1eo7uFnOxqncDXQ5" : {
"Info" : {
"Email" : "abc#abc.com"
},
"Settings" : {
"Status" : "Pending"
}
}
}
I added a rule too
"Invitation" : {
".indexOn": ["Info/Email","Settings/Status"]
}
My AngularFire code is as follows:
var rootRef = firebase.database().ref().child('Invitation');
var userInvitations = rootRef.child("Info").orderByChild("Email").equalTo("abc#abc.com");
var allInvitations = $firebaseArray(userInvitations);
But I am getting a FIREBASE WARNING: Using an unspecified index. Consider adding ".indexOn": "Email" at /Invitation/Info to your security rules for better performance. and of course I am not receiving any data.
What are the mistakes I made here? Also can I add multiple orderByChild, for example: if I want to find details of the record, which has "Info/Email" equal to "abc#abc.com" and "Settings/Status" equal to "Pending" ?
The Firebase API only allows you to filter children one level deep
So with reference to your data structure:
if it were to be this way,
{
"-KhWrBcYyMluJbK7QpnK" : {
"Email" : "xyz#gmail.com",
"Settings" : {
"Status" : "Accepted"
}
},
"-KhX0tgQvDtDYqt4XRoL" : {
"Email" : "abc#abc.com",
"Settings" : {
"Status" : "Accepted"
}
},
"-KhX1eo7uFnOxqncDXQ5" : {
"Email" : "abc#abc.com",
"Settings" : {
"Status" : "Pending"
}
}
}
You should write your rules this way instead.
"Invitation" : {
"Info" : {
".indexOn": "Email",
},
"Settings" : {
".indexOn": "Status",
}
}
Then you will be able to query the data
var allInvitations = [];
var rootRef = firebase.database().ref().child('Invitation');
var userInvitations = rootRef.orderByChild("Email").equalTo("abc#abc.com");
userInvitations.on('value', snap => {
var invitations = snap.val()
for (prop in invitations ) {
allInvitations.push(invitations[prop ]);
}
console.log(allInvitations);
})

Receive data many-to-many relation Firebase

I have an extended question to this question.
What if the player belong to more than one team?
I have this
json
"Players" : {
"-YRHd4IjrjsBXx__B" : {
"name" : "The best forward",
"creationDate" : "2016-02-26 15:50:39",
"teams" : {
"-KAByPeIz4IjrjsBXx__B" : true,
"-KEFPuCXcqOah_GJwsMCu" : true,
"-KEwuQxvGpYTEJ7YQ58-l" : true,
"-KKF8vPtf8J7cfqFh2PLm" : true
},
},
etc...
}
players-service.js
getPlayers: function(teamid) {
var Players = {};
var teamsIndex = ref.child('teams/' + teamid + '/players/');
var playersIndex = ref.child('players/');
teamsIndex.on('child_added', function(snapshot) {
var playerKey = snapshot.key;
playersIndex.child(playerKey).on('value', function(playersnap){
$timeout(function() {
console.log("key", playerKey);
players[playerKey] = playersnap.val();
});
});
});
teamIndex.on('child_removed', function(snapshot) {
$timeout(function(snapshot) {
delete players[snapshot.key()];
});
});
return players;
}
But it returns a list of object. I know that I could probably query/change the data structure to/in firebase and return it as a $firebaseArray which I prefer as I use angularfire.
You usually structure your data depending on how you want to retrieve them.
From my understanding (correct me if I'm wrong) you want to get all the players in a team. For this purpose I would use this structure:
"Players": {
"player1": {...},
"player2": {...},
"player3": {...}
},
"Teams': {
"team1": {...},
"team2": {...}
},
"TeamPlayers" : {
"team1": {
"player1": true,
"player2": true
},
"team2": {
"player1": true,
"player3": true
}
}
Or using an array
"TeamPlayers" : {
"team1": [
0: "player1",
1: "player2"
]
}

Resources