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);
?
Related
I am trying to query Dynomo DB table and I want to go through over the resulting items in a function in my AWS Lambda. I am not able to extract result from Dynamo DB query. It is inside the closure, I am able to console log it, but I am not able to assign it for any variable in the scope of outer function.
What should I do to get it outside?
function check(id) {
//build params
let params = {
TableName: 'demo_table',
KeyConditionExpression: #key =: id,
Limit: 5,
ScanIndexForward: false,
ExpressionAttributeNames: {
#key: process.env.PRIMARYKEY
},
ExpressionAttributeValues: {
: id: id
}
};
//query ddb
let result = {};
ddb.query(params, function(err, data) {
if (err) {
console.log("AN ERROR OCCURED\n");
console.log(err);
} else {
//How to copy the data from here to outside??
//I can console log and see the data
result = data;
}
});
console.log(result); //returns {}
}
const check = async (id) => {
//build params
let params = {
TableName: 'demo_table',
KeyConditionExpression: #key =: id,
Limit: 5,
ScanIndexForward: false,
ExpressionAttributeNames: {
#
key: process.env.PRIMARYKEY
},
ExpressionAttributeValues: {
: id: id
}
};
let result = await new Promise((resolve, rejects) => {
ddb.query(params, function (err, data) {
if (err) rejects(err)
resolve(data)
});
})
console.log(result); //returns {}
}
By using promises you can get the data. database read is an asyncronous operation.
I beginner in Sails JS. I try to make multiple .query("SELECT ... "). I have a problem when looks like it not run sequence. I will explain my problem, look to My snippet code :
var isSuccess = true;
for(var loc = 0 ; loc < decode.length ; loc++){
Location.query('SELECT * FROM abc', function (err, res){
if (err) {
isSuccess = false;
return res.json(200, {
data: {
},
status: {
id: 0,
message: err.message
}
});
} else {
var validate = res.rows;
sails.log(validate);
secondQuery.query('SELECT * FROM def', function (err, seconResult) {
if (err) {
isSuccess = false;
sails.log("Update is Success : "+isSuccess);
return res.json(200, {
data: {
},
status: {
id: 0,
message: err.message
}
});
} else {
}
});
}
});
if(isSuccess){
return res.json(200, {
data: {
},
status: {
id: 1,
message: "Successful",
isSuccess: isSuccess
}
});
}
}
When i request the API via POST method and the result is :
On Console Node JS Server :
Update is Success : false
So it means the request is failed and must be return status = 0 in postman
But in the Postman, it show :
data: {
},
status: {
id: 1,
message: "Successful",
isSuccess: true
}
My Question :
Can anyone help me to explain why and how to make it a sequence process, not like "multi thread" ? (In my case, i need to use RAW query cause i will face with really complex query)
Thank you.
A for loop is synchronous while Location.query() method is not.
So if you want to do it in sequence, you'll have to use Bluebird Promise.
Example (not tested) :
const Bluebird = require('bluebird');
const promises = [];
for(let loc = 0 ; loc < decode.length ; loc++) {
promises.push(Location.query('SELECT ...'));
}
Bluebird.each(promises, (result) => res.json(200, {/* your payload data */}))
.catch((err) => res.json(200, {/* your error data */}));
NB : Be careful when you use res variable in your callbacks when you use it in Sails controllers.
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){
...
})
}
I'm using bluebird.
I've also used bluebird's Promisify for the models.
var Promise = require('bluebird');
var mongoose = Promise.promisifyAll(require('mongoose'));
var Collection = Promise.promisifyAll(require('../models/collection'));
var Vote = Promise.promisifyAll(require('../models/vote'));
throughout my project, it has been working successfully, but for some reason, I cannot get it to return the collections value on this 'save' method.
Here is my model:
var CollectionSchema = new mongoose.Schema({
user : {type: mongoose.Schema.ObjectId, ref: 'User', required: true},
whiskey : {type: mongoose.Schema.ObjectId, ref: 'Whiskey', required: true},
favorite: {type: Boolean, default: false},
timestamp: { type : Date, default: Date.now }
});
CollectionSchema.statics.createCollection = function(o) {
console.log('hit model')
return Collection
.findAsync(o)
.then(function(existing) {
console.log('existing collection ', existing)
if (existing.length) {
return{
message: 'already collected'
}
} else {
console.log('no existing collections found')
return Collection
.saveAsync(o)
.then(function(collection) {
console.log('new collection / does not console.log ', collection)
return {
collection: collection
};
});
}
})
};
Here is the controller, where the collectionCreate method is invoked and expecting a response 'data' from the promise. However, the saveAsync mongoose method does not seems to invoke or return :
exports.create = function(req, res){
console.log('init')
console.log('init body ', req.body)
Collection.createCollectionAsync({user: req.user._id, whiskey: req.body.whiskey}).then(function(data){
console.log('collection promise ', data)
res.send(data);
})
};
I could really use a second set of eyes to point out where I went wrong.
You are not supposed to use the …Async promisified versions of functions that already return promises. That will only lead to Bluebird passing in an additional callback that is never called.
Collection.createCollection({user: req.user._id, whiskey: req.body.whiskey}).then(function(data){
res.send(data);
}, function(err) {
…
})
Hi My task is to create an remote method using strongloop & execute an mongoDB function for the rest API. my mongoDB is like
"db.users.count({user_email_id :"bhargavb#ideaentity.com",user_password:"5f4dcc3b5aa765d61d8327deb882cf99",isdeleted:1,user_status:1});
if (count=1) then"
to execute this in strongloop I'm trying something like
module.exports = function(GetUserFind) {
var server = require('../../server/server');
var db = server.dataSources.MongoTours;
db.connect(function(err, db) {
if (err) return console.log('error opening db, err = ', err);
console.log("db opened!");
GetUserFind.login = function(par,par2,par3,par4,cb){
console.log(par,par2,par3,par4);
db.collection('users', function(err, collection) {
console.log("the collection is"+ collection);
if (err) return console.log('error opening users collection, err = ', err);
collection.count ({user_email:par}, function(err, result) {
if(err) {
console.error(err);
return;
}
cb(null,result);
});
cb(null,par,par2,par3,par4);
});
}
});
GetUserFind.remoteMethod(
'login',
{
accepts: [{arg: 'user_email_id', type: 'string'},
{arg: 'user_password', type: 'string'},
{arg: 'isdeleted', type: 'number'},
{arg: 'user_status', type: 'number'}],
returns: {arg: 'result', type: 'object'},
http: {path:'/Loogin', verb: 'get'}
}
);
// db.close();
}
but I'm not able to do the same, can anyone tell me how can i execute mongoDB statements in strongloop remote methods
thanks in advance
I tried it the following way,
module.exports = function(GetUserFind) {
var response = 'User does not exists';
var server = require('../../server/server');
var sha256 = require('js-sha256');
// getUser function login declaration
GetUserFind.login = function(par, par2, cb) {
console.log(par, par2);
// calling users collection method
var MongoClient = require('mongodb').MongoClient;
var self = this;
this.callback = cb;
// to connect to mongoDB datasource
MongoClient.connect('mongodb://192.168.0.78:27017/tours', function(err,
db) {
console.log(sha256(par2));
if (err) {
// try{
console.log(err);
}
db.collection('users').find({
user_email_id : par,
user_password : sha256(par2)
},{user_password:0}).toArray(function(err, result) {
//https://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/
if (err) {
throw err;
}
if (result.length > 0) {
self.callback(null, result);
// db.disconnect();
} else {
self.callback(null, response);
// db.disconnect();
}
});
});
}
GetUserFind.remoteMethod('login', {
// passing input parameters
accepts : [ {
arg : 'user_email_id',
type : 'string'
}, {
arg : 'user_password',
type : 'string'
} ],
// getting output parameters
// returns: {arg: 'user_email_id', type: 'string'},
returns : [ {
arg : 'result',
type : 'object'
} ],
http : {
path : '/Loogin',
verb : 'get'
}
});
// db.close();
};
This may help other.
If any one knows better & easy way, Please post or update the answer