Problem
Node.js MongoDB library consistently returns undefined for collection.count({}). This question has been posted and answered numerous times, and I've been sure to go through all previous solutions, but none seem to work, and I always get undefined.
As a background to the question, I am making a Job Automator, and before adding a new job, I want to make sure that 0 records already exist in the database that have the same name as the new job being added (i.e. names are unique). EDIT: There are a few cases where I do want to allow duplicates, so I don't want using indexing and dis-allow duplicates at the DB level.
Code
In this case, the console.log() inside count just prints undefined. In this case, I have put in an empty query string as part of debugging (not currently testing for name collisions).
add: function(io, newJob)
{
//mc is where require('mongodb').MongoClient is saved
//connectionString contains a valid connection string
//activeCollection contains the name of the collection
//(I am sure mc, connectionString and activeCollection are valid)
//I know this as I have used them to insert documents in previous parts
mc.connect(connectionString, function(err,db)
{
if(err)
throw err;
else
{
db.collection(activeCollection, function(err,collection)
{
if(err)
throw err;
//Start of problematic part
//See also "What I've tried" section
collection.count({},function(err,count)
{
console.log(count);
});
//End of problematic part
//Omitting logic where I insert records for brevity,
//as I have confirmed that works previously.
});
}
db.close();
});
}
What I've tried
I've read the previous questions, and replaced the content between //Start of problematic part and //End of problematic part in the previous code block with the following blocks:
Fully breaking out the callback (also prints undefined):
function countDocs(callback)
{
collection.count({},function(err, count)
{
if(err)
throw err;
callback(null, count);
}
}
countDocs(function(err,count)
{
if(err)
throw err;
console.log(count);
});
I've even tried things I know wouldn't work
var count = collection.count({});
NEW (1/28/16)
It was a little sloppy of me to not check the error in my count(), so I added a if(err)console.log(err); into the count() block and it turns out the error is:
{ [MongoError: server localhost:27017 sockets closed]
name: 'MongoError',
message: 'server localhost 27017 sockets closed' }
Which I don't really understand because in other sections of code, I am able to use the same connect() and collection() calls and make inserts into the DB just fine. Any insight based on this?
Any help would be much appreciated!
Let's deal with the intent of the question:
I am making a Job Automator, and before adding a new job, I want to
make sure that 0 records already exist in the database that have the
same name as the new job being added (i.e. names are unique).
Rather than labor through javascript, just set a unique index on the name key. In mongo:
db.collection.ensureIndex( { name: 1 }, { unique: true } )
Then when you insert a document use a try/catch block to catch any attempt to create a document with a duplicate name.
You can use collection.find().toArray(function(err,documents) { ...});, which returns an empty array if no documents match your query. Checking the length property of the array should be equivalent to what you are trying to achieve with count().
More info in the docs: https://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#toArray
Related
I have this existing function:
const inferProcessingError = R.ifElse(
R.propEq('conversionJobStatus', 3),
R.always('Last Process failed with error; please contact DevOps'),
R.always(null)
);
which is called like this:
const msg = inferProcessingError(jobStruct || {});
with this jobStruct:
{"id":9,"mediaGroupId":1000000,"conversionJobStatus":3,
"errorDetails": {
"Cause": {
"errorMessage": "MediaConvert Job Failed with ERROR status: ERROR Video codec [indeo4] is not a supported input video codec",
},
"Error": "Error",
}
}
and I need to create an error message string which includes the data from the Cause.errorMessage element.
This would be dead simple with a native JavaScript function, but I'm learning Ramda and want to just modify the existing code to include in the error message.
An R.prop('Cause')['errorMessage'] could work except that I can't figure out how to reference the jobStruct that was passed in to the inferProcessingError statement.
I can see that the R.ifElse and subsequent Ramda functions are able to get that reference, but when I embed an R.prop('Cause') in the error message string, it resolves to a function and not the value of the Cause element because it seems to be waiting for the data structure.
So...how do I gain access to the jobStruct reference? (arguments is not defined here).
UPDATE:
I can get this to work by referencing the original jobStruct as in R.Prop('ErrorDetails', jobStruct)['Cause']['errorMessage'] but that seems rather kludgy to me...
BUT if the call to inferProcessingError is actually inside a map statement and references an element in a larger structure, then the map index is not available to reference the data structure for the R.prop.
Perhaps you could use the pipe and path methods to achieve this "the ramda way".
Begin by using ramda's path() function to extract the nested errorMessage value from the input jobStruct object. Next, enclose that in a pipe() that transforms the extracted message into a string formatted with a custom error prefix:
const incCount = R.ifElse(
R.propEq('conversionJobStatus', 3),
/* Evaluate this pipe if the error case is satisfied */
R.pipe(
/* Path to extract message from input object */
R.path(["errorDetails", "Cause", "errorMessage"]),
/* Prefix string to extracted error message */
R.concat('Custom error prefix:')),
R.always('')
);
incCount({"id":9,"mediaGroupId":1000000,"conversionJobStatus":3,
"errorDetails": {
"Cause": {
"errorMessage": "MediaConvert Job Failed with ERROR etc etc",
},
"Error": "Error",
}
});
Here's a working example - hope that helps!
Update
Thanks to #customcommander for the suggestion to use concat for the string prefix, as well as returning an empty string value for the second branch
I am writing a script that will take in all my Lambda functions and create a Node server for local testing. I'm trying to string out all of the dbconfig objects from each file. I use https://regexr.com/ to test out my patterns, and I have done a number of variations that all work in there, but they will not work in my script. I am at a loss as to why. The objects all look like this:
const dbconfig = {
server: process.env.SERVER,
userName: process.env.USER_NAME,
password: process.env.PASSWORD,
options: {
database: process.env.DATABASE,
table: process.env.TABLE,
encrypt: true,
requestTimeout: 300000,
rowCollectionOnRequestCompletion: true,
}
}
I have tried (amongst others):
/(.+[\n\r]).+process.env(.+[\n\r])+/g
/const dbconfig(.+[\s\n\r])+/g
/(.+\s).+process.env(.+\s)+/g
Each one of these matches the whole object declaration as expected, but in Node it will replace:
nothing, leaves it as is
the first line (const dbconfig = {
all of the lines that contain process.env but leave the rest
I have no idea why I would get the different results, and why it would fail. Any suggestions welcome!
Edit:
Sorry, not enough detail included. By replace I mean I am doing a substitution for an empty string ''.
I am reading in the files to an array inside a loop of directory names like this:
files.push(fs.readFileSync(`../lambda/${folder}/index.js`, {encoding: 'utf8'}));
I am pulling out the required libraries from each of these like this:
let imports = new Set();
let arr;
files.forEach((file, idx) => {
while ((arr = replaceOptions.from[0].exec(file)) !== null) {
imports.add(arr[0]);
}
});
I then join the files into a master single file, strip out the requires and trying to do these configs, and will append a single copy at the top:
fs.writeFileSync('joined.js', files.join('\n'));
try {
const results = replace.sync(replaceOptions);
console.log('Replacement results:', results);
}
catch (error) {
console.error('Error occurred:', error);
}
fs.writeFileSync('server.js', Array.from(imports.values()).join('\n'));
fs.appendFileSync('server.js', fs.readFileSync('joined.js'));
I was formerly doing the string replace myself, but then ended up using npm package replace-in-file jsut for ease / in case I was stuffing up something there.
And yes, I do realise this code is a bit messy and inefficient currently, it's just been iterations of trying to get a base achievement of the desired outcome.
I still don't know why I would get the difference between an online regex builder/tester and Node, but there you go. I do now have a working implementation:
/(.*\s*).+process\.env(.*\s*)*?\}\s*\}/gm
I'm trying to put dynamically function call in Node.js in order to avoid a infinite list of switch-case.
Here a instance of file I want to reach :
var Object = require('../models/Object'); // it's a classic mongoose Schema's object
Here my dynamic function :
/***** post words *****/
router.post('/words', function (req, res) {
var word = req.body ;
var Theme = req.body.theme
[Theme].add[Theme](word, function(err, words) {
if(err) {
res.json(err);
throw err;
}
return res.json(words);
})
})
We suppose a case where the object.name is "Object".
The function I want to build have to call an addPost method in an another file. In order to dynamically call this files I have prepared a computed name property mechanism. Hence I always call the file corresponding to my need. If I have required Object and the user post with a Object's category value, okay it will go on the Object file to compute the Post toward the good destination. So my computed name property look as following :
console.log(req.body.name) // > "Object"
Name = req.body.name
console.log(Name) // > "Object"
Okay now,
When I enter explicitly the name of the object to cal the function, as following :
Object.functionObject()
the function works fine.
BUT if I use a dynamically value call, like :
[Name].functionOf[Name]()
the function returns me :
TypeError: Cannot read property of undefined
Also,
I have try with ${Name} but my console returns me :
TypeError: console.log(...) is not a function
I have tried to install ES6 on Node.js's side sames result for now.
How it is possible since the console.log return me the great value ?
[Theme].add[Theme]()
This code creates an array with Theme as the first value and then calls functionOf, which doesn't exist on the array type. addTheme is actually a syntax error, but the engine never reaches it.
Although I recommend using a switch, I think what you're trying to do is something like this
// define Object1, Object2 with functionObject()
var objectCollection = {};
objectCollection[Object1.name] = Object1;
objectCollection[Object2.name] = Object2;
// Then you get req.body.name later
var objectName = req.body.name;
// and call the method
objectCollection[objectName].functionObject()
This will technically work, but there are a lots of failure possibilities and relying on reflection in this way makes me nervous because your client needs to know the inner workings (object and function names for instance). This is bad.
If the behavior of these objects is that different you should probably separate these into separate routes and let your client decide which it needs if you're making an api.
You could also create a factory to return the proper object and bury the switch statement there.
I am assuming Object contains all those methods
router.post('/words', function (req, res){
var word = req.body ;
var Theme = req.body.theme; // Post
// Object.addPost();
Object[`add${Theme}`](word, function(err, words) {
if(err){
res.json(err) ;
throw err;
}
return res.json(words);
});
// if you want Object.Post.addPost then
Object[Theme][`add${Theme}`](word, function(err, words) {
if(err){
res.json(err) ;
throw err;
}
return res.json(words);
});
});
Been trying to debug this for over a day. Any help is very much appreciated.
In cloud code, I am attempting to invoke Parse.Object.fetchAll() on an array of Parse.Role pointers like so:
function loadRoles (company) {
var roles = company.get('roles');
console.log(roles.length); // 1
console.log(roles[0].isNew()); // false
promise = Parse.Object.fetchAll(roles)
.fail(function(error){
// This gets triggered with error
// {code: 101, message: 'All objects must exist on the server'}
});
}
Given that the roles array contains a single Parse.Role that is both not isNew(), and has an id, I have no idea why I'm getting this error. I've tinkered with this for more than a day and can't seem to figure it out. Any help would be much appreciated.
These roles have to exist on the server. Missing roles will cause that error if any of those objects have been removed in another operation.
EDIT: Use Parse.Object.fetchAllIfNeeded(roles) to avoid the error.
var db = new Dexie(app.settings.unpublishedBooksDb);
db.version(1).stores({
friends: "++id,name,shoeSize"
});
db.open();
db.close();
I have a precreated indexedDB database using the code above, and then on another view in the application, I need to add a row to a table.
var db = new Dexie('myDb');
db.open().then(function() {
console.log ('opened'); //this works
db.friends.add({name:"Fredrik"}); //this doesnt do anything and adding a catch doesn't throw an error either
}).finally(function () {
db.close();
});
I tried using .transaction but still the same. If I try using Chrome's console, I get an error : Cannot read property add of undefined
your second db instance contains no info about what tables it would contain. So the implicit table property (db.friends) is not there. What actually happens is that it throws TypeError: cannot read property 'add' of undefined. If you would catch the call (not just do a finally), you would get that TypeError catched.
What you can do is to reference the friends table by db.table('friends').add ({name: 'Fredrik'}) instead of db.friends.add({name: 'Fredrik'}).
Beware though that defining the database without specifying table schema is not as thorowgly tested and used, so I would recommend using it with a schema defined to avoid other pitfalls as well. If you for architectural reasons still need to do it your way, be aware that transaction scopes works a little different since you cannot use the dynamic implicit tale properties in the transaction scopes either and db.table() currently does not return a transaction-bound Table instance if you are in a transaction scope. You would have to use the old transaction API:
db.transaction('rw', 'friends', function (friends, trans) {
friends.put({name: 'Fredrik'});
});
...instead of:
db.transaction('rw', 'friends', function () {
db.friends.put({name: 'Fredrik'});
});
Best wishes,
David