Mongoose create multiple objects in a array with partial success - mongodb

There's a method Model#create in a Mongoose to which I can send an array of objects.
var items = [{id: 1},{id: 2},{id: 3},{id: 4}];
Model.create(array, function (err, succededItems) {
...
});
Everything works fine except behavior when some of the objects which I try to create failed to be saved. In this scenario in err object I get, there're details about first object which has been failed, and in succededItems I get an array of the objects which have been saved.
I doubt is it possible to get in err or other way, an array which objects failed to save and reason, why did this happen?
Thank you in advance.

Unfortunately Mongoose returns only the first error, so your best option would be to use a combination of promises :
Promise
.all( items.map( item => {
return Model.create( item )
.catch( error => ( { error } ) )
}) )
.then( items => {
items.forEach( item => {
if ( item.error ) {
console.log( "Item has failed with error", item.error );
} else {
console.log( "Item created successfully" );
}
} );
} );

Related

how to avoid duplicate entries in akita store?

Before sending API request to insert I want to check the item is already exist in store or not, if not then only send the request to insert.
add(){
const item = {id: 4,name:'test1',description:'description'}
// here i want to check whether test1 already exist or not
this.store.add(item);
this.httpService.insert(item).subscribe(
result => {
this.messageService.handleSuccess('inserted');
this.store.updateId(id, result.id);
},
error => this.messageService.handleError('error on insert:', error)
);
can someone please help?
You can use the query:
add(){
const item = {id: 4,name:'test1',description:'description'}
if(query.hasEntity(4)) {
// exists
}
this.store.add(item);
this.httpService.insert(item).subscribe(
result => {
this.messageService.handleSuccess('inserted');
this.store.updateId(id, result.id);
},
error => this.messageService.handleError('error on insert:', error)
);

Mongodb will not create document within if/else statement

New to MongoDB. I'm trying to add a new document to an existing collection only if a document with the same name does not already exist within that collection. I'm using an if/else statement first to check whether there is a document with the same name as the new entry, and if not, an else statement that will create the new entry. Whatever I try, the new document is not getting added, and instead it returns an empty array. I'd be grateful for any help.
I've tried switching the if/else statements; checking for null and undefined values upon return of if statement
app.post('/cocktails/new', isLoggedIn, (req, res) => {
// add to DB then show item
let name = req.body.name;
let style = req.body.style;
let spirit = req.body.spirit;
let image = req.body.image;
let description = req.body.description;
let newCocktail = {name: name, style: style, base: spirit, image:
image, description: description}
Cocktail.find({name}, function (err, existCocktail) {
if(existCocktail){
console.log(existCocktail)
}
else {Cocktail.create(newCocktail, (err, cocktail) => {
console.log(cocktail)
if (err) {console.log(err)}
else {
res.redirect('/cocktails/' + cocktail._id)}
})
}
})
})
In the event document is not found with the if function, else statements will execute, leading to new document being created with the newCocktail object.
You should use findOne instead of find.
find returns an empty array when no docs found.
The following expression returns true, so your existCocktail condition becomes true, causing your new data not added.
[] ? true : false
Also I refactor your code a bit, you can use destructuring your req.body.
app.post("/cocktails/new", isLoggedIn, (req, res) => {
// add to DB then show item
const { name, style, spirit, image, description } = red.body;
let newCocktail = {
name,
style,
base: spirit,
image,
description
};
Cocktail.findOne({ name }, function(err, existCocktail) {
if (existCocktail) {
console.log(existCocktail);
res.status(400).json({ error: "Name already exists" });
} else {
Cocktail.create(newCocktail, (err, cocktail) => {
console.log(cocktail);
if (err) {
console.log(err);
res.status(500).json({ error: "Something went bad" });
} else {
res.redirect("/cocktails/" + cocktail._id);
}
});
}
});
});

Optional chaining of methods like sort, limit, skip

I'm passing the query params and there could be any combination of sort, limit or skip for the Mongoose Query.
What I've thought of is to create mutliple mongoose queries based on what params have been passed. So if only sort is passed then the resulting query will have Document.find({}).sort({}) and incase only limit is passed then query would be Document.find({}).limit({})
Should I write something like this -
router.get('/', (req, res) => {
if(req.query.sortByPrice) {
Property.find({})
.sort({ price: req.query.sortByPrice === 'asc' ? 1 : -1 })
.populate('user_id', 'name')
.exec((err, properties) => {
if (err)
return res
.status(404)
.json({ error: "Can't get user details!" });
res.status(200).json(properties);
});
}
if(req.query.limit) {
Property.find({})
.limit(req.query.limit)
.populate('user_id', 'name')
.exec((err, properties) => {
if (err)
return res
.status(404)
.json({ error: "Can't get user details!" });
res.status(200).json(properties);
});
}
});
You can create a variable options from your request body and pass it as the third argument to the .find() query, no need to write redundant code with if-else block.
Secnd argument to .find() query is projection, so don't forget to pass an empty object there.
Try this :
let options={};
if(req.query.sortByPrice){
options.sort = {
price: req.query.sortByPrice === 'asc' ? 1 : -1
}
}
if(req.query.limit){
options.limit = req.query.limit
}
Property.find({},{},options)
.populate('user_id', 'name')
.exec((err, properties) => {
if (err)
return res
.status(404)
.json({ error: "Can't get user details!" });
res.status(200).json(properties);
return;
});
Note: Dont forget to return after res.status().json(), otherwise you might get error cant set headers after they are sent . if you try to send response again.

Mongoose remove one object from array of array

I have Mongoose schema like this:
{
......
project: [
{
Name: String,
Criteria:[
{
criteriaName:String,
}
]
}
]
......
}
And I want to remove one of the objects of criteria array which is in the array of project based on the object id
I tried the code following
criteria.findOneAndUpdate({
"_id": uid,
},{ $pull: { "project.Criteria": { _id: cid } } }, (err) => {
......
}
However this cannot work, it said "Cannot use the part (Criteria) of (project.Criteria) to traverse the element"
Do you need to do it in one query to the database? If not, the following solution may work for you:
criteria.findOne({ _id: uid })
.then((obj) => {
// Filter out the criteria you wanted to remove
obj.project.Criteria = obj.project.Criteria.filter(c => c._id !== cid);
// Save the updated object to the database
return obj.save();
})
.then((updatedObj) => {
// This is the updated object
})
.catch((err) => {
// Handle error
});
Sorry if the .then/.catch is confusing. I can rewrite with callbacks if necessary, but I think this looks a lot cleaner. Hope this helps!

Uspert multiple documents with MongoDB/Mongoose

Say I have a list of models:
const documents = [{}, {}, {}];
And I want to insert these into the DB, or update them all, but only if a condition is met:
Model.update({isSubscribed: {$ne: false}}, documents, {upsert:true},(err, result) => {
});
The above signature is surely wrong - what I want to do is insert/update the documents, where the condition is met.
There is this Bulk API:
https://docs.mongodb.com/manual/reference/method/Bulk.find.upsert/
but I can't tell if it will work when inserting multiple documents.
Imagine this scenario: We have a list of employees and a form of some sorts to give them all a penalty, at once, not one by one :)
On the backend side, you would have your eg addBulk function. Something like this:
Penalty controller
module.exports = {
addBulk: (req, res) => {
const body = req.body;
for (const item of body) {
Penalty.create(item).exec((err, response) => {
if (err) {
res.serverError(err);
return;
}
});
res.ok('Penalties added successfully');
}
}
Then you'll probably have an API on your frontend that directs to that route and specific function (endpoint):
penaltyApi
import axios from 'axios';
import {baseApiUrl} from '../config';
const penaltyApi = baseApiUrl + 'penalty'
class PenaltyApi {
static addBulk(penalties) {
return axios({
method: 'post',
url: penaltyApi + '/addBulk',
data: penalties
})
}
}
export default PenaltyApi;
...and now let's make a form and some helper functions. I'll be using React for demonstration, but it's all JS by the end of the day, right :)
// Lets first add penalties to our local state:
addPenalty = (event) => {
event.preventDefault();
let penalty = {
amount: this.state.penaltyForm.amount,
unit: this.state.penaltyForm.unit,
date: new Date(),
description: this.state.penaltyForm.description,
employee: this.state.penaltyForm.employee.value
};
this.setState(prevState => ({
penalties: [...prevState.penalties, penalty]
}));
}
Here we are mapping over our formData and returning the value and passing it to our saveBulkEmployees() function
save = () => {
let penaltiesData = Object.assign([], this.state.penalties);
penaltiesData.map(penal => {
penal.employeeId = penal.employee.id;
delete penal.employee;
return penaltiesData;
});
this.saveBulkEmployees(penaltiesData);
}
...and finally, let's save all of them at once to our database using the Bulk API
saveBulkEmployees = (data) => {
PenaltyApi.addBulk(data).then(response => {
this.success();
console.log(response.config.data)
this.resetFormAndPenaltiesList()
}).catch(error => {
console.log('error while adding multiple penalties', error);
throw(error);
})
}
So, the short answer is YES, you can absolutely do that. The longer answer is above :) I hope this was helpful to you. If any questions, please let me know, I'll try to answer them as soon as I can.