Trouble filtering data in firebase database - javascript

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.

Related

Javascript Conditional return object

I used aws-sns to create one webhook. Two lambda functions are checked by this webhook. One of the lambda functions publishes 'orderId' and'startTime', while another publishes 'orderId' and 'roundName'. Both lambdas fire at different times. As a result, publishing can happen at two different times. One or both of the'startTime' and 'roundName' parameters may be undefined.
If 'roundName' exists, the 'updateOrder' variable will return 'roundName,' and the database will be updated. When'startTime' is set and 'roundName' is left blank, the 'roundName' will be rewritten from the database, which I don't want. Because if there is a 'roundName,' there will always be a 'roundName,' the value of 'roundName' can change but it will never be undefined.If startTime changes as well as roundName change then it will update the database. But my current logic is wrong. Struggling to implementing diffrent scenario logic.
const data = {
Records: [
{
Sns: {
Message:
'[{\n "orderId": "a4013438-926f-4fdc-8f6a-a7aa402b40ea",\n "roundName": "RO1"}]',
},
},
],
};
const existingData = [
{
modifiedAt: "2022-03-09T13:18:06.211Z",
lastMile: "progress",
createdAt: "2022-02-26T06:38:50.967+00:00",
orderId: "a4013438-926f-4fdc-8f6a-a7aa402b40ea",
},
];
// parse the data
const parseData = data.Records.flatMap((record) =>
JSON.parse(record.Sns.Message)
);
// check if the data exist or not
const existingOrder = existingData.filter(
(o1) => parseData.some((o2) => o1.orderId === o2.orderId)
);
// if there is no existingOrder then return false
if (existingOrder.length === 0) return;
// if there is exisiting order then add roundName and startTime from SNS event
const updateOrder = existingOrder.map((i) => {
const roundName = parseData.find((r) => {
return r.orderId === i.orderId;
}).roundName;
const startTime = parseData.find((r) => {
return r.orderId === i.orderId;
}).startTime;
return {
roundName: roundName ?? "",
startTime: startTime ?? "",
};
});
console.log(updateOrder);

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 :)

Get Call for JSON data, then looping through to find total values of a data point

Goal is to make a get call and loop through the JSON data and find the total vehicles in service (true value) and out of service (false value).
JSON example (Single Array):
[{
TxId : "",
Value : {
Vin : "",
Owner : "",
VehicleType : "",
LastLocation : "",
InService : false
},
Timestamp : "",
IsDelete : ""
},
TxId : "",
Value : {
Vin : "",
Owner : "",
VehicleType : "",
LastLocation : "",
InService : false
},
Timestamp : "",
IsDelete : ""
},
TxId : "",
Value : {
Vin : "",
Owner : "",
VehicleType : "",
LastLocation : "",
InService : false
},
Timestamp : "",
IsDelete : ""
}]
I have started my js file code:
function barChartModel(context) {
const self = this;
/* chart data */
let barSeries = [{name: "In-service", items: [0]},
{name: "Out-of-service", items: [0]}];
$.get("http://localhost:9000/vehicles", (data) => {
How would I go about looping through this JSON data to find the totals of vehicles in service vs out of service. My goal is to use that data to create bar chart visualization. I am a little lost now.
There's two points to you question:
The basic logic of counting a specific type of item in an array
Working with async data and (probably?) a chart rendering library in knockout
1. Getting the bar values
The other answer already showed an approach by using filter. I'd like to suggest an alternative using reduce:
const data = [{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":false},"Timestamp":"","IsDelete":""},{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":false},"Timestamp":"","IsDelete":""},{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":true},"Timestamp":"","IsDelete":""}];
const inService = data.reduce(
(sum, x) => sum + (x.Value.InService ? 1 : 0),
0
);
console.log(inService);
2. Working with async data
You can use an observable array to write the data response to. Then, you can have a computed automatically recalculate the in-service sums when anything changes and combine it in to your chart data.
By subscribing to the computed chart data, you can trigger some sort of external update to rerender the graphs (depending on which library you use)
const isInService = ({ Value: { InService }}) => InService;
const count = (pred, xs) =>
xs.reduce((sum, x) => sum + pred(x), 0);
// App
// When the async call completes, it writes to this array
const data = ko.observableArray([]);
// Whenever the data changes, this outputs a new configuration
// object
const graphData = ko.computed(() => {
const inService = count(isInService, data());
return [
{ name: "In-service", items: [inService]},
{ name: "Out-of-service", items: [data().length - inService]}
];
});
// The first render renders an empty graph, the data hasn't
// yet loaded
renderGraph(graphData());
// Ensure future updates
graphData.subscribe(renderGraph);
// Load initial data
fakeFetch("/someUrl").then(data);
// Utils
function fakeFetch() {
return new Promise((res, rej) => {
setTimeout(
res.bind(null, getData()),
1000
)
})
}
function renderGraph(graphData) {
console.log(
graphData.map(bar =>
bar.name.padEnd(16, " ") + "|" +
Array(bar.items[0]).fill("⬛").join("")
).join("\n")
);
}
function getData() { return [{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":false},"Timestamp":"","IsDelete":""},{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":false},"Timestamp":"","IsDelete":""},{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":true},"Timestamp":"","IsDelete":""}]; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
var data = [{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":false},"Timestamp":"","IsDelete":""},{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":false},"Timestamp":"","IsDelete":""},{"TxId":"","Value":{"Vin":"","Owner":"","VehicleType":"","LastLocation":"","InService":false},"Timestamp":"","IsDelete":""}];
var inServiceCount = ko.utils.arrayFilter(data, function(item){
return item.Value.InService;
}).length;
var outOfServiceCount = data.length - inServiceCount; // Assuming InService is boolean and can not be null
console.log(inServiceCount); //0
console.log(outOfServiceCount); //3
Fiddle

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 lazy load

I'm trying to lazy load firebase items to later on load more of them whenever user reaches end of div container. When i remove .endAt() and .startAt() i'm receiving the 15 items though they are not beeing incremented and it's stuck at these 15 items.
When i keep .endAt() and .startAt() i'm receiving firebase warning
Using an unspecified index. Consider adding ".indexOn": "title" at /items even though .indexOn is set. I'm confused by that warning. Thanks in advance for any help.
Firebase structure
{
"items" : {
"-Kk6aHXIyR15XiYh65Ht" : {
"author" : "joe",
"title" : "Product 1"
},
"-Kk6aMQlh6_E3CJt_Pnq" : {
"author" : "joe",
"title" : "Product 2"
}
},
"users" : {
"RG9JSm8cUndpjMfZiN6c657DMIt2" : {
"items" : {
"-Kk6aHZs5xyOWM2fHiPV" : "-Kk6aHXIyR15XiYh65Ht",
"-Kk6aMTJiLSF-RB3CZ-2" : "-Kk6aMQkw5bLQst81ft7"
},
"uid" : "RG9JSm8cUndpjMfZiN6c657DMIt2",
"username" : "joe"
}
}
}
Security rules
{
"rules": {
".read": true,
".write": "auth != null",
"users":{
"$uid": {
".write": "$uid === auth.uid"
"items":{
".indexOn": "title",
"$itemId": {
"title": {".validate": "...}
"type": {".validate": "...}
}
}
}
}
}
}
}
Code structure for lazy load
let _start = 0,
_end = 14,
_n = 15;
function lazyLoadItems(){
firebase.database().ref('items')
.orderByChild('title')
.startAt(_start)
.endAt(_end)
.limitToFirst(_n)
.on("child_added", snapshot=> console.log(snapshot.val()));
_start += _n;
_end += _n;
}
You're misunderstanding how Firebase queries work. It's easiest to see if you use hard-coded values:
firebase.database().ref('items')
.orderByChild('title')
.startAt(0)
.endAt(14)
.limitToFirst(15)
There is no item with title=0 or title=14, so the query doesn't match anything.
Firebase Database queries match on the value of the property you order on. So when you order by title the values you specify in startAt and endAt must be titles. E.g.
ref.child('items')
.orderByChild('title')
.startAt("Product 1")
.endAt("Product 1")
.limitToFirst(15)
.on("child_added", function(snapshot) { console.log(snapshot.val()); });
See for the working sample of this: http://jsbin.com/hamezu/edit?js,console
To implement pagination, you'll have to remember the last item of the previous page and pass that in to the next call: startAt(titleOfLastItemOnPreviousPage, keyOfLastItemOnPreviousPage).
startAfter is the last element
each time you have to send the last element:
const lastAlert = querySnapshot.docs[querySnapshot.docs.length - 1]
here an example:
export const fetchAlerts = (createdForId, startAfter) => {
return new Promise((resolve, reject) => {
firebase
let query = firebase
.firestore()
.collection('alerts')
.where('generated_for.uid', '==', createdForId)
.where('read', '==', false)
.orderBy('created_at', 'desc')
.limit(25)
if (startAfter) {
query = query.startAfter(startAfter)
}
query
.get()
.then(querySnapshot => {
const lastAlert = querySnapshot.docs[querySnapshot.docs.length - 1]
const alerts = querySnapshot.docs.map(doc => ({
...doc.data(),
uid: doc.id,
}))
resolve(alerts)
resolve({ alerts, lastAlert })
})
.catch(error => {
console.log(error.message)

Categories

Resources