Cannot use “undefined” as a Firestore value - node.js

I am trying to retrieve data from my Firestore database using node.js, I want to collect a field from one Firestore query and pass the value into another Firestore query but I keep getting this error in my logs, the first Firestore query successfully retrieves data, but my problem is passing a value to the second query
Error: Value for argument "value" is not a valid query constraint. Cannot use "undefined" as a Firestore value. If you want to ignore undefined values, enable `ignoreUndefinedProperties`. at Object.validateUserInput (/workspace/node_modules/#google-cloud/firestore/build/src/serializer.js:271:19) at validateQueryValue (/workspace/node_modules/#google-cloud/firestore/build/src/reference.js:2048:18) at CollectionReference.where (/workspace/node_modules/#google-cloud/firestore/build/src/reference.js:988:9) at step2 (/workspace/index.js:74:43) at /workspace/index.js:65:17 at QuerySnapshot.forEach (/workspace/node_modules/#google-cloud/firestore/build/src/reference.js:748:22) at updateBets (/workspace/index.js:60:22) at processTicksAndRejections (internal/process/task_queues.js:97:5)
here is my code
async function updateBets() {
var marketRef = db.collection('matches');
var snapshot = await marketRef.where('matchStatus', '==', 'FINISHED').get();
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
console.log('I found documents');
snapshot.forEach(doc => {
step2();
async function step2() {
var marketRef2 = db.collection('markets');
var snapshot2 = await marketRef2.where('marketId', '==', doc.data().matchId).get();
console.log(doc2.id, '=>', doc2.data());
snapshot2.forEach(doc2 => {
console.log(doc2.id, '=>', doc2.data());
if (doc2.data().marketTitleId == 'FULL_TIME_RESULT') {
var a = doc.data().homeTeamScore;
var b = doc.data().awayTeamScore;
var winnerIndex;
if (a > b) {
winnerIndex = 0;
var resultIndex = ['WINNER', 'LOSER', 'LOSER'];
var docName = `${doc.data().matchId}` + '000' + '1';
var sfRef = db.collection('markets').doc(docName);
batch5.update(sfRef, {
results: resultIndex
});
} else if (a == b) {
winnerIndex = 1;
var docName = `${doc.data().matchId}` + '000' + '1';
var resultIndex = ['LOSER', 'WINNER', 'LOSER'];
var sfRef = db.collection('markets').doc(docName);
batch5.update(sfRef, {
results: resultIndex
});
} else if (a < b) {
winnerIndex = 2;
var docName = `${doc.data().matchId}` + '000' + '1';
var resultIndex = ['LOSER', 'LOSER', 'WINNER'];
var sfRef = db.collection('markets').doc(docName);
batch5.update(sfRef, {
results: resultIndex
});
}
}
})
}
});
batch5.commit().then(() => {
console.log("im done with results");
}).catch((err) => {
console.log('Mac! there was an error with results: ', err);
});
}

You could try:
const data = doc.data();
const matchId = data.matchId;
and then put matchId into query.
Also log the "matchId" variable to see the value.

Related

connection.queryRaw is not a function error in NodeJs WebAPI call

I am new to Nodejs. I am developing WebAPI by using NodeJs and MSSQl as database.My api is giving proper response in case of POST endpoint If it is called while server is listening through Dev environment command [npm run start]. But, If I Deploy my API on Windows IIS , it is giving the mentioned error. My reference API endpoint code is as below :
router.post('/',async (req,res,next)=>{
// console.log('Enter products creation')
const Product = Array.from(req.body) // req.body
// console.log('New Product details passed on',Product)
const createProd = require('../CreateProduct')
const response = await createProd(Product)
res.status(404).json({
message : response.retStatus
})
})
CreateProduct function called in above code is as below :
const sql = require("mssql/msnodesqlv8");
const dataAccess = require("../DataAccess");
const fn_CreateProd = async function (product) {
let errmsg = "";
let objBlankTableStru = {};
let connPool = null;
// console.log('Going to connect with Connstr:',global.config)
await sql
.connect(global.config)
.then((pool) => {
global.connPool = pool;
productsStru = pool.request().query("DECLARE #tblProds tvp_products select * from #tblProds");
return productsStru;
})
.then(productsStru=>{
objBlankTableStru.products = productsStru
productsOhStru = global.connPool.request().query("DECLARE #tblprodsOh tvp_product_oh select * from #tblprodsOh");
return productsOhStru
})
.then((productsOhStru) => {
objBlankTableStru.products_oh = productsOhStru
let objTvpArr = [
{
uploadTableStru: objBlankTableStru,
},
{
tableName : "products",
tvpName: "tvp_products",
tvpPara: "tblProds"
},
{
tableName : "products_oh",
tvpName: "tvp_product_oh",
tvpPara: "tblProdsOh",
}
];
newResult = dataAccess.getPostResult(
objTvpArr,
"sp3s_ins_products_tvp",
product
);
console.log("New Result of Execute Final procedure", newResult);
return newResult;
})
.then((result) => {
// console.log("Result of proc", result);
if (!result.recordset[0].errmsg)
errmsg = "New Products Inserted successfully";
else errmsg = result.recordset[0].errmsg;
})
.catch((err) => {
console.log("Enter catch of Posting prod", err.message);
errmsg = err.message;
if (errmsg == "") {
errmsg = "Unknown error from Server... ";
}
})
.finally((resp) => {
sql.close();
});
return { retStatus: errmsg };
};
module.exports = fn_CreateProd;
GetPost() function is as below :
const getPostResult = (
tvpNamesArr,
procName,
sourceData,
sourceDataFormat,
singleTableData
) => {
let arrtvpNamesPara = [];
let prdTable = null;
let newSrcData = [];
// console.log("Source Data :", sourceData);
let uploadTable = tvpNamesArr[0];
for (i = 1; i <= tvpNamesArr.length - 1; i++) {
let tvpName = tvpNamesArr[i].tvpName;
let tvpNamePara = tvpNamesArr[i].tvpPara;
let TableName = tvpNamesArr[i].tableName;
let srcTable = uploadTable.uploadTableStru[TableName];
srcTable = srcTable.recordset.toTable(tvpName);
let newsrcTable = Array.from(srcTable.columns);
newsrcTable = newsrcTable.map((i) => {
i.name = i.name.toUpperCase();
return i;
});
if (!singleTableData) {
switch (sourceDataFormat) {
case 1:
newSrcData = sourceData.filter((obj) => {
return obj.tablename.toUpperCase() === TableName.toUpperCase();
});
break;
case 2:
newSrcData = getObjectDatabyKey(sourceData, TableName);
break;
default:
newSrcData = getTableDatabyKey(sourceData, TableName);
break;
}
} else {
newSrcData = sourceData;
}
// console.log(`Filtered Source data for Table:${TableName}`, newSrcData);
prdTable = generateTable(
newsrcTable,
newSrcData,
tvpName,
sourceDataFormat
);
arrtvpNamesPara.push({ name: tvpNamePara, value: prdTable });
}
const newResult = execute(procName, arrtvpNamesPara);
return newResult;
};
Finally, I have found the solution to this.. it is very strange and shocking and surprising that If I using Morgan Middleware in app.js and have used it by syntax : app.use(morgan('dev')) , then it is the culprit command..I just removed dev from this command after which problem got resolved..But I found no help regarding this issue over Google anywhere..I really fear that what type of challenges I am going to face in future development if these kind of silly error come without giving any hint..I would be highly obliged If anyone could make me understand this kind of silly errors..

How to get All the effective Currency exchange rate by date in NetSuite one time?

[![enter image description here][1]][1]
Any method to query All the effective Currency exchange rates by date in NetSuite one time?
Just like the Field "AS OF" in the Currency Exchange Rates Page did.
I thought about the "N/curency" module and the https.get function, but it seems to be high cost, any tips or solution?
[1]: https://i.stack.imgur.com/1KPMK.png
The table is exposed in SuiteTalk and the Analytics browser so you can get the values either way. Via Analytics/SuiteQL
require(['N/query'], function(query) {
var sql =
"SELECT " +
" cr.id, b.symbol as basecurrency, c.symbol as transactioncurrency, cr.effectivedate, cr.exchangerate" +
" FROM " +
" currencyrate as cr, currency as c, currency b where c.id = transactioncurrency and b.id = basecurrency and cr.effectivedate = '5/2/2022' ";
var results = query.runSuiteQL({
query: sql
}).asMappedResults();
console.log(results);
});
The script below is my final solution to fulfill my demand, You may run it on any Record page's Console, It will print the result JUST like the "Currency Exchange Rates" page shows.
I'm not sure if it's a good one, but it did returns the data that I want.
require(['N/query'], function getEffCcyExchRtArr(query) {
const getQueryResBySql = (sql) => {
const resObj = query.runSuiteQL({query: sql, });
return resObj.asMappedResults();
};
const getPagedQueryResBySql = (sql) => {
const maxPageSize = 1_000;
const resultSet = query.runSuiteQLPaged({query: sql, pageSize: maxPageSize, });
const tempArr = [];
if (resultSet && resultSet.count > 0) {
for (let i = 0; i < resultSet.pageRanges.length; i++) {
const page = resultSet.fetch(i);
page.data.results.forEach(item => tempArr.push(item.asMap()));
}
}
return tempArr;
};
const getAllCurrencyArr = () => {
const sql = `
SELECT
Currency.id AS id,
Currency.isbasecurrency AS is_base_ccy,
Currency.symbol AS iso_symbol
FROM
Currency
WHERE
NVL(Currency.isinactive, 'F') = 'F'
`;
return getQueryResBySql(sql);
};
const getAllEffectiveCcyExchRtArr = (effectiveDateStr) => {
let effectiveDate = !!effectiveDateStr ? `to_date('${effectiveDateStr}','YYYY-MM-DD')` : 'CURRENT_DATE';
let sql = `
SELECT
currencyRate.basecurrency AS base_ccy,
currencyRate.transactioncurrency AS trans_ccy,
currencyRate.exchangerate AS exchange_rt,
currencyRate.effectivedate AS effective_date
FROM
currencyRate
WHERE
currencyRate.effectivedate <= ${effectiveDate}
ORDER BY currencyRate.effectivedate DESC, currencyRate.id DESC
`;
return getPagedQueryResBySql(sql);
};
const currencyArr = getAllCurrencyArr();
const baseCurrencyIdArr = currencyArr.filter(item => item['is_base_ccy'] === 'T').map(item => item.id);
const allCurrencyIdArr = currencyArr.map(item => item.id);
const currencyIdSymbolObj = currencyArr.reduce((pre, cur) => {
pre[cur?.id] = cur?.['iso_symbol'];
return pre;
}, {});
const allEffectiveCcyExchRtArr = getAllEffectiveCcyExchRtArr();
const effectiveCcyExchRtArr = [];
for (const baseCurrencyId of baseCurrencyIdArr) {
for (const currencyId of allCurrencyIdArr) {
for (const currencyObj of allEffectiveCcyExchRtArr) {
if (currencyObj?.['base_ccy'] === baseCurrencyId && currencyObj?.['trans_ccy'] === currencyId) {
effectiveCcyExchRtArr.push({
baseCurrency: currencyIdSymbolObj[baseCurrencyId],
transactionCurrency: currencyIdSymbolObj[currencyId],
exchangeRate: currencyObj?.['exchange_rt']
});
break;
}
}
}
}
window.console.table(effectiveCcyExchRtArr);
}, );

Edit a JSON object

I retrieved a JSON object from a local database, I want to edit a value (invItems) and add a new value to it (filed[filed.invItems]), then upload it back to the database, but it does not seem to work (the JSON does not seem to change)
async function invPut(itemID, message) {
var filed = await frenzyDB.getKey(id + "_invcache");
console.log("Before: " + filed)
newInvItems = filed.invItems + 1;
filed.invItems = newInvItems;
filed[filed.invItems] = itemID;
console.log("After: " + filed);
await frenzyDB.addKey(id + "_invcache", filed)
}
Console Output:
Before: {"invItems":0}
After: {"invItems":0}
It shows no errors, but the JSON doesnt change. Am I doing something wrong? If so, what can I do to fix it?
Thanks for all your help!
Notes:
frenzyDB is just a javascript file that deals with a standard REPL.it Database
Code of frenzyDB:
const Database = require("#replit/database")
const db = new Database()
async function addKey(key, value) {
await db.set(key, value).then(() => {return;});
}
async function getKey(key) {
return await db.get(key).then(value => {return value;});
}
function listAllKeys() {
db.list().then(keys => {return keys;});
}
async function hasKey(key) {
var keys = await listAllKeys();
if (keys.includes(key)) {
return true;
} else {
return false;
}
}
async function removeKey(key) {
await db.delete(key).then(() => {return;});
}
module.exports = {
addKey,
getKey,
listAllKeys,
hasKey,
removeKey
};
Edit: Latest code:
async function invPut(itemID, message) {
await init(message.author.id);
var filed = await frenzyDB.getKey(message.author.id + "_invcache");
console.log(filed)
const result = {};
result.invItems = (filed['invItems'] + 1) || 1;
result.hasOwnProperty(filed.invItems) ? result[filed.invItems + 1] = itemID : result[filed.invItems] = itemID;
console.log(result);
frenzyDB.addKey(message.author.id + "_invcache", result)
message.reply("A **"+ itemIDs[itemID].name + "** was placed in your inventory");
return true;
}
EDIT 2: Latest Console Output:
{ '4': 3, invItems: 5 }
{ '5': 3, invItems: 6 }
Any help will be appreciated!
Thanks
Try this
// Demo Data
const itemID = 10;
var filed = { "invItems" : 0 };
// Real function
console.log("Before: " + JSON.stringify(filed));
const result = {};
result.invItems = (filed['invItems'] + 1) || 1;
result.hasOwnProperty(filed.invItems) ? result[filed.invItems + 1] = itemID : result[filed.invItems] = itemID;
console.log("After: " + JSON.stringify(result));
The result I get is
Before: {"invItems":0}
After: {"0":10,"invItems":1}
You would then of course use result to store the data away in the DB.
async function invPut(itemID, message) {
// Typo?
var filed = await frenzyDB.getKey(itemID + "_invcache");
console.log("Before: " + filed)
const result = {};
result.invItems = (filed['invItems'] + 1) || 1;
result.hasOwnProperty(filed.invItems) ? result[filed.invItems + 1] = itemID : result[filed.invItems] = itemID;
console.log("After: " + result);
// Typo?
await frenzyDB.addKey(itemID + "_invcache", result)
}
Answer Edit:
const result = { ...filed };
result.invItems = (filed['invItems'] + 1) || 1;
result.hasOwnProperty(filed.invItems) ? result[filed.invItems + 1] = itemID : result[filed.invItems] = itemID;
console.log(JSON.stringify(result));
maybe this will help you
const json = fs.readFileSync(`${__dirname}/data/data.json`, "utf-8");
const inputData = JSON.parse(json);
inputData.push({input: 'front'}) // creates new element for data.json
-------------------------------------------
array.push({front: 'front', back: 'back'});

Node.js Use Promise.all For returning asynchronous call?

Currently, I am using cloud functions to query for posts. When the post is queried for, I set some updates for firebase after querying for some additional update for the post, as such:
const getPostsForDate = admin.firestore().collection('posts').where('timeOfDeletion', '<', currentTime)
return getPostsForDate.get().then(snapshot => {
const updates = {}
var counter = 0
const batch = admin.firestore().batch()
snapshot.forEach((doc) => {
var key = doc.id
admin.database().ref('/convoID/' + key).once('value', (snapshot) => {
if (snapshot.exists()) {
const convoIDCollection = snapshot.val()
for (var child in convoIDCollection) {
console.log(child)
updates["conversations/" + child] = null
updates["messages/"+ child] = null
updates["convoID/"+ child] = null
}
}
updates["/convoID/" + key] = null
updates["/reveals/" + key] = null
updates["/postDetails/" + key] = null
const postFireStoreRef = admin.firestore().collection('posts').doc(key)
const posterRef = admin.firestore().collection('posters').doc(key)
batch.delete(postFireStoreRef)
batch.delete(posterRef)
counter++
})
})
if (counter > 0) {
console.log("at the deletion")
return Promise.all[admin.database().ref().update(updates), batch.commit()]
}
else {
console.log("null")
return null
}
})
})
The problem is, however, is that the query admin.database().ref('convoID/...) is asynchronous; thus, the updates are sent to the database empty and nothing changes. Now, the solution to this is promises, except implementing the promise.all with all the other returns is not going as expected. I have tried
var promises = []
snapshot.forEach((doc) => {
var key = doc.id
promises.push(admin.database().ref('/convoID/' + key).once('value', (snapshot) => {
if (snapshot.exists()) {
const convoIDCollection = snapshot.val()
for (var child in convoIDCollection) {
console.log(child)
updates["conversations/" + child] = null
updates["messages/"+ child] = null
updates["convoID/"+ child] = null
}
}
updates["/convoID/" + key] = null
updates["/reveals/" + key] = null
updates["/postDetails/" + key] = null
const postFireStoreRef = admin.firestore().collection('posts').doc(key)
const posterRef = admin.firestore().collection('posters').doc(key)
batch.delete(postFireStoreRef)
batch.delete(posterRef)
counter++
})
)
})
promises.all(promises).then(() =>
if (counter > 0) {
console.log("at the deletion")
return Promise.all[admin.database().ref().update(updates), batch.commit()]
}
else {
console.log("null")
return null
}
);
Except I receive the error unexpected token if, Declaration or statement expected In the end by the promises.all() Is this the correct way to wait for the asynchronous call to finish?
To wrap this question up, I'll attempt to summarize what we covered in comments:
Fix arrow function definition. Change this:
promises.all(promises).then(() => code here)
to this:
Promise.all(promises).then(() => { code here });
And, fix calling of Promise.all(). Change this:
return Promise.all[...]
to this:
return Promise.all([...])
And, admin.database().ref().once() will return a promise, but only if you do NOT pass .once() a regular callback.
So, change this:
promises.push(admin.database().ref('/convoID/' + key).once('value', (snapshot) => { ...}));
to this:
promises.push(admin.database().ref('/convoID/' + key).once('value').then(snapshot => {...}));
And, it's a little cleaner if you change the general structure of this:
let promises = [];
snapshot.forEach((doc) => {
promises.push(...)
});
Promise.all(promises).then(...)
to this:
Promise.all(snapshot.map(doc => {
return admin.database().ref('/convoID/' + key).once(...).then(...);
})).then(...);

Complex query on more than one child in cloud functions

Here is the structure of my firebase database:
/UserData
/DeviceMgmt
/Counters
/NumberOfAll:
/NumberOfSelected
/TotalDownloaded
...
/Devices
/pushId1
/uid
/signOutTime
/toSelect=true (optional)
/downloaded
/lastDownload
/pushId2
/pushId3
...
And this is my cloud function:
exports.markDevicesForDownload = functions.database.ref('/UserData/DeviceMgmt/Counters/NumberOfSelected').onUpdate( (change) => {
const changeRef = change.after.ref;
const deviceMgmtRef = changeRef.parent.parent; // /UserData/DeviceMgmt
if (change.after.val() === 0 ) { //NumberOfSelected gets 0 value
return deviceMgmtRef.once('value')
.then((snap) => {
const devicesRef = snap.child('Devices').ref;
var average;
var numberOfAllDevices;
var totalDownloaded;
numberOfAllDevices = snap.child('Counters/NumberOfAll').val();
totalDownloaded = snap.child('Counters/TotalDownloaded').val();
average = Math.round(totalDownloaded/numberOfAllDevices);
return devicesRef
.orderByChild('signOutTime')
.equalTo(0)
.once('value',(devices) => {
return devices.ref
.orderByChild('downloaded')
.endAt(average)
.once('value',(devices) => {
devices.forEach((device) => {
device.child('toSelect').ref.set(true);
});
});
});
});
} else {
return false;
}
});
The function triggers when the counter NumberOfSelected = 0;
This happens when under any of device pushId there is no child toSelect. Then the query on downloaded child makes all devices with downloaded less than average set toSelect=true.
I wanted to limit the devices only to those which have signOutTime equal 0.
Somehow that query does not work and all devices are considered.
What I did wrong???
I would push all async tasks into a promise array and then return them all when all tasks complete:
exports.markDevicesForDownload = functions.database.ref('/UserData/DeviceMgmt/Counters/NumberOfSelected').onUpdate((change) => {
const changeRef = change.after.ref;
const deviceMgmtRef = changeRef.parent.parent; // /UserData/DeviceMgmt
if (change.after.val() === 0) { //NumberOfSelected gets 0 value
return deviceMgmtRef.once('value')
.then((snap) => {
const promises = [];
const devicesRef = snap.child('Devices').ref;
var average;
var numberOfAllDevices;
var totalDownloaded;
numberOfAllDevices = snap.child('Counters/NumberOfAll').val();
totalDownloaded = snap.child('Counters/TotalDownloaded').val();
average = Math.round(totalDownloaded / numberOfAllDevices);
const dR = devicesRef
.orderByChild('signOutTime')
.equalTo(0)
.once('value', (devices) => {
const dW = devices.ref
.orderByChild('downloaded')
.endAt(average)
.once('value', (devices) => {
devices.forEach((device) => {
if (device.child("signOutTime").val() === 0){
promises.push(device.child('toSelect').ref.set(true));
}
});
});
promises.push(dW);
});
promises.push(dR);
return Promise.all(promises);
});
} else {
return false;
}
});

Resources