return all data in subarray inside subarray - node.js

this is my mongoose schema:
var book_listSchema = new mongoose.Schema({
userId: {
type: String,
required: true
},
first_name: {
type: String,
required: true
},
last_name: {
type: String,
required: true
},
newList: [{
list_name: String,
books: [{
list_name: String,
book_name: {
type: String,
required: true
},
book_author: {
type: String,
required: true
},
date_added: {
type: Date,
default: Date.now
},
date_finished: {
type: Date,
default: Date.now
},
book_image: {
type: String,
required: true
}
}]
}],
});
var book_list = mongoose.model('book_list', book_listSchema);
module.exports = book_list;
This is my query:
book_list.findOne({
"userId": req.userContext.userinfo.sub
}, {
newList: {
$elemMatch: {
"list_name": listName
}
}
})
.skip((perPage * page) - perPage)
.limit(perPage)
.exec(function(err, doc) {
console.log(doc);
response.render('display_list', {
userContext,
list_books: doc.newList[0].books,
listName: listName,
pages: Math.ceil(doc.newList[0].books.length / perPage),
current: page
})
})
});
This is the doc/file that is returned:
{
_id: new ObjectId("63b60f5844ffdd86146d6e5b"),
newList: [{
list_name: 'read',
_id: new ObjectId("63b60f5844ffdd86146d6e5c"),
books: [Array]
}]
}
What I need returned is just the books:[Array]. I am trying to render the contents of the books:[Array] to a page with pagination.

Try this:
book_list.findOne({
"userId": req.userContext.userinfo.sub,
"newList.list_name": listName
}, {
"newList.$.books": 1
})
.skip((perPage * page) - perPage)
.limit(perPage)
.exec(function(err, doc) {
console.log(doc);
response.render('display_list', {
userContext,
list_books: doc.newList[0].books,
listName: listName,
pages: Math.ceil(doc.newList[0].books.length / perPage),
current: page
})
})
});
It'll only return the array of books and if you use doc.newList[0].books, it will give you the array you want.
Make sure, $ is a positional operator only project the 1st element that match the query, so if you have multiple lists with the same name, it'll just return the first one.
If you face any query. Please let me know

Related

How do I remove an array of referenced Objects when deleting the main document?

This is my MongoDB schema:
const MenuSchema = new Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
image: {
type: String,
},
caterer: {
type: Schema.Types.ObjectId,
ref: 'User'
},
products: [{
type: Schema.Types.ObjectId,
ref: 'Product'
}]
}, { timestamps: true })
const ProductSchema = new Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
image: {
type: String,
},
price: {
type: String
}
}, { timestamps: true })
What I'm wondering - is how I can delete the array of products, at the same time as I delete the main "Menu" document? When I remove the Menu, I can also assume that the products belonging to the menu should be removed.
At the moment this is how I remove the menu (and tried to remove its products):
await Menu.findOneAndDelete({ _id: req.params.menu_id }, (err, response) => {
if (err) {
console.error(err);
}
Product.remove({ _id: { $in: req.body.products }}, (err, res) => {
if (err) {
console.error(err);
}
console.log('Deleted products');
});
});
However, the products do not get removed. Any suggestions?
Mongoose provides a pre and post middleware on your schema. Which means you can delete all the referenced documents before or after you do an operation on the current schema.
Read more here.
Here's an example, inside your schema add this:
const MenuSchema = new Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
image: {
type: String,
},
caterer: {
type: Schema.Types.ObjectId,
ref: 'User'
},
products: [{
type: Schema.Types.ObjectId,
ref: 'Product'
}]
}, { timestamps: true })
const ProductSchema = new Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
image: {
type: String,
},
price: {
type: String
}
}, { timestamps: true })
MenuSchema.post('remove', removeProducts);
function removeProducts(doc) {
Products.remove({_id: { $in: doc.products}})
}
Assuming Products is the name of your model.
Try This It works for Me.
await Menu.findOneAndDelete({ _id: req.params.menu_id }, (err, response) => {
if (err) {
console.error(err);
}
Product.remove({ _id: { $in: response.products }}, (err, res) => {
if (err) {
console.error(err);
}
console.log('Deleted products');
});
});
You can use post schema hooks of mongoose as below
schema.post('remove', function(doc) {
console.log('%s has been removed', doc._id);
});
Mongoose Post Hook
But the best approach is to use transactions to execute multiple operations on the database as below.
let session = null;
db.startSession()
.then((_session) =>{
session = _session;
session.startTransaction();
return Menu.deleteOne({ _id: req.params.menu_id });
})
.then(()=> Product.deleteMany({ _id: { $in: req.body.products }}))
.then(()=>{
session.commitTransaction();
})
.catch((err)=>{
session.abortTransaction()
return handleError(err);
})
Mongoose Transactions

Mongoose : Can't populate after find to make $near query

I am trying to populate user informations to get his location but i am getting undefined.
Here are my schemas:
AnnounceSchema:
const AnnounceSchema = new mongoose.Schema({
titre: String,
contenu: String,
image: String,
tag: String,
media: String,
date: {
type: Date,
default: Date.now
},
commentaires: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }],
authorId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
})
UserSchema:
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: { type: String, required: true, unique: true },
loc: { type: { type: String, default: 'Point' }, coordinates: { type: [Number] } }
});
UserSchema.index({ loc: '2dsphere' });
And here is my query :
Announce.find({})
.populate('authorId', null,
{
'authorId.loc': {
$near: {
$geometry: {
type: 'Point',
coordinates: [req.user.loc.coordinates[0], req.user.loc.coordinates[1]]
},
$maxDistance: 10000
}
}
})
.exec((err, announces) => {
if (err) {
res.send(err)
} else {
res.render('announce/search', { announces: announces })
}
})
The error I'm getting is "unable to find index for $geoNear query". I've added an index to the AnnonceSchema but no change:
AnnonceSchema.index({ "authorId.loc" : "2dsphere"})
This should work:
Announce.find({})
.populate('author',
{
path : 'authorId',
match: {
'loc': {
$near: {
$geometry: {
type: 'Point',
coordinates: [req.user.loc.coordinates[0], req.user.loc.coordinates[1]]
},
$maxDistance: 10000
}
}
},
model: 'User',
})
.exec((err, announces) => {
if (err) {
res.send(err)
} else {
res.render('announce/search', { announces: announces })
}
})
populate works in two ways
it can populate by reference Announce.find({}).populate('authorId')
or by a custom query (which is what you need)

Node.js with mongoose delete one array element

I am trying to delete one array element when I click delete button on jade view page.
When clicked, it's going to send selected instructor objected as req.body.
At sever side, it will find courses that contain the instructor objectId.
Any idea for me?
Thank you for reading it.
here is my code:
var id = req.body._id;
clist.find({ instructors: { $in: [id] } }).exec(function (err, result) {
result.forEach(function (obj) {
clist.update(
{ _id: new mongoose.Types.ObjectId(obj._id)},
{ $pull: { instructors : [new mongoose.Types.ObjectId(id)] } }
);
console.log(new mongoose.Types.ObjectId(obj._id) + ' was deleted');
});
});
Schema Clist and ilist:
var instructorlist = mongoose.Schema({
name: { type: String, required: true },
age: { type: Number, required: true },
gender: { type: String, required: true },
DOB: { type: Date, required: true, default: Date.now },
email: { type: String, required: true },
phone: { type: Number, required: true },
address: { type: String, required: true },
dateofstart: { type: Date, required: true},
courses: [{
type: mongoose.Schema.Types.ObjectId,
ref: "clist"
}]
});
var courselist = mongoose.Schema({
coursename: { type: String, required: true },
coursenumber: { type: String, required: true },
coursecredit: { type: Number, required: true },
courseroom: { type: String, required: false },
courseregisteddate: {type: Date, default: Date.now},
students: [{
type: mongoose.Schema.Types.ObjectId,
ref: "slist"
}],
instructors: [{
type: mongoose.Schema.Types.ObjectId,
ref: "ilist"
}]
});
one example for mongodb :
{
"_id": {
"$oid": "591a7a3b391a1842e8a69e23"
},
"coursename": "JDKD",
"coursenumber": "COMP4483",
"coursecredit": 4,
"courseroom": "sdaf",
"instructors": [
{
"$oid": "591a374422a3a13d38c0bbe5"
}
],
"students": [],
"courseregisteddate": {
"$date": "2017-05-16T04:04:11.848Z"
},
"__v": 0
}
When I add instructor objectID in Course.
var newcourse = new clist({
'coursename': req.body.coursename, 'coursenumber': req.body.coursenumber, 'coursecredit': req.body.coursecredit
, 'courseroom': req.body.room, 'instructors': instructors._id
});
Use same operation to find and update multiple
clist.update(
{ instructors: { $in: [id] }},
{ $pull: { instructors : { _id : new mongoose.Types.ObjectId(id) } } }, //or{ $pull: { instructors: mongoose.Types.ObjectId(id) } }
{
multi:true
},
function(error, success){
if(error){
console.log("error",error)
}
console.log("success",success)
});

Mongoose: How to findById when the query is not complete

I am building a search query where it will find a database object by its ID even while the user is typing.
ordersRouter.route('/searchorder/:term')
.get(function(req, res){
term = req.params.term;
console.log(term);
Orders.findById(term)
.populate({ path: 'userPurchased products.product', select: '-username -password' })
.exec(function(err, orders){
if (err) throw err;
res.json([orders]);
});
});
The problem here is that when the term does not exactly the same as the ID, it will return nothing. How can I return IDs with partial term?
EDIT: My order model schema
var orderSchema = new Schema({
orderId: { type: String },
userPurchased: { type: Schema.Types.ObjectId, ref: 'users' },
products: [
{
product: { type: Schema.Types.ObjectId, ref: 'products' },
size: { type: String, required: true },
quantity: { type: Number, required: true },
subTotal: { type: Number, required: true }
}
],
totalQuantity: { type: Number },
totalPrice: { type: Number },
modeOfPayment: { type: String },
shippingAd: { type: String },
refNumber: { type: String },
isDone: { type: Boolean, default: false },
orderStatus: { type: String, default: 'Pending' },
dateOrdered: { type: Date, default: Date.now() },
fromNow: { type: String }
});
You can run your query using regex i.e. create a regular expression object from the string using the RegExp constructor then run the query as:
ordersRouter.route('/searchorder/:term')
.get(function(req, res){
term = req.params.term;
console.log(term);
Orders.find({'orderId': new RegExp(term)})
.populate({
path: 'userPurchased products.product',
select: '-username -password'
})
.exec(function(err, orders){
if (err) throw err;
res.json(orders);
});
});

Mongoose Schema.update doesn't update boolean

I have tried updating other fields and it works just fine.
The command I am using in my API:
User.update({ email: targetUser.email }, { $set: { isAdmin: true }, $push: { 'log.updated': new Date() } }, function (err, user) {
if (err) {
responseObject.err = err;
responseObject.data = null;
responseObject.code = 422;
return res.json(responseObject);
}
return res.json(responseObject);
});
To clarify, when I try to run this, the API returns a code 200, meaning everything worked fine, but when I check the database the isAdmin value wasn't changed.
Any suggestions would be helpful, running out of ideas here!
User Schema as requested:
var UserSchema = new Schema({
name: { type: String, default: "", index: 'text' },
email: { type: String, lowercase: true },
role: { type: String, default: "" },
meta: {
skills: { type: Array, default: [], index: 'text' },
about: { type: String, default: "", index: 'text' },
education: { type: Array, default: [], index: 'text' },
location: {
address: {
a: { type: String, default: "" },
p: { type: String, default: "" },
c: { type: String, default: "" }
},
geo: {
lat: { type: Number, default: 0 },
lng: { type: Number, default: 0 }
}
}
},
compMeta:
{
departments: { type: Array, default: [], index: 'text' },
employees:
[
{
emId: Number,
empName: String,
empDep: String // Dunno if i should use Dep name or Dep ID gonna look in to that later
}
],
}
,
settings: {
search: {
distance: {
n: { type: Number, default: 100 },
t: { type: String, default: "km" }
}
}
},
created: {
type: Date,
default: Date.now
},
//Rating is an array of objects that consist of rateing 0-100 , job database id , comments from the Company
rating:
[
{
rate: Number,
jobId: Number,
jobComments: String
}
],
/*rating:
{
userTotalRating: {type: Number, default: 0},
ratingCounter : {type: Number, default: 0}
}*/
sensitive: {
cpr_cvr: String,
},
stripe: { type: String },
facebook: {},
linkedin: {},
log: {
updated: { type: Array, default: [] }
},
hashedPassword: String,
provider: { type: String, default: 'local' },
salt: String
});
UPDATE:
Mongodb version: 3.0.7
Turns out I just forgot to add the isAdmin field to my User Schema! Also, my call to the update was wrong, I changed it to this:
User.update({ email: targetUser.email }, { $set: { isAdmin: true }}, { $push: { 'log.updated': new Date() } }, function (err, user) {
if (err) {
responseObject.err = err;
responseObject.data = null;
responseObject.code = 422;
return res.json(responseObject);
}
return res.json(responseObject);
});
Thanks to everyone that put an effort to help me! :)
I encountered a similar problem. The solution was to add the callback.
This doesn't work:
Ride.updateOne({driver:req.body.id},{$set:{isBusy:true}});
This works:
Ride.updateOne({driver:req.body.id},{$set:{isBusy:true}},(e,s)=>{});
Try updating two fields with $set
User.update({ email: targetUser.email }, { $set: { isAdmin: true, 'log.updated': new Date() } }, function (err, user) {
if (err) {
responseObject.err = err;
responseObject.data = null;
responseObject.code = 422;
return res.json(responseObject);
}
return res.json(responseObject);
});
Hope it's works.
There is easier way to handle the issue. As per the documentation, the second parameter is the object where you can update the statement.
A.findByIdAndUpdate(id, update, options, callback)
So you just need to take everything inside the update object.
User.update({ email: targetUser.email, $set: {isAdmin: true}} // ... etc

Resources