I am writing an API in NodeJS in which I use Mongoose and BlueBird. Regarding promise chain, my data was supposed to go through waterfall functions but it didn't. Let my example start with getTagNames to get some JSON , feeding data to retrieveTag to query and end up with res.json().
exports.getTagValues = function (req, res) {
var userId = req.params.uid;
getTagNames(req, res)
.then(retrieveTag)
.then(function (data) {
console.log('tags', data);
res.json(200, data);
})
.catch(function(err){
console.log('err', err);
//handle Error
})
}
Here is my toy data,
function getTagNames(req, res) {
var userId = req.params.uid;
return new Promise.resolve({
'userId': userId,
'variables': [
{ id: 1, name: 'hotel', type: 'String' },
{ id: 2, name: 'location', type: 'String' }
],
})
}
The way I query data. After querying inside mongo, I check whether or not have a document with userID. In case not, insert and return document. Note Tag is my mongo model
function retrieveTag(data){
Tag.findOne({'userId': data.userId})
.exec()
.then( function(tag){
if (tag) {
console.log('result', tag);
// do something ...
return tag;
}
else {
var newTag = new Tag({
advertiserId: advertiserId,
variables: variables
});
newTag.save()
.then(function () {
console.log('newTag', newTag);
return newTag;
});
}
})
}
Here is my result (userId is 1), my expectation is console.log('tags', data); occurs after all then data should not be undefined
tags undefined
GET /api/tag/values/1 200 3ms
newTag { __v: 0,
userId: '1',
_id: 581b96090e5916cf3f5112fe,
variables:
[ { type: 'String', name: 'hotel', id: 1 },
{ type: 'String', name: 'location', id: 2 } ] }
My question is how can I fix it. If there's some unclear, please help me correct.
The explanation is a bit unclear, but if I follow you right you loose data in the promise resolvement chain.
When reading your code, I notice that retrieveTag does not return the Mongoose promise. To let .then in getTagValues use the data found in retrieveTag.
So change to this:
function retrieveTag(data){
return Tag.findOne({'userId': data.userId})
.exec()
.then( function(tag){
...
})
}
Related
updateProfile: async function(req, res) {
try {
const update = req.body;
const id = req.params.id;
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
const image = req.files.profileImage;
const cloudFile = await upload(image.tempFilePath);
const profileImage = cloudFile.url
console.log('Loging cloudfile', profileImage)
await User.updateOne(id, { update }, { profileImage }, { new: true },
function(err, doc) {
if (err) {
console.log(err)
}
if (doc) {
return res.status(200).send({ sucess: true, msg: 'Profile updated successful' })
}
});
} catch (error) {
res.status(500).json({ msg: error.message });
}
}
But I'm getting an error of "Callback must be a function, got [object Object]"
I have tried to $set: update and $set: profileImage but still not working.
So the image successful upload into the cloudinary but the update for mongoose is not working.
Upon brief research into the issue, I think you are feeding the arguments in wrong. Objects can be confusing but not to worry.
Your code is:
await User.updateOne(id, { update }, { profileImage }, { new: true }
However, I believe it should be something more like:
await User.updateOne({id: id}, { profileImagine: profileImage, new: true },
The API reference annotates use of the function as:
const filter = { name: 'John Doe' };
const update = { age: 30 };
const oldDocument = await User.updateOne(filter, update);
oldDocument.n; // Number of documents matched
oldDocument.nModified; // Number of documents modified
Am trying to update some data in the mangodb database using node js but the document won't update. I get this log message { n: 0, nModified: 0, ok: 1 }
Here is my code:
I connect successfully to the data
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/cinemas')
.then(()=> console.log('connected to mongoDB....'))
.catch(err => console.log('Could not connect to mongoDB because',err));
Here is the schema
const moviesSchema = new mongoose.Schema({
title: String,
acotrs:[String],
genre: [String],
date: {type: Date, default: Date.now},
onCinema :Boolean
});
Model connection
const Movie = mongoose.model('Movie',movieSchema);
I can search the database with no issues, the only issue is the update function which does not update the database. Here is the function:
async function updateMovie(id){
const result = await Movie.update({_id: id},{
$set:{
title: 'New Movie',
onCinema: false
}
});
console.log(result);
}
updateMovie('5a68fdf95db93f6477053ddd');
I get this log on the console { n: 0, nModified: 0, ok: 1 } which tells me nothing was updated. Please help. What am I doing wrong?
const result = await Movie.update({_id: id},{
$set:{
title: 'New Movie',
onCinema: false
}
});
The update function gives output like this.
{ n: 0, nModified: 0, ok: 1 }
try with the findByIdAndUpdate
const result = await Movie.findByIdAndUpdate(id,{
$set:{
title: 'New Movie',
onCinema: false
}
});
it will give the exact results in json format.
The id you have given is in string type and _id is an ObjectId so adding mongoose.Types.ObjectId(id) should solve the issue.
async function updateMovie(id){
const result = await Movie.update({_id: mongoose.Types.ObjectId(id)},{
$set:{
"title": 'New Movie',
"onCinema": false
}
});
console.log(result);
}
updateMovie('5a68fdf95db93f6477053ddd');
Try This:
`MongoClient.connect(url, { useNewUrlParser: true }, (err, client) => {
let queryId = { _id: this.id }; //Get the ID of the object
let myObj = {
$set: {
name: 'Somename' //Whatever you want to change for that ID
}
var db = client.db("database_name");
db.collection("collection_name").updateOne(queryId, myObj, (err, res) => {
if (err) {
console.log("not Updated");
}
else{
console.log("Updated");
}
});
}`
Let me know if you have any other questions.
I'm stuck in my GraphQL resolver fetching todo-lists for a particular user belonging to a company. According to whether or not they have access to all todo-lists or a certain few, it will fetch for groups the user registered to that have belonging todo-lists, and those should be fetched.
The code so far is capable of logging the requested todo-lists on the query but I have yet to come to the solution on how to actually return data of all of the user's registered groups's todo-lists.
I chose to export the actual logic into a separate function
The Resolver:
allowedListItems: {
type: new GraphQLList(TodoItem),
resolve(parentValue, args) {
return Promise.all([fetchAllowedItems(parentValue._id)]);
}
},
The Promise Function
function fetchAllowedItems(userId) {
return User.findOne({ _id: userId }).then((user) => {
if (user.todoGroups) {
return user.todoGroups.map((groupId) => {
return TodoGroup.findOne({ _id: groupId }).then(group => {
return group.todoLists.map((listId) => {
return TodoList.findOne({ _id: listId })
})
})
})
} else {
return TodoList.find({ company: parentValue.company }).exec();
}
})
}
I am not getting any errors from GraphQL so I guess it's about the way I make the promisses return to the resolver, I'd appreciate a lot if you can help me out!
Update:
I should wrap the maps with a Promise.all, as the mapping returns an array.
Though the updated code brings no improvement in the returned data.
async resolve(parentValue, args) {
let user = await User.findOne({ _id: parentValue._id })
if (user.todoGroups) {
return Promise.all(user.todoGroups.map((groupId) => {
return TodoGroup.findOne({ _id: groupId }).then(group => {
return Promise.all(group.todoLists.map((listId) => {
return TodoList.findOne({ _id: listId });
}))
})
}))
} else {
return TodoList.find({ company: parentValue.company }).exec();
}
}
},
Current query result:
{
"data": {
"user": {
"_id": "5ba11690ad7a93d2b34d21a9",
"allowedTodos": [
{
"_id": null,
"title": null
}
]
}
}
}
You need to call Promise.all on an array of promises, not a promise for that. Also you'll have to call it on each level:
allowedListItems: {
type: new GraphQLList(TodoItem),
resolve(parentValue, args) {
return User.findOne({ _id: parentValue._id }).then(user => {
if (user.todoGroups) {
return Promise.all(user.todoGroups.map(groupId => {
// ^^^^^^^^^^^^
return TodoGroup.findOne({ _id: groupId }).then(group => {
return Promise.all(group.todoLists.map(listId => {
// ^^^^^^^^^^^^
return TodoList.findOne({ _id: listId })
}));
});
}));
} else {
return TodoList.find({ company: parentValue.company }).exec();
}
});
}
}
so I have an array from another function that passes res which is a list looking like this:
[ RowDataPacket { UserID: 26 }, RowDataPacker { UserID: 4 } ]
it stores user id's, what I want is a function that finds the user id's username, and stores them in another array. This is what I have:
function getThem(res, params) {
var promises = res.map(function (item) { // return array of promises
// return the promise:
for (i = 0; i < Object.keys(res).length; i++) {
console.log("user: ", res[i].UserId);
getUsernameFromId(res[users.length].UserId).then(function() {
console.log("username: ", res[0].username);
users.push(res[0].username);
});
}
}, function (err) {
console.error(err);
});
Promise.all(promises).then(function () {
console.log("users: ", users);
//do something with the finalized list of albums here
});
}
output in console:
user: 26
user: 4
user: 26
user: 4
users: []
username: undefined
username: undefined
username: undefined
username: undefined
so how can I wait for the for loop to complete the mysql call? Maybe there is another way of doing this?
edit: don't mind the undefined usernames, it's easy to fix later. Just tell me how I can have those undefined inside an array
Assuming (have to assume, because your code seems to use res like a majick object that has everything you need before you do anything with it) the actual res looks like
[ { UserID: 26 }, { UserID: 4 } ]
and getUsernameFromId returns an object with a username property, like
{ username: 'blah', ...otherproperties }
getThem can be simply
function getThem(res, params) {
return Promise.all(res.map(({UserID}) => getUsernameFromId(UserId).then(({username}) => username)))
.then(users => {
console.log("users: ", users);
//do something with the finalized list of albums here
});
}
or in "old school" javascript
function getThem(res, params) {
return Promise.all(res.map(function (_ref) {
var UserID = _ref.UserID;
return getUsernameFromId(UserId).then(function (_ref2) {
var username = _ref2.username;
return username;
});
})).then(function (users) {
console.log("users: ", users);
//do something with the finalized list of albums here
});
}
I'm trying to write test (spec) on a mongo DB wrapper and stumbled on this weird issue.
My code, build on top of a thin wrapper of mongodb, expose _id as string to the world but use (convert) them to ObjectId when talking to mongo.
I've an helper creating fixtures:
var _ = require('lodash'),
Promise = require('bluebird'),
MongoDb = require('mongodb');
var fixtureData = [
{
'uuid': '1',
'owner': 'A',
'data': 'someData1'
},
{
'uuid': '2',
'owner': 'A',
'data': 'someData2'
},
{
'uuid': '3',
'owner': 'B',
'data': 'someData3'
},
{
'uuid': '4',
'owner': 'A',
'data': 'someData4'
},
{
'uuid': '5',
'owner': 'A',
'data': 'someData5'
},
{
'uuid': '6',
'owner': 'B',
'data': 'someData6'
}
]
module.exports.loadFixtures = function (url, collectionName) {
var MongoClient = MongoDb.MongoClient;
return MongoClient.connect(url, {
promiseLibrary: Promise
}).then(function (db) {
return db.dropCollection(collectionName)
.catch(function (err) {
if (err.message === 'ns not found') {
return 'does not exist';
}
throw err;
})
.then(function () {
return db.collection(collectionName).insertMany(fixtureData);
}).then(function (result) {
_.forEach(result.insertedIds, function (value, idx) {
fixtureData[idx]._id = value;
});
return db;
});
}).then(function (db) {
db.close();
return fixtureData;
});
};
I use jasmine to test and I call this at every beforeEach to always start each test with the same exact situation.
I then have a function to test the delete (simplyfing):
var dataToDelete = fixtureData[0];
sut.deleteDocument(dataToDelete_.id)
.then(function(result) {
expect(....);
});
Inside my deleteDocument I do nothing special:
db.collection('myCollection').deleteOne({ _id: theId })
then(function(result)) {
if (result.deletedCount === 0) {
throw new Error('No document to delete with ID: ' + _id);
}
return null;
});
The theId variable here is obtained converting in a mongo ObjectId the id passed as parameter with a very simple function:
function (id) {
if (_.isString(id)) {
return MongoDb.ObjectId(id);
}
if (MongoDb.ObjectId.isValid(id) === true) {
return id;
}
throw new Error('Invalid ObjectId');
};
I'm using mongodb Node.js driver version 2.2.16.
The problem here is that I ALWAYS receive a deletedCount = 0 if I use an ObjectId as _id but if I covert it to String it works and delete the function.
This completely puzzle me because every documentation I've found and every example always say _id is a ObjectId.
Can someone explain what's going on?
EDIT: (the answer got me in the right direction but this is the actual anwer you are looking for if you end up in this situation) if you end up in this situation you are passing strings in the _id field when creating the document. Find out why you do that if it is not intended and you'll fix it
Are you sure your fixtures aren't mangled between different tests?
And by the way shouldn't
return MongoDb.ObjectId(id);
be
return new MongoDb.ObjectId(id);
?