Let's say I have this schema
{
jedi: [{
name:String
lightsaber_color:String
]}
}
I want to return all and only the names of them.
I tried
Jedi.find({})
.select('jedi.name')
.exec(function (err, jedi) {
if (err) {
console.log("nothing found")
}
}
It returns me nothing, while this code returns me everything.
Jedi.find({})
.select('jedi')
.exec(function (err, jedi) {
if (err) {
console.log("nothing found")
}
}
I see that jedi is an array so I think that .select('jedi.name') may not work for this reason.
What is the right syntax to do so?
You can try with this
Jedi.find({}, {'jedi.name':1}, function (err, jedi) {
if (err) {
console.log("nothing found")
}
else{
console.log(jedi);
}
}
Related
My code is currently returning as 'undefined';
function findDoc(rowObj) {
Database.findOne(
{ 'name': rowObj.name },
'_id, foreign_id'
).exec((err, docs) => {
if (err) {
throw err;
} else {
return docs;
});
}
try {
console.log('before');
let searchDoc = await findDoc(rowObj);
console.log(searchDoc);
console.log('after');
} catch (err) {
console.log(err);
}
This outputs;
before
undefined
after
whereas if i change the findDoc function to console.log(docs); rather than return docs i get;
before
CORRECT OBJECT
after
How can i fix this to get the second result using the first structure where it returns to the main try catch block?
Thanks
You should either wrap your function with async or use a callback as follows :
function findDoc(rowObj,callback) {
Database.findOne(
{ 'name': rowObj.name },
'_id, foreign_id'
).exec((err, docs) => {
if (err) {
throw err;
} else {
callback(docs);
});
}
try {
console.log('before');
findDoc(rowObj,(searchDoc) => {
console.log(searchDoc);
console.log('after');
});
} catch (err) {
console.log(err);
}
Background
I have a NodeJS app that is meant to be used as a RESTful API. It is connected with a MongoDB database in the backend using Mongoose. The app is built upon the idea of nested documents. It stores wikis, sections and notes with the following schema:
const noteSchema = new mongoose.Schema({ title: String, content: String });
const sectionSchema = new mongoose.Schema({ title: String, notes: [noteSchema] });
const wikiSchema = new mongoose.Schema({ title: String, sections: [sectionSchema] });
All of which are accessed via a single model of the wiki:
const wikiModel = mongoose.model("Wiki", wikiSchema);
A user can do GET, POST, PUT, DELETE requests on each of the endpoints to manipulate the data inside. If someone wants to ping the Notes endpoint (the furthest down in the hierarchy), it must first check the wiki and then the section endpoint, to ensure that each of them exists.
Here's an example:
app.get('/:wikiTitle/:sectionTitle/:noteTitle', function(req, res) {
wikiModel.findOne({ title: req.params.wikiTitle }, function(err, wiki) {
if (err) {
res.send('\nAn unkown error has occured');
console.error(err);
} else if (wiki) {
const sectionTitle = req.params.sectionTitle;
wikiModel.findOne({ 'sections.title': sectionTitle }, function(err, section) {
if (err) {
res.send('\nAn unkown error has occured');
console.error(err);
} else if (section) {
const noteTitle = req.params.noteTitle;
wikiModel.findOne({ 'sections.notes.title': noteTitle }, function(err, n) {
if (err) {
res.send('\nAn unkown error has occured');
console.error(err);
} else if (n) {
const section = n.sections.find((s) => { return s.title === sectionTitle; });
const note = section.notes.find((n) => { return n.title === noteTitle; });
if (note.content) {
res.send('\n' + note.title + '\n\n' + note.content);
} else {
res.send('\n' + note.title + '\n\n[ No content to show ]');
}
} else {
res.send('\nNo such note exists');
}
});
} else {
res.send('\nNo such section exists');
}
});
} else {
res.send('\nNo such wiki exists');
}
});
});
This is a very lengthy method and the first two queries are actually frequently throughout the app. I also understand a MongoDB query is an asynchronous operation and thus, why I put each consequent MongoDB query within it's parent (the one I wish to finish before that one begins).
Question
Is there a way to split each MongoDB query into its own method or introduce promises in a way that would shorten the code? I would rather prefer advice that ultimately causes the splitting of my code into individual methods as what you see above is one of many endpoints which all use the same queries.
So in the end result I would like to have something close to the likes of:
app.get('/:wikiTitle/:sectionTitle/:noteTitle', function(req, res) {
if (getWiki(req.params.wikiTitle)) {
// Continue with second query
if (getSection(req.params.sectionTitle)) {
// Continue with third query...
}
}
});
function getWiki(wikiTitle) {
wikiModel.findOne({ title: wikiTitle }, function(err, wiki) {
if (err) {
console.error(err);
res.send('An unknown error occured.');
} else if (wiki) {
// Send OK result to continue to next query
return wiki
} else {
res.send('No wiki found');
return null;
}
});
}
function getSection(sectionTitle) {
wikiModel.findOne({ 'sections.title': sectionTitle }, function(err, section) {
if (err) {
console.error(err);
res.send('An unknown error occured.');
} else if (section) {
// Send OK result to continue to next query
return section
} else {
res.send('No section found');
return null;
}
});
}
I am hoping this will significantly cut the length of code and also utilise re-usability of code. Any advice on how I could come close to achieving something like this is welcome.
You can definitely use callbacks in the same way as the ones call your model. For example:
app.get('/:wikiTitle/:sectionTitle/:noteTitle', function(req, res) {
getWiki(req.params.wikiTitle, function (err, title) {
if (err) {
return res.send(err);
}
getSection(req.params.sectionTitle, function (err, section) {
if (err) {
return res.send(err);
}
// Todo: use title and section, etc...
});
});
});
function getWiki(wikiTitle, cb) {
wikiModel.findOne({ title: wikiTitle }, function(err, wiki) {
if (err) {
console.error(err);
return cb('An unknown error occured.');
} else if (wiki) {
// Send OK result to continue to next query
return cb(null, wiki);
} else {
return cb('No wiki found');
}
});
}
function getSection(sectionTitle, cb) {
wikiModel.findOne({ 'sections.title': sectionTitle }, function(err, section) {
if (err) {
console.error(err);
return cb('An unknown error occured.');
} else if (section) {
// Send OK result to continue to next query
return cb(null, section);
} else {
return cb('No section found');
}
});
}
This is a standard way of using async functions in node. By convention, the first parameter is always an error parameter.
If you want your code to be cleaner, you can try to use guard clauses / early outs to exit error cases early. This will cut down on your need for if / else conditional statements.
You can also look into libraries like async for cleaner chaining of asynchronous calls.
When you are comfortable, you can also look into using promises and the 'async' javascript keyword (different from the async library above, confusing, I know) which will also allow you to cut down on the lines of code you have to write to get nice async code.
You should use async functions (Promises) like
app.get('somePath', async (req, res, next) => {
try {
const doc = await model.find({ someField: 'some value' }).exec(); // exec returns promise
res.send({ document: doc });
} catch (error) {
// here you can handle all errors or/and call next for the error middleware
next(error);
}
});
I am trying to asynchronously retrieve some data from a database and then append those values in an array I need for some other things, I have this function:
function retrieveProduct(SKU, callback) {
Products.find({
SKU: key
}, function (err, doc) {
if (err) {
callback(err, null);
} else {
callback(null, doc[0]);
}
});
}
and I use it like this:
for (var key in orders) {
retrieveProduct(key, function (err, doc) {
if (err) {
console.log(err);
}
products_found[key] = doc
console.log(doc);
});
}
console.log(products_found);
}
Where orders is a list of IDs.
The problem is that when I try to append doc to products_found nothing is being appended and nothing is logged to the console. Moreover, ESlint is telling me not to make functions within a loop.
What am I doing wrong?
I'm trying to display a MongoDB collection of employees into Pug. I know it has something to do with the first 'data' object. I cannot figure out how to render the data within the array.
MongoDB collection:
{
"data":[
{
"active":true,
"userName":"example",
"employeeDetails":{
"personalDetails":{
"firstName":"Dennis",
"lastName":"Glover",
"emailAddress":"example#example.com"
}
}
},
{
"active": false,
"userName": example2,
"employeeDetails": {
"personalDetails": {
"firstName": "Ben",
"lastName": "Dover",
"emailAddress": "example2#example.com"
}
}
},
]
}
Express:
MongoClient.connect(url, function(err, db) {
if (err) {
console.log('Unable to connect to the Server', err);
} else {
console.log('Connection established to', url);
var employeecollection = db.collection('employees');
// Find all employees
employeecollection.find({}).toArray(function(err, employeeResult) {
if (err) {
res.send(err);
} else if (employeeResult.length) {
res.render('employeelist', {
'employeelist': employeeResult,
});
} else {
res.send('No documents found');
}
db.close();
});
};
});
Pug:
table
each employee in employeelist
tr#employee_list_item
td #{employee.userName}
I have fiddled around got it working with Angular2 using ng-repeat, however I cannot seem to get it to render in Pug unless I strip out the 'data' object in the array (which needs to be there).
As much as I can see employeelist[0].data is the array you want to iterate on.
Change employeelist to employeelist[0].data
table
each employee in employeelist[0].data
tr#employee_list_item
td #{employee.userName}
Update. Alternative method:
As Mohit suggested, if you send from the route itself, then your original code will work.
// Find all employees
employeecollection.find({}).toArray(function(err, employeeResult) {
if (err) {
res.send(err);
} else if (employeeResult.length) {
res.render('employeelist', {
'employeelist': employeeResult[0].data,
});
} else {
res.send('No documents found');
}
db.close();
});
Then, in your view:
Pug
table
each employee in employeelist
tr#employee_list_item
td #{employee.userName}
Hope this helps you!
AAAModel.find({'category' : category})
.skip(100)
.sort({date: 'desc'})
.exec(function(err, result) {
if (err) {
next(err);
}
if (result) {
result.remove();
}
});
the above doesn't work.
I would like to remove the 100 Items or more of the search results, what should I do?
You could try one of this approach:
Model.findOne({_id: 'specific_id'}, (err, doc) => {
doc.remove((err) => {
if (err) // handle err
});
// or simply use
doc.remove();
});
or
Model.findOne({_id: 'specific_id'}).remove((err, doc) => {
});
or
Model.remove({_id: 'specific_id'}, (err, doc) => {
});
Use this query
AAAModel.find(
{'category' : category},
{ skip: 100,sort:{date: -1}},
function(err, results) {
if (err) {
next(err);
}
if (result) {
result.remove();
}
});
I've got the same problem.
In fact in latest mongoose query has remove method. So, it theory it could work like this:
AAAModel.find({'category' : category})
.skip(100)
.sort({date: -1})
.remove(function(err, result) {
if (err) {
next(err);
}
if (result) {
console.log("Number of deleted records:" + result);
}
});
But in my case this code removed all records
So, the possible solution is
AAAModel.find({'category' : category})
.select("_id")
.skip(100)
.sort({date: -1})
.exec(function(err, idObjLst) {
if (err) {
return next(err);
}
var ids = idObjLst.map(function(idObj) { return idObj._id; });
if (ids && ids.length > 0) {
AAAModel.remove({_id: {$in: ids}}).exec(function(err, result) {
console.log("Removed " + result + " elements");
});
});
Could be quite expensive though.
As we know, the parameter results is an array of documents in your find callback, not a single document. so you can iterate it and remove item one by one, despite this is not best way to remove documents for performance.
Actually, mongoose Mode.remove can be used to remove documents, but the query condition can not skip and sort until now.