Execute promise or await with generated string variable - javascript

I am building a mongoose query and storing it in a variable call query. The code below shows it
let query = "Product.find(match)";
if (requestObject.query.sortBy) {
query = query.concat(".", "sort(sort)");
const parts = requestObject.query.sortBy.split(":");
sort[parts[0]] = parts[1] === "desc" ? -1 : 1;
}
if (requestObject.query.fields) {
query = query.concat(".", "select(fields)");
const fields = requestObject.query.fields.split(",").join(" ");
const items = await Product.find(match).sort(sort).select(fields); //.populate("category").exec();
/**const items = await Product.find(match).sort(sort).select("-__v"); //.populate("category").exec();**/
}
I am facing an issue when attempting to run a mongoose query that I have generated and stored in a string. When I run it in post man, the response is 200 but no data is returned. Below is a console.log(query) on line 2
what I hope to achieve is to have await or create a new promise execute the content id query variable like shown below
const items = new Promise((resolve) => resolve(query)); //.populate("category").exec();
items
? responseObject.status(200).json(items)
: responseObject
.status(400)
.json({ message: "Could not find products, please try again" });
I will appreciate it very much that and also if you can give me a better way of doing it, I will love that

This doesn't really make sense. You are building a string, not a query. You can't do anything with that string. (You could eval it, but you really shouldn't). Instead, build a query object!
let query = Product.find(match);
if (requestObject.query.sortBy) {
const [field, dir] = requestObject.query.sortBy.split(":");
const sort = {};
sort[field] = dir === "desc" ? -1 : 1;
query = query.sort(sort);
}
if (requestObject.query.fields) {
const fields = requestObject.query.fields.split(",");
query = query.select(fields);
}
//query.populate("category")
const items = await query.exec();
if (items) {
responseObject.status(200).json(items)
} else {
responseObject.status(400).json({ message: "Could not find products, please try again" });
}
If you really want to get that string for something (e.g. debugging), build it separately from the query:
let query = Product.find(match);
let queryStr = 'Product.find(match)';
if (requestObject.query.sortBy) {
const [field, dir] = requestObject.query.sortBy.split(":");
const sort = {[field]: dir === "desc" ? -1 : 1};
query = query.sort(sort);
queryStr += `.sort(${JSON.stringify(sort)})`;
}
if (requestObject.query.fields) {
const fields = requestObject.query.fields.split(",");
query = query.select(fields);
queryStr += `.select(${JSON.stringify(fields)})`;
}
//query.populate("category")
//queryStr += `.populate("category")`;
console.log(queryStr);
const items = await query.exec();
…

Related

Wait for all Firebase data query requests before executing code

I am trying to fetch data from different collections in my cloud Firestore database in advance before I process them and apply them to batch, I created two async functions, one to capture the data and another to execute certain code only after all data is collected, I didn't want the code executing and creating errors before the data is fetched when i try to access the matchesObject after the async function to collect data is finished, it keeps saying "it cannot access a property matchStatus of undefined", i thought took care of that with async and await? could anyone shed some light as to why it is undefined one moment
axios.request(options).then(function(response) {
console.log('Total matches count :' + response.data.matches.length);
const data = response.data;
var matchesSnapshot;
var marketsSnapshot;
var tradesSnapshot;
var betsSnapshot;
matchesObject = {};
marketsObject = {};
tradesObject = {};
betsObject = {};
start();
async function checkDatabase() {
matchesSnapshot = await db.collection('matches').get();
matchesSnapshot.forEach(doc => {
matchesObject[doc.id] = doc.data();
console.log('matches object: ' + doc.id.toString())
});
marketsSnapshot = await db.collection('markets').get();
marketsSnapshot.forEach(doc2 => {
marketsObject[doc2.id] = doc2.data();
console.log('markets object: ' + doc2.id.toString())
});
tradesSnapshot = await db.collection('trades').get();
tradesSnapshot.forEach(doc3 => {
tradesObject[doc3.id] = doc3.data();
console.log('trades object: ' + doc3.id.toString())
});
betsSnapshot = await db.collection('bets').get();
betsSnapshot.forEach(doc4 => {
betsObject[doc4.id] = doc4.data();
console.log('bets object: ' + doc4.id.toString())
});
}
async function start() {
await checkDatabase();
// this is the part which is undefined, it keeps saying it cant access property matchStatus of undefined
console.log('here is matches object ' + matchesObject['302283']['matchStatus']);
if (Object.keys(matchesObject).length != 0) {
for (let bets of Object.keys(betsObject)) {
if (matchesObject[betsObject[bets]['tradeMatchId']]['matchStatus'] == 'IN_PLAY' && betsObject[bets]['matched'] == false) {
var sfRef = db.collection('users').doc(betsObject[bets]['user']);
batch11.set(sfRef, {
accountBalance: admin.firestore.FieldValue + parseFloat(betsObject[bets]['stake']),
}, {
merge: true
});
var sfRef = db.collection('bets').doc(bets);
batch12.set(sfRef, {
tradeCancelled: true,
}, {
merge: true
});
}
}
}
});
There are too many smaller issues in the current code to try to debug them one-by-one, so this refactor introduces various tests against your data. It currently won't make any changes to your database and is meant to be a replacement for your start() function.
One of the main differences against your current code is that it doesn't unnecessarily download 4 collections worth of documents (two of them aren't even used in the code you've included).
Steps
First, it will get all the bet documents that have matched == false. From these documents, it will check if they have any syntax errors and report them to the console. For each valid bet document, the ID of it's linked match document will be grabbed so we can then fetch all the match documents we actually need. Then we queue up the changes to the user's balance and the bet's document. Finally we report about any changes to be done and commit them (once you uncomment the line).
Code
Note: fetchDocumentById() is defined in this gist. Its a helper function to allow someCollectionRef.where(FieldPath.documentId(), 'in', arrayOfIds) to take more than 10 IDs at once.
async function applyBalanceChanges() {
const betsCollectionRef = db.collection('bets');
const matchesCollectionRef = db.collection('matches');
const usersCollectionRef = db.collection('users');
const betDataMap = {}; // Record<string, BetData>
await betsCollectionRef
.where('matched', '==', false)
.get()
.then((betsSnapshot) => {
betsSnapshot.forEach(betDoc => {
betDataMap[betDoc.id] = betDoc.data();
});
});
const matchDataMap = {}; // Record<string, MatchData | undefined>
// betIdList contains all IDs that will be processed
const betIdList = Object.keys(betDataMap).filter(betId => {
const betData = betDataMap[betId];
if (!betData) {
console.log(`WARN: Skipped Bet #${betId} because it was falsy (actual value: ${betData})`);
return false;
}
const matchId = betData.tradeMatchId;
if (!matchId) {
console.log(`WARN: Skipped Bet #${betId} because it had a falsy match ID (actual value: ${matchId})`);
return false;
}
if (!betData.user) {
console.log(`WARN: Skipped Bet #${betId} because it had a falsy user ID (actual value: ${userId})`);
return false;
}
const stakeAsNumber = Number(betData.stake); // not using parseFloat as it's too lax
if (isNaN(stakeAsNumber)) {
console.log(`WARN: Skipped Bet #${betId} because it had an invalid stake value (original NaN value: ${betData.stake})`);
return false;
}
matchDataMap[matchId] = undefined; // using undefined because its the result of `doc.data()` when the document doesn't exist
return true;
});
await fetchDocumentsById(
matchesCollectionRef,
Object.keys(matchIdMap),
(matchDoc) => matchDataMap[matchDoc.id] = matchDoc.data()
);
const batch = db.batch();
const queuedUpdates = 0;
betIdList.forEach(betId => {
const betData = betDataMap[betId];
const matchData = matchDataMap[betData.tradeMatchId];
if (matchData === undefined) {
console.log(`WARN: Skipped /bets/${betId}, because it's linked match doesn't exist!`);
continue;
}
if (matchData.matchStatus !== 'IN_PLAY') {
console.log(`INFO: Skipped /bets/${betId}, because it's linked match status is not "IN_PLAY" (actual value: ${matchData.matchStatus})`);
continue;
}
const betRef = betsCollectionRef.doc(betId);
const betUserRef = usersCollectionRef.doc(betData.user);
batch.update(betUserRef, { accountBalance: admin.firestore.FieldValue.increment(Number(betData.stake)) });
batch.update(betRef, { tradeCancelled: true });
queuedUpdates += 2; // for logging
});
console.log(`INFO: Batch currently has ${queuedUpdates} queued`);
// only uncomment when you are ready to make changes
// batch.commit();
}
Usage:
axios.request(options)
.then(function(response) {
const data = response.data;
console.log('INFO: Total matches count from API:' + data.matches.length);
return applyBalanceChanges();
}

Firebase Cloud Function updating ref with incorrect values

I want to add a new node to the database if the node doesn't exist. I don't want to return anything to the client, I just want to update the database with the new values. On the client I have a listener that observes the credit_counts property, once the update happens it receives it there and notifies all users that this particular user has a new credit.
In the code below I check to see if (!snapshot.exists() and if it's not there I add the node to the database using admin.database().ref('/user_credits/{creditId}/{userId}').set({ dict });. After pasting the url I check the db and the layout is:
I'm a Swift developer. In Swift I can just do:
Database.database().reference().child("/user_credits/\(creditId)/\(userId)").setValue(dict) and the tree will be correct.
user_credits > {creditId} > {userId} > dict are incorrect. It should be user_credits > sample_123 > user_xyz > dict values. Where am I going wrong at?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.updateViewsCtAtPostsRef = functions.https.onRequest((request, response) => {
const currentTimeStamp = Date.now();
const receivedTimeStamp = admin.database.ServerValue.TIMESTAMP;
const creditId = "sample_123";
const userId = "userId_xyz";
admin.database().ref('user_credits').child(creditId).child(userId).once('value', snapshot => {
if (!snapshot.exists()) {
var dict = {
"joined_date": receivedTimeStamp,
"timeStamp": receivedTimeStamp,
"credits_count": 1
};
return admin.database().ref('/user_credits/{creditId}/{userId}').set({ dict });
} else {
const previousTimeStamp = snapshot.child("timeStamp").val();
const creditsCount = snapshot.child("credits_count").val();
if (previousTimeStamp + whatever) < currentTimeStamp {
let updatedCount = creditsCount + 1
return admin.database().ref('/user_credits/{creditId}/{userId}').update({ "timeStamp": receivedTimeStamp, "credits_count": updatedCount });
} else {
return true
}
}
});
});
I had to change the ref to:
return admin.database().ref('/user_credits/' + creditId + '/' + userId).set({ "joined_date": receivedTimeStamp, "timeStamp": receivedTimeStamp, "credits_count": 1 });
I also had to update the ref inside the else statement to follow the same format.
The syntax is fine, but the reference does not match the structure; that should rather be:
admin.database().ref('user_credits').child(creditId).child(userId).child('dict')
... else there won't be any snapshot.child("timeStamp") or snapshot.child("credits_count").

.save is not a function and .map is not a function while updating document

I have below code: When I try to from postman it says :
"message": "account.save is not a function"
const account = await Account.find({ "buildings.gateways.devices.verificationCode": code })
var accountId = account ? account.map(item => item._id) : null
const buildings = _.flatMap(account, a => a.buildings)
const gateways = _.flatMap(buildings, b => b.gateways);
const devices = _.flatMap(gateways, g => g.devices);
// finding deviceId to insert for user from that account
const device = _.filter(devices, d => d.verificationCode === code);
device.patientFirstName = req.body.firstName;
device.patientLastName = req.body.lastName;
account.save();
If I try to change to findOne() then it says
"account.map is not a function"
account always returns values then why it cant map i dont understand.
Your help is appreciated. Thanks
.find() returns an array so you're able to run Array.map() but you need to .save() each document separately:
const accounts = await Account.find({ "buildings.gateways.devices.verificationCode": code })
var accountId = account ? account.map(item => item._id) : null
for(let account of accounts){
await account.save();
}
.findOne() (awaited) returns single document so you cannot use .map():
const account = await Account.findOne({ "buildings.gateways.devices.verificationCode": code })
var accountId = account ? account._id : null
account.save();

Get author user ID using Parse.Query not current user

I have two parse classes, User and Place.
If user ads a place, user is added as Pointer to the Place user column.
In order to list all places and determine how many places has a user, i use the following query:
loadTotalPointsDetail(params: any = {}): Promise<Place[]> {
const page = params.page || 0;
const limit = params.limit || 100;
const query = new Parse.Query(Place);
query.equalTo('user', Parse.User.current());
query.skip(page * limit);
query.limit(limit);
query.include('category');
query.include('user');
query.doesNotExist('deletedAt');
return query.find();
}
Filtering by Parse.User.current()) i will get current user places.
If i don't filter by Parse.User.current()) it will return all places as objects, containing all data.
How can i filter by place real author / user? not current (loggedIn)?
loadTotalPointsDetail(params: any = {}): Promise<Place[]> {
const page = params.page || 0;
const limit = params.limit || 100;
const query = new Parse.Query(Place);
const user = new Parse.User();
user.id = 'The id of the user that you want to search for';
query.equalTo('user', user);
query.skip(page * limit);
query.limit(limit);
query.include('category');
query.include('user');
query.doesNotExist('deletedAt');
return query.find();
}
I'll post the solution here, not the most indicated but it works for me:
async loadData() {
try {
const places = await this.placeService.loadTotalPointsDetail(this.params);
const placeU = await this.placeService.loadPlaceU(this.getParams().id);
for (const place of places) {
this.places.push(place);
}
let u = placeU.user.id;
let totalUserPlaces = places.filter(x => x.user.id == u);
if (totalUserPlaces) {
/* The total returned by reduce() will be stored in myTotal */
const myTotal = totalUserPlaces.reduce((total, place) => {
/* For each place iterated, access the points field of the
current place being iterated and add that to the current
running total */
return total + place.points;
}, 0); /* Total is initally zero */
this.points = myTotal;
} else {}
} catch (err) {
const message = await this.getTrans('ERROR_NETWORK');
this.showToast(message);}}
so i'm loading two separate queries:
const places = await this.placeService.loadTotalPointsDetail(this.params); -> Gets all posts listings
const placeU = await this.placeService.loadPlaceU(this.getParams().id); --> Gets the ID for current post
I extract the user post ID:
let u = placeU.user.id;
I filter using that user in order to get his posts:
let totalUserPlaces = places.filter(x => x.user.id == u);

Nodejs loop through the string and save into mongodb with auto increment length

In NodeJS I have string like this
"Package=Package&Qty=1&Price=123?Package=Package Two&Qty=3&Price=702?Package=Package Three&Qty=1&Price=199?Package=Package One&Qty=4&Price=852?";
I want to save each Qty with corresponding Package and Price. So lets say I have Package Two with Qty 3 then it would save 3 times with Package Two and Price would be 702.
So for now my code looks like this
const querystring = require('querystring');
const string = "Package=Package&Qty=1&Price=123?Package=Package Two&Qty=3&Price=702?Package=Package Three&Qty=1&Price=199?Package=Package One&Qty=4&Price=852?";
const items = string.split('?').filter(Boolean);
var TicketCountQuery = Ticket.count();
for(const query of items) {
// Parse the query string of each group
const { Package, Qty, Price } = querystring.parse(query);
for(let i = 0; i < Number(Qty); i++) {
console.log('Package Name ' + Package);
console.log('Package Price ' + Price);
TicketCountQuery.exec(function (e, count) {
if( count !== '' ) {
let TicketId = parseInt(count) + 1;
console.log(TicketId);
let ticketData = new Ticket({
_id : new mongoose.Types.ObjectId(),
ticketid : 'TKT '+ TicketId,
packagename : Package,
price : Price
});
ticketData.save((err) => {
if( err ) {
if( err.errors ) {
console.log('something went wrong');
}
}
else {
console.log('tickets saved');
}
});
}
});
}
}
Here the problem is it is just checking the mongodb collection only for the first time. That's why the ticketid is always same. I want that ticketid should be increment for each insert. But somehow its not working. So can someone tell me how to do this?
Any help and suggestion will be really appreciable.
The problem is that you're running the N exec queries before saving any ticket, this happens because the mongoose.exec runs asynchronously, so all the queries will return the same count. An easy fix is using async/await. According to the docs, .exec returns a promise if no callback is supplied, so we can easilly wait until the exec is done using await
const string = "Package=Package&Qty=1&Price=123?Package=Package Two&Qty=3&Price=702?Package=Package Three&Qty=1&Price=199?Package=Package One&Qty=4&Price=852?";
const qs = require('querystring');
async function processTickets(string) {
const TicketCountQuery = Ticket.count();
// We split the string into multiple valid query strings.
// We strip the empty item due to the '?' at the end using .filter(Boolean)
const items = string.split('?').filter(Boolean);
// We loop through each group
for(const query of items) {
// Parse the query string of each group
const { Package, Qty, Price } = qs.parse(query);
for(let i = 0; i < Number(Qty); i++) {
// We send the email here <Qty> times.
console.log('Package Name ' + Package);
console.log('Package Price ' + Price);
try {
// We wait until exec is done
// No other tickets can be fetched/saved, preventing your problem
const count = await TicketCountQuery.exec();
if( count !== '' ) {
let TicketId = parseInt(count) + 1;
let ticketData = new Ticket({
_id: new mongoose.Types.ObjectId(),
ticketid: 'TKT ' + TicketId,
packagename: Package,
price: Price
});
// We save each ticket one at a time
// So the next one will have the correct ID.
await ticketData.save();
console.log('ticket saved');
}
} catch(e) {
console.log('something went wrong', e);
}
}
}
}
processTickets(string)
.then(res => {
console.log('Tickets processed!')
})
.catch(err => {
console.error(err)
})

Categories

Resources