The flow is:
I generate currentKey (key consist from 3 words separated by -)
I check if any document in my collection already have currentKey.
If it is not - currentKey will be pasted into my specific document in my collection.
But if it is - currentKey will be regenerated and all over again.
And the problem is mongoose have asynchronous callback that will run when query will finish executing. And i don't know how to implement my flow with this asynchronously flow. As the example (this is just example for convenience, it's not a existing framework or etc.) what i did on php is like next:
$currentKey = someService.GenerateKey();
//exists() returns true or false
while($db->find('tablename', ['key' => $currentKey])->exists())
{
$currentKey = someService.GenerateKey();
}
//It's update 'tablename' where `id` equals to $someUserId and set `key` to $currentKey value
$db->update('tablename', ['id' => $someUserId], ['key' => $currentKey]);
Can you help me, pls?
Promises are designed for that.
function generateUniqueKey(modelId) {
return new Promise((resolve, reject) => {
currentKey = someService.GenerateKey();
MyModel.count({ key: currentKey })
.then(count => {
if (count > 0) {
generateUniqueKey(modelId);
} else {
MyModel.update({ id: modelId }, { $set: { key: currentKey } })
.then(resolve(currentKey));
}
})
.catch(err => reject(err));
});
}
generateUniqueKey(someUserId).then(key => console.log('Generated unique key:', key));
But if you don't care about the result you can just remove the Promise mechanism and let the generateUniqueKey() method end on its own.
function generateUniqueKey(modelId) {
currentKey = someService.GenerateKey();
MyModel.count({ key: currentKey })
.then(count => {
if (count > 0) {
generateUniqueKey(modelId);
} else {
MyModel.update({ id: modelId }, { $set: { key: currentKey } })
.then(() => console.log('Generated unique key:', currentKey))
.catch(err => console.log(err));
}
})
.catch(err => console.log(err));
}
generateUniqueKey(someUserId);
Related
I'm trying to make a Mongoose query to run only once if the field inside the document hasn't been updated already, but I'm getting a bit lost with the $exist, $update, $ne - I don't know what to use really.
I have the following query:
const assignPhotoCodeToModel = (modelID, photoCode) => {
const filter = { id: modelID }
const update = { photoCode: photoCode }
const options = { new: true, useFindAndModify: false }
return new Promise((resolve, reject) => {
ModelModel.findOneAndUpdate(filter, update, options)
.then((response) => {
resolve(response)
})
.catch((error) => {
reject(error)
})
})
}
But I want this to run only if the field Model.photoCode is empty. If it already has a value, I just want it to return that value.
The problem I'm having is that every time the backend hits the route, a new photoCode gets assign to the model even if it has been already assigned. So it gets reassigned again again with a new one.
router.post(
'/payment/success',
(req, res, next) => {
...omitted_code...
photoCodeModel
.assignPhotoCodeToModel(req.body.modelId, photoCode)
... omitted_code
})
Edit 1
I'm writing this after trying the answers provided here. At first glance they seem to work, but now I'm getting into the error that when the Model.photoCode already has a value in it the return of the following function:
const assignPhotoCodeToModel = (modelID, photoCode) => {
//the difference is here
const filter = { id: modelID, photoCode : null }
const update = { $set: { photoCode: photoCode } }
const options = { new: true, useFindAndModify: false }
return new Promise((resolve, reject) => {
ModelModel.findOneAndUpdate(filter, update, options)
.then((response) => {
resolve(response)
})
.catch((error) => {
reject(error)
})
})
}
returns null, while I'm actually expecting the same model document to be returned. I'm guessing is because of the filtering? When I remove the photoCode : null it goes back to "working", but then I'm back to the problem that photoCode field gets populated with new values.
Basically I would like that function populates the filed photoCode once, but every subsequent call should just bring me back the same model document.
I think what you need is $set .
And, following your logic, I think you have to query "document that doesn't have photoCode yet".
It should be:
const assignPhotoCodeToModel = (modelID, photoCode) => {
//the difference is here
const filter = { id: modelID, photoCode : null }
const update = { $set: { photoCode: photoCode } }
const options = { new: true, useFindAndModify: false }
return new Promise((resolve, reject) => {
ModelModel.findOneAndUpdate(filter, update, options)
.then((response) => {
resolve(response)
})
.catch((error) => {
reject(error)
})
})
}
Write it in a neat asynchronous form. It will work.
const assignPhotoCodeToModel = async (modelID, photoCode) => {
const filter = { id: modelID, photoCode : null };
const update = { photoCode: photoCode };
const options = { new: true, useFindAndModify: false };
return await ModelModel.findOneAndUpdate(filter, update, options);
}
I have two collections Developer and project. I want to assign more then one project to developer but i don't want to repeatedly assign any project.
/*#Submit assign project to developer */
exports.putDevAssign = (req, res, next) => {
const {projectId, teamId} = req.body
Team.findById(teamId)
.then(result => {
if(result.project.length == 0 ) {
Team.findByIdAndUpdate(teamId, {$push: { project : projectId } }, { new: true }).populate('project')
.then(result => {
res.redirect('/Assigned-Developer/' + teamId)
})
.catch(err => console.log(err))
} else {
for(let i = 0; i < result.project.length; i++) {
if( projectId == result.project[i] ) {
req.flash('message', 'Already Assigned')
return res.redirect('/Assigned-Developer/' + teamId)
} else {
console.log('hello')
Team.findByIdAndUpdate(teamId, {$push: { project : projectId } }, { new: true }).populate('project')
.then(result => {
return res.redirect('/Assigned-Developer/' + teamId)
})
.catch(err => console.log(err))
}
}
}
})
.catch(err => console.log)
}
But in above code i can assign first project and it is neither repeatedly stored. but from second project it is stored repeatedly . I don't want this to happen. i want to assign only unique project. Help me with this silly error that i have made. Thank you in advance.
When you're comparing the existing projects, you're checking each element and adding the project if that element does not match. You should compare all the current projects before adding the new one.
Try this code:
/*#Submit assign project to developer */
exports.putDevAssign = (req, res, next) => {
const {projectId, teamId} = req.body
Team.findById(teamId)
.then(result => {
if(result.project.length == 0 ) {
Team.findByIdAndUpdate(teamId, {$push: { project : projectId } }, { new: true }).populate('project')
.then(result => {
res.redirect('/Assigned-Developer/' + teamId)
})
.catch(err => console.log(err))
} else {
for(let i = 0; i < result.project.length; i++) {
if( projectId == result.project[i] ) {
req.flash('message', 'Already Assigned')
return res.redirect('/Assigned-Developer/' + teamId) // project already in list
}
}
// if code reached here, project not in current project list, must add it
console.log('hello')
Team.findByIdAndUpdate(teamId, {$push: { project : projectId } }, { new: true }).populate('project')
.then(result => {
return res.redirect('/Assigned-Developer/' + teamId)
})
.catch(err => console.log(err))
}
}
})
.catch(err => console.log)
}
In my ReactJS app I'm fetching information from my NodeJS app to display on the page the album by id. When I add songs to my database it has to go through the POST request and add the info to the database but the database has a constraint where the index key has to be unique. What I'm trying to do in the code below is use Promises to select album by id then insert the data on that album_id so that the insert and constraint is only ran on that id. This doesn't work and the constraint is still ran on all objects the same. Does anyone have a solution as on what I could do in order to run the constraint separately so that different albums can have the same index?
const addSong = (request, response) => {
const id = parseInt(request.params.id)
const { name, link, index, album_id } = request.body;
let p = new Promise((resolve, reject) => {
if(true) {
resolve('Success')
} else {
reject('Failed')
}
})
for (let i = 0; i < request.body.length; i++) {
const object = request.body[i]
p.then(() => {
pool.query('SELECT * FROM songs WHERE album_id = $1', [album_id], (error, results) => {
if (error) {
throw error
} else {
console.log("SELECT " + JSON.stringify(request.body));
}
})
})
.then(() => {
pool.query(
'INSERT INTO songs (name, link, index, album_id) VALUES ($1, $2, $3, $4) ON CONFLICT (index) DO NOTHING RETURNING *',
[
object.name,
object.link,
object.index,
object.album_id,
],
(error, results) => {
if (error != null) {
console.log(error)
} else {
console.log('INSERT ' + JSON.stringify(request.body))
}
}
)
})
.catch(err => {
console.log(err)
})
}
}
I am trying to finish a login functionality with mysql and express. I got a work_id and a user_password, and I want to use the work_id to find whether the user exists in my database. I use promise to do this, I can log the selected user information in the console, but the promise is always pending, and the web storm console didn't terminate.
What I want is a boolean value from the promise, whether the user exists or not.
Here is my code:
query.js.
const pool = require('./connect');
module.exports = {
query: function (sqlString, params) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
reject(err)
} else {
connection.query(sqlString, params, (err, rows) => {
if (err) {
reject(err)
} else {
resolve(rows)
}
connection.release()
})
}
})
})
}
}
sqlCRUD.js, about the sql statement
const user = {
queryByWorkId: 'select * from user_info where work_id=?',
queryAll: 'select * from user_info',
resetPassword: 'update user_info set user_password = ? where work_id = ?',
};
user.js, I execute the test here.
const Model = require('./main')
const crypto = require('crypto')
const _ = require('./query')
const $sqlQuery = require('./sqlCRUD').user
class User{
// others
static findOne(form={}) {
const { work_id, user_password } = form
return _.query($sqlQuery.queryByWorkId, work_id)
.then(res => {
console.log(res)
if (res.length > 0) {
const u = res[0]
return u
}
return false
})
.catch(err => {
console.log('User.findOne error', err)
return {
errmsg: JSON.stringify(err)
}
})
}
Here is my test, in user.js
const test = () => {
const form = {
work_id: '007',
user_password: 'root',
}
const r = User.findOne(form)
console.log('r', r)
}
And this is the output:
I am not allowed to embed a picture here, so SO generates a link
I got confused about this: in my query.js file, I return a promise, in my User.findOne(form={}) method, I call it with a then and catch,
return _.query($sqlQuery.queryByWorkId, work_id).then(res => console.log(res)).catch(err => console.log(err)), but the console did't terminate, and I just got a Promise { }.
What's wrong with my code? How can I get a value returned from a then clause in promise when select data using mysql? Thanks in advance.
I am quite new to Node.js and haven't been working with json data before so really hope that you can help me.
I am trying to get all event information from Ticketmaster's API and add specific variables to mongoDB. However, the APIs' that I am currently using are limited to 200 events per page. It is therefore not possible for me to connect the event information with venue information since these are added seperately to mongoDB and are not exhaustive of all event and venue information (not able to connect on ids because of missing event and venue data).
My question is therefore in regards to how I can get all pages into my database at once?
The code that I have written so far looks something like below:
app.get('/tm', (req, res) => {
axios // getting venues
.get('https://app.ticketmaster.com/discovery/v2/venues.json?apikey=myApiKey&page=0&size=200&countryCode=DK')
.then(response => {
const venuesToBeInserted = response.data._embedded.venues.map(venue => { // preparing venues
return {
sourceID: venue.id,
venue: venue.name,
postalCode: venue.postalCode,
city: venue.city.name,
country: venue.country.name,
countryCode: venue.country.countryCode,
address: !!venue.address ? venue.address.line1 : null,
longitude: !!venue.location ? venue.location.longitude : null,
latitude: !!venue.location ? venue.location.latitude : null,
source: 'ticketmaster'
}
})
// Create all venues at once
Venue.create(venuesToBeInserted).then(venues => {
console.log("venues inserted")
axios // getting events and shows - note the page parameter in the api link
.get('https://app.ticketmaster.com/discovery/v2/events.json?apikey=myApiKey&countryCode=DK&size=200&page=0')
.then(response => {
const eventsToBeInserted = response.data._embedded.events.map(events => { // preparing events
const event = events._embedded.attractions[0]
return {
sourceID: event.id,
name: event.name,
slug: slugify(event.name).toLowerCase(),
tags: !!event.classifications ? [event.classifications[0].genre.name, event.classifications[0].subGenre.nam] : [], // duplicate genres occur
// possible tags from ticketmaster: type and subtype
}
})
// Create all events at once
Event.create(eventsToBeInserted).then(events => {
console.log("events inserted")
const showsToBeInserted = response.data._embedded.events.map(show => {
const event = events.find(event => event.sourceID == show._embedded.attractions[0].id);
const venue = venues.find(venue => venue.sourceID == show._embedded.venues[0].id);
if (!!event && !!venue) {
return {
event: event._id,
venue: venue._id,
timezone: show.dates.timezone,
dateStart: !!show.dates.start.dateTime ? show.dates.start.dateTime : show.dates.start.localDate,
tickets: !!show.priceRanges ? {
minPrice: show.priceRanges[0].min,
maxPrice: show.priceRanges[0].max,
currency: show.priceRanges[0].currency
}: {}
}
}
})
// Let's see what we have created in the database
Venue.find({}).select({
name: 1,
slug: -1
}).limit(10).populate('event').populate('venue').then(events => {
console.log(util.inspect(events));
}).catch(err => {
console.error(err);
});
}).catch( err => {
console.error(err)
})
}).catch( err => {
console.error(err)
})
}).catch(err => {
console.error(err)
});
}).catch(err => {
console.error(err)
})
})
EDIT
Using the approach that Jake suggested gave me an error (Error: Requested failed with status code 401). I have tried to search for it online but I cannot figure out why the error happens.. See picture below of part of the error message in my console.log.
error message
You can do this using promises, which you already are using, you just need to chain them together using recursion.
function getVenues(page, size, venues) {
page = page || 0;
size = size || 200;
venues = venues || [];
return axios
.get(`https://app.ticketmaster.com/discovery/v2/venues.json?apikey=myApiKey&page=${page}&size=${size}&countryCode=DK`)
.then(response => response.data._embedded.venues)
.then(rawVenues => {
rawVenues.forEach(venue => venues.push(venue));
if (rawVenues.length < size) {
// All done return the compiled list.
return venues;
}
// Recurse over the next set of venues by adding another promise to the chain.
return getVenues(page + 1, size, venues);
});
}
function getEvents(page, size, events) {
page = page || 0;
size = size || 200;
events = events || [];
return axios
.get(`https://app.ticketmaster.com/discovery/v2/events.json?apikey=myApiKey&countryCode=DK&size=${size}&page=${page}`)
.then(response => response.data._embedded.events)
.then(rawEvents => {
rawEvents.forEach(event => events.push(event));
if (rawEvents.length < size) {
// All done return the compiled list.
return events;
}
// Recurse over the next set of events by adding another promise to the chain.
return getEvents(page + 1, size, events);
});
}
app.get('/tm', (req, res) => {
getVenues().then(rawVenues => {
const venuesToBeInserted = rawVenues.map(venue => {
return {
sourceID: venue.id,
venue: venue.name,
postalCode: venue.postalCode,
city: venue.city.name,
country: venue.country.name,
countryCode: venue.country.countryCode,
address: !!venue.address ? venue.address.line1 : null,
longitude: !!venue.location ? venue.location.longitude : null,
latitude: !!venue.location ? venue.location.latitude : null,
source: 'ticketmaster'
};
});
// Return promise so errors bubble up the chain...
return Venue.create(venuesToBeInserted).then(venues => {
console.log("venues inserted");
// Return promise so errors bubble up the chain...
return getEvents().then(rawEvents => {
const eventsToBeInserted = rawEvents.map(rawEvent => {
const event = events._embedded.attractions[0];
return {
sourceID: event.id,
name: event.name,
slug: slugify(event.name).toLowerCase(),
tags: !!event.classifications ? [event.classifications[0].genre.name, event.classifications[0].subGenre.nam] : []
};
});
// Return promise so errors bubble up the chain...
return Event.create(eventsToBeInserted).then(events => {
console.log("events inserted");
const showsToBeInserted = rawEvents.map(show => {
const event = events.find(event => event.sourceID == show._embedded.attractions[0].id);
const venue = venues.find(venue => venue.sourceID == show._embedded.venues[0].id);
if (!!event && !!venue) {
return {
event: event._id,
venue: venue._id,
timezone: show.dates.timezone,
dateStart: !!show.dates.start.dateTime ? show.dates.start.dateTime : show.dates.start.localDate,
tickets: !!show.priceRanges ? {
minPrice: show.priceRanges[0].min,
maxPrice: show.priceRanges[0].max,
currency: show.priceRanges[0].currency
} : {}
}
}
});
// Do something with the found shows...
});
});
});
}).then(() => { // This then is fired after all of the promises above have resolved...
return Venue.find({}).select({
name: 1,
slug: -1
}).limit(10).populate('event').populate('venue').then(events => {
console.log(util.inspect(events));
res.send(events);
});
}).catch(err => { // Catches any error during execution.
console.error(err);
res.status(500).send(err);
});;
});