How to intersection query Real-time Firebase database? - javascript

I'm trying to gain an understanding of query in Firebase. I want to pass userId A & B and find out if they are subscribed to a common chatId, it will either return true or false.
How can query both userId and evaluate results for my desired output?
export const checkForExistingChat = (currentUserId, recipient) => {
var IdList = {}
var query = database
.ref(`Chats/${currentUserId}`)
.orderByChild("subscribedToChat")
.once("value", function (dataSnapshot) {
dataSnapshot.forEach(function (childSnapshot) {
const childData = childSnapshot.val();
console.log("childData : ", childData);
});
});
};
Export JSON of Chat
"Chats" : {
"61vtPjp8YVVSzpvexwXMgEHghYf1" : {
"subscribedToChat" : {
"1a555cbf-30b7-4c8f-9986-4252a7620c45" : "1a555cbf-30b7-4c8f-9986-4252a7620c45",
"2d718ea7-eafa-48db-af14-f165f07b3b08" : "2d718ea7-eafa-48db-af14-f165f07b3b08",
"2e4fd8bb-4afb-4229-83ec-5a427fe2731d" : "2e4fd8bb-4afb-4229-83ec-5a427fe2731d",
"35c537ef-57dd-48c5-9350-5d1fe2e9d118" : "35c537ef-57dd-48c5-9350-5d1fe2e9d118",
"3a816ac1-6e97-4d66-ae19-77e65f8c2df4" : "3a816ac1-6e97-4d66-ae19-77e65f8c2df4",
}
},
"qqpBNbEa8ZSiCEUlseFeGeiRqzh2" : {
"subscribedToChat" : {
"1a555cbf-30b7-4c8f-9986-4252a7620c45" : "1a555cbf-30b7-4c8f-9986-4252a7620c45",
"35c537ef-57dd-48c5-9350-5d1fe2e9d118" : "35c537ef-57dd-48c5-9350-5d1fe2e9d118"
}
}
}

Since you already know how to load data from Firebase, this is essentially a non-Firebase problem: finding the overlapping keys in two lists of keys.
A quick code snippet:
var json = {
"Chats" : {
"61vtPjp8YVVSzpvexwXMgEHghYf1" : {
"subscribedToChat" : {
"1a555cbf-30b7-4c8f-9986-4252a7620c45" : "1a555cbf-30b7-4c8f-9986-4252a7620c45",
"2d718ea7-eafa-48db-af14-f165f07b3b08" : "2d718ea7-eafa-48db-af14-f165f07b3b08",
"2e4fd8bb-4afb-4229-83ec-5a427fe2731d" : "2e4fd8bb-4afb-4229-83ec-5a427fe2731d",
"35c537ef-57dd-48c5-9350-5d1fe2e9d118" : "35c537ef-57dd-48c5-9350-5d1fe2e9d118",
"3a816ac1-6e97-4d66-ae19-77e65f8c2df4" : "3a816ac1-6e97-4d66-ae19-77e65f8c2df4",
}
},
"qqpBNbEa8ZSiCEUlseFeGeiRqzh2" : {
"subscribedToChat" : {
"1a555cbf-30b7-4c8f-9986-4252a7620c45" : "1a555cbf-30b7-4c8f-9986-4252a7620c45",
"35c537ef-57dd-48c5-9350-5d1fe2e9d118" : "35c537ef-57dd-48c5-9350-5d1fe2e9d118"
}
}
}
};
var keys1 = Object.keys(json.Chats["61vtPjp8YVVSzpvexwXMgEHghYf1"].subscribedToChat);
var keys2 = Object.keys(json.Chats["qqpBNbEa8ZSiCEUlseFeGeiRqzh2"].subscribedToChat);
console.log(keys1, keys2);
var commonKeys = keys1.filter(function(key) {
return keys2.indexOf(key) >= 0;
});
console.log(commonKeys);
This is an O(n^2) algorithm, but I doubt that'll be a concern on the list sizes you're likely to have. If it is a concern, and the lists are sorted, you can keep a cursor in each least, and move forward only through them once to make it an O(2n) algorithm.

Related

Super Efficient object unwrapping

I have a snapshot coming from Firebase that I want to turn into a JS class. I currently have a monster loop that is terrible in almost every way. Would anyone have an example of a better way to turn the raw json into this class?
My current method is very long (see section two). I would love to learn a fast more efficient method.
Class:
class SuggestedLocation {
country_slug
region_slug
slug
marker_type
typeInType
geometry
properties
type
id
constructor(country_slug, region_slug, slug, marker_type, typeInType, geometry, properties, type, id) {
this.country_slug = country_slug
this.region_slug = region_slug
this.slug = slug
this.marker_type = marker_type
this.typeInType = typeInType
this.geometry = geometry
this.properties = properties
this.type = type
this.id = id
}
}
Current unwrapping method:
static fromSnapshot(snapshot) {
let suggestedLocations = [new SuggestedLocation()]
if (snapshot.exists()) {
const value = snapshot.val()
const countrySlugs = Object.keys(value)
for (const country_slug of countrySlugs) {
const regionSlugs = Object.keys(value[country_slug])
for (const region_slug of regionSlugs) {
const slugs = Object.keys(value[country_slug][region_slug])
for (const slug of slugs) {
const markerTypes = Object.keys(value[country_slug][region_slug][slug])
for (const markerType of markerTypes) {
const accomAmenityTypes = Object.keys(value[country_slug][region_slug][slug][markerType])
for (const accomAmenityType in accomAmenityTypes) {
const typeInTypes = Object.keys(value[country_slug][region_slug][slug][markerType][accomAmenityType])
for (const typeInType of typeInTypes) {
const ids = Object.keys(value[country_slug][region_slug][slug][markerType][accomAmenityType][typeInType])
for (const id in ids) {
const geoJsonObject = value[country_slug][region_slug][slug][markerType][accomAmenityType][typeInType][id]
const properties = geoJsonObject["properties"]
const geometry = geoJsonObject["geometry"]
const type = geoJsonObject["type"]
suggestedLocations.push(new SuggestedLocation(country_slug, region_slug, slug, markerType, typeInType, geometry, properties, type, id))
}
}
}
}
}
}
}
}
return new SuggestedLocationsObject(suggestedLocations)
}
Example Json:
{
"united-kingdom" : {
"calderdale" : {
"rossendale-way" : {
"accommodations" : {
"Campground" : {
"zO3HxZVELbd" : {
"geometry" : {
"coordinates" : [ -2.1901328761018704, 53.65022995288969 ],
"type" : "Point"
},
"properties" : {
"marker-color" : "#6e875f",
"marker-size" : "medium",
"marker-symbol" : "lodging",
"name" : "",
"place_id" : "zO3HxZVELbd",
"plus_code" : ""
},
"type" : "Feature"
}
}
}
}
}
}
}
You will have to drill down your snapshot data any way, but you could use Object.entries instead of Object.keys, so you get the corresponding value in one go.
Also you could use map and flatMap to produce the array of SuggestedLocation instances without explicit push.
If the multiple assignments to this.* bother you, then you could consider changing the constructor signature so it takes an object instead of the individual values. Then you can use Object.assign to transfer those values in one go.
Finally, I don't really get why you want the array of SuggestedLocation instances to have an initial element with all undefined properties ([new SuggestedLocation()]). The only reason I can think of is for the case where snapshot.exists() is false... then you would have an array with this one entry. But when there is data, why would you have this dummy entry included?
Here is the code that expresses the above ideas (in plain JavaScript - not TypeScript):
class SuggestedLocationsObject extends Array {
constructor(arr) {
super();
Object.assign(this, arr);
}
}
class SuggestedLocation {
constructor(obj) {
Object.assign(this, obj);
}
}
function fromSnapshot(snapshot) {
return new SuggestedLocationsObject(
!snapshot.exists()
? [new SuggestedLocation()]
: Object.entries(snapshot.val()).flatMap(([country_slug, regionSlugs]) =>
Object.entries(regionSlugs).flatMap(([region_slug, slugs]) =>
Object.entries(slugs).flatMap(([slug, markerTypes]) =>
Object.entries(markerTypes).flatMap(([markerType, accomAmenityTypes]) =>
Object.entries(accomAmenityTypes).flatMap(([accomAmenityType, typeInTypes]) =>
Object.entries(typeInTypes).flatMap(([typeInType, ids]) =>
Object.entries(ids).map(([id, {properties, geometry, type}]) =>
new SuggestedLocation({country_slug, region_slug, slug, markerType, typeInType, geometry, properties, type, id})
)
)
)
)
)
)
)
);
}
let data = {
"united-kingdom" : {
"calderdale" : {
"rossendale-way" : {
"accommodations" : {
"Campground" : {
"zO3HxZVELbd" : {
"geometry" : {
"coordinates" : [ -2.1901328761018704, 53.65022995288969 ],
"type" : "Point"
},
"properties" : {
"marker-color" : "#6e875f",
"marker-size" : "medium",
"marker-symbol" : "lodging",
"name" : "",
"place_id" : "zO3HxZVELbd",
"plus_code" : ""
},
"type" : "Feature"
}
}
}
}
}
}
};
let snapshot = {
exists() { return true },
val() { return data }
}
let result = fromSnapshot(snapshot);
console.log(result);

JavaScript - Targeting an object value to create another variable

So I have an array which looks like this:
[
{ TransactionValues: '50.00' },
{ TransactionValues: '-77.43' },
{ TransactionValues: '-20.23' },
{ TransactionValues: '200.23' }
]
I am trying to find a way to target the monetary value and create a variable based on the sum of these. When I try to target the "50.00" for example I get "Undefined" and it's still an array.
I'm not exactly sure how I can target it specifically, is it possible? Any help would be appreciated
As per the comments here is the full code (be wary I'm still learning so it's not elegant):
var fs = require('fs');
var parse = require('csv-parse');
var transactionValues = []; //Need an array to hold transactions
var currentTrans = [];
var savingsTrans = [];
//constuctor for transactions
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : transactions
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = {
"TransactionValues" : transactions
}
currentTrans.push(cTrans);
}
else {
var sTrans = {
"TransactionValues" : transactions
}
savingsTrans.push(sTrans);
}
};
//parses the csv file, loops each row and adds it to the transactionValue array
var parser = parse({columns: true}, function (err, results) {
console.table(results);
for (const row of results) {
addData(row.AccountID, row.AccountType, row.InitiatorType, row.DateTime, row.TransactionValue );
logTrans(row.AccountType, row.TransactionValue);
}
console.log(transactionValues);
console.log(currentTrans);
console.log(savingsTrans);
});
fs.createReadStream(__dirname+'/testData/customer-1234567-ledger.csv').pipe(parser)
not completely following but at the end of the day you have an array like data below.
you can use filter to target the attribute you want.
you can use map to pull out just the values.
you can use reduce to sum them all up.
run the snippet below to see each step
const data = [
{ TransactionValues: '50.00', AccountType: 'CURRENT' },
{ TransactionValues: '-77.43', AccountType: null},
{ TransactionValues: '-20.23', AccountType: 'CURRENT' },
{ TransactionValues: '200.23', AccountType: null }
];
const CurrentTrans = data.filter((x) => x.AccountType === 'CURRENT');
const SavingTrans = data.filter((x) => x.AccountType !== 'CURRENT');
console.log('CurrentTrans');
console.log(CurrentTrans);
console.log('SavingTrans');
console.log(SavingTrans);
const CurrentTransValues = CurrentTrans.map((x) => parseFloat(x.TransactionValues));
const SavingTransValues = SavingTrans.map((x) => parseFloat(x.TransactionValues));
console.log('CurrentTransValues');
console.log(CurrentTransValues);
console.log('SavingTransValues');
console.log(SavingTransValues);
const TotalCurrentValues = CurrentTransValues.reduce((sum, x) => sum + x);
const TotalSavingValues = SavingTransValues.reduce((sum, x) => sum + x);
console.log('TotalCurrentValues');
console.log(TotalCurrentValues.toFixed(2));
console.log('TotalSavingValues');
console.log(TotalSavingValues.toFixed(2));
So I may have fixed it by using parseFloat in my addData and logTrans functions:
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : parseFloat(transactions)
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = parseFloat(transactions);
currentTrans.push(cTrans);
}
else {
var sTrans = parseFloat(transactions);
savingsTrans.push(sTrans);
}
};
Now that seems to of worked. So I can use the "Sum values of objects in array" as suggested before. Thank you everyone :)

When i am removing a data from firebase using react js it deletes the data and one null value is entered. How can I avoid null entry

While deleting the data from firebase database null value is getting inserted into firebase.
deleteImag = e => {
const val = e.target.value;
var x = '';
const rootRef = fire.database().ref();
const fooRef = rootRef.child(`assets/${this.state.id}`);
fooRef.on("value", snap => {
const foo = snap.val();
if (foo !== null) {
Object.keys(foo).forEach(key => {
if (foo[key].id = val) {
console.log(key);
x = key;
// The Object is foo[key]
var res = null;
console.log(foo[key].id);
fire.database().ref(`assets/${this.state.id}/${x}`).remove();
}
});
}
});
};
JSON:
{
"hero" : {
"img" : "http://aws-website-testing-slv0n.s3-website-us-east-1.amazonaws.com/images/CornHole3.jpg"
},
"id" : "urn:microsense:cms:asset:80c71fdb-0d32-41de-8170-8e35409b8e63",
"purpose" : "activity",
"thumbnail" : {
"img" : "http://aws-website-testing-slv0n.s3-website-us-east-1.amazonaws.com/images/CornHole3.jpg"
}
}, null, null, {
enter image description here
There was array issue. When we remove an array in Firebase it replace that element with null. So the above article was helpful for me. Thanks

Taking the Average of a Data Set in Firebase Database

I'm in the process of designing an app but coming a bit unstuck with Javascript. So far I have a Firebase Realtime Database with the following structure.[!
What I'd like to do is for each time an area in green is added / updated, take a value(red) from that area in green get the average from all the values that are held within each green object and place it into a brown object at the bottom.
Would anyone have any idea on how to complete this using Javascript / Firebase functions?
JSON Export:
{
"5Rz8DpU34PeXAcnriD6vEiPu7jk2" : {
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"rating1" : 5
},
"average" : 0
},
"Fi43uP2LcbVLi2uFwUyCAp2uvSH2" : {
"average" : 0
},
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"Fi43uP2LcbVLi2uFwUyCAp2uvSH2" : {
"rating1" : 5,
"rating2" : 5
},
"asdas" : {
"rating1" : 2
},
"average" : 0
},
"gov4hRpDgDVhyVgsQrYJnn1rfeW2" : {
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"rating1" : 5
},
"average" : 0
}
}
The following Cloud Function code should do the trick:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.database();
exports.average = functions.database
.ref('/ratings/{blueId}/{greenId}')
.onWrite((change, context) => {
const blueId = context.params.blueId;
const blueRef = db.ref('ratings/' + blueId);
const blueAverageRef = db.ref('ratings/' + blueId + '/average');
let totalSum = 0;
let nbrOfElem = 0;
return blueRef
.once('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
if (childSnapshot.val().val) {
//console.log(childSnapshot.val());
totalSum += childSnapshot.val().val;
nbrOfElem++;
}
});
})
.then(() => {
//console.log('totalSum: ' + totalSum);
//console.log('nbrOfElem: ' + nbrOfElem);
return blueAverageRef.transaction(function(average) {
if (nbrOfElem > 0) {
return { val: totalSum / nbrOfElem };
} else {
return 0;
}
});
})
.catch(error => {
console.log(error);
});
});
Note that it uses a Transaction, see https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions and https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
The database shall be structured as:
-ratings
-blueNode1
-greenNode11
-val:2 // <- red node in your picture
-greenNode12
-val:10
-average // <- red node in your picture
-val:6
-blueNode2
-greenNode21
-val:5
-greenNode22
-val:3
-greenNode23
-val:1
-average
-val:5
It would be something like this:
exports.detectGreenChanges = functions.database.ref('/requests/{blueId}/{greenId}').onWrite((change, context) => {
const greenIdData = change.after.val();
const blueId = context.params.blueId;
const greenId = context.params.greenId;
// Do whatever you want with the data and set it to where ever you want
});
Take a look at this docs, it'll help you set the value back to where you want.
Good luck!

Firebase - Get data by key or chid values - javascript

I am trying to read the data from firebase database, and display the same in a webpage.
My database structure is as below -
If you see the image, i am able to read the "UserData" using the below code -
firebase.initializeApp(config);
var database = firebase.database();
var ref = database.ref('UserData');
ref.once('value', gotData1, errData);
function gotData1(data){
//console.log(data.val());
var usrData = data.val();
var keys = Object.keys(usrData);
//console.log(keys);
for (var i = 0; i< keys.length; i++){
var k = keys[i];
var id = usrData[k].AssignedID;
var name = usrData[k].Name;
$(document).ready(function() {
var $formrow = '<tr><td>'+id+'</td><td>'+name+'</td></tr>';
$('#userInfo').append($formrow);
});
}
}
In the highlighted part of the image, you can see keys with values 196214, 196215, 196216
Now, I need to fetch the values for "One, Count" by matching the key values with available AssignedID.
How can i achieve the same?
Update, JSON as text -
{
"app_url" : "https://app_name?ls=1&mt=8",
"UserData" : {
"HNpTPoCiAYMZEeVOs01ncfGBj6X2" : {
"Name" : "Arunima Vj"
"Email" : "asd#upp.com",
"AssignedID" : 196214
},
"VXU2tdGdzZX90PJa9mpEL3zAiZo2" : {
"Name" : "Lakshman Medicherla"
"Email" : "asd#upp.com",
"AssignedID" : 196215
},
"dFlwtqDNrja2RkOySVtW106IQP62" : {
"Name" : "Prashanth Sripathi"
"Email" : "asd#upp.com",
"AssignedID" : 196216
}
}
"teams" : {
"196214" : {
"1105" : {
"One" : 7619,
"count" : 24
},
"1379" : {
"Two" : 7145,
"count" : 21
}
},
"196215" : {
"1111" : {
"One" : 7779,
"count" : 20
},
"1508" : {
"Two" : 1176,
"count" : 21
}
},
"196216" : {
"1106" : {
"One" : 7845,
"count" : 22
},
"1509" : {
"Two" : 1156,
"count" : 26
}
}
}
}
Your data structure is quite nested, which makes the code more difficult to read. But this navigates the structure generically in the minimum code I could come up with:
var ref = firebase.database().ref("/42824688");
ref.child("UserData").once('value', gotUserData);
function gotUserData(snapshot){
snapshot.forEach(userSnapshot => {
var k = userSnapshot.key;
var id = userSnapshot.val().AssignedID;
var name = userSnapshot.val().Name;
ref.child("teams").child(id).once("value", teamsSnapshot => {
teamsSnapshot.forEach(teamSnapshot => {
var teamKey = teamSnapshot.key;
teamSnapshot.forEach(teamProp => {
var prop = teamProp.key;
var val = teamProp.val();
console.log(k+" "+name+" "+id+": "+teamKey+", "+prop+"="+val);
});
});
});
})
}
So for each user, this loads the teams data for that user and then loops over the teamsSnapshot to get each teamSnapshot and then loops over that to get each team property.
Working jsbin: http://jsbin.com/noziri/edit?html,js,console

Categories

Resources