Mongoose: How to findById when the query is not complete - node.js

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);
});
});

Related

return all data in subarray inside subarray

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

Mongoose How to sort .populate field

I'm work with an user/articles profile system. I have been using the .populate() to render the posts but I cannot get the articles sorted by the date they were created.
I am using the createdAt variable as the main way of ordering the posts displayed.
For reference:
router.get('/:id', async (req, res) => {
const user = await User.findById(req.params.id, function(error) {
if(error) {
req.flash("error", "something went wrong")
res.redirect("/");
}
}).populate('articles')
res.render('users/show',{
user: user
});
and the article.js:
const ArticleSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
author: {
type: String
},
markdown: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
},
slug: {
type: String,
required: true,
unique: true
},
sanitizedHtml: {
type: String,
required: true
},
img: {
type: String
},
type:{
type: String
},
user : { type: Schema.Types.ObjectId, ref: 'User' },
}, {timestamps: true});
In advance thank you all for the help.
There is a property called options in populate,
.populate({
path: 'articles',
options: { sort: { createdAt: -1 } }
})

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)

How to push objectId to mongodb schema field array of object using findByIdAndUpdate

Everytime I hit api I am getting same error-
I have tried sending value as parameters also but failed. Any help would be appreciated. When i use $set it updates same value everytime the web service is called but it does work but not with $push.
MongoError: The field 'songId' must be an array but is of type objectId in document
{_id: ObjectId('59709590380026118c22dd61')}
My Playlist schema code:-
var PlaylistSchema = new mongoose.Schema({
name: String,
coverphoto: { type: String, default: '' },
userId: {
type: ObjectId,
ref: 'User'
},
songId: [{ type: ObjectId, ref: 'Song' }],
updated_at: { type: Date, default: Date.now },
});
my Song Schema code
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var SongSchema = new mongoose.Schema({
audioName:{type:String,default:''},
title: String,
// coverphoto: { type: String, default: '' },
singer: { type: String, default: '' },
movie: { type: String, default: '' },
album: { type: String, default: '' },
lyricist: { type: String, default: '' },
actors: { type: String, default: '' },
lyrics: { type: String, default: '' },
genre: { type: String, default: 'Random' },
duration: { type: String, default: '' },
size: { type: String, default: '' },
userId: {
type: ObjectId,
ref: 'User'
},
categoryId: {
type: ObjectId,
ref: 'Category'
},
updated_at: { type: Date, default: Date.now },
});
module.exports = mongoose.model('Song', SongSchema);
My Api code
/* post api to add song in playlist */
router.post('/addSongToPlaylist', function (req, res, next) {
Playlist.findOne({ '_id': req.body.playlistId }, function (err, playlist) {
if (err) return next(err);
console.log(playlist)
console.log("req.body.songId", req.body.songId);
if (playlist) {
Playlist.findByIdAndUpdate(
req.body.playlistId,
{ $push: { "songId": req.body.songId } },
{ new: true },
function (err, playlistData) {
if (err) return next(err);
res.json({ message: 'New Song added successfully', playlist: playlistData, success: true });
});
} else if (!Song) {
res.json({ message: 'Failed to add song', success: false });
}
});
});

Resources