Updating an array using conditionals with mongoose in node.js - javascript

I am trying to update a MongoDB collection that I have created a schema for using Mongoose. My goal is to remove all elements in an array known as 'alarms' that are less than (in the past) of the current time.
Here is my Mongoose Schema:
var mongoose = require('mongoose');
var ReminderSchema = new mongoose.Schema({
firstName: String,
lastName: String,
phone: Number,
email: String,
medication: String,
alarms: [Date]
});
module.exports = mongoose.model('Reminder', ReminderSchema);
Here is the update. I am trying to remove all alarms that are less than the current date.
var date = new Date();
var Reminder = require('./models/Reminders.js');
//remove ALL alarms that are are before current time
Reminder.update({alarms: {$lte: date}}, {$pullAll: {alarms: {$lte: date}}}, function(err, updated){
if(err){
console.log(err);
}
else {
console.log(updated);
}
});
I am currently getting this error:
{ [MongoError: $pullAll requires an array argument but was given a Object]
name: 'MongoError',
message: '$pullAll requires an array argument but was given a Object',
driver: true,
index: 0,
code: 2,
errmsg: '$pullAll requires an array argument but was given a Object' }
Any thoughts?

The $pullAll receives an array not an object:
{ $pullAll: { <field1>: [ <value1>, <value2> ... ], ... } }
The $pullAll operator removes all instances of the specified values
from an existing array. Unlike the $pull operator that removes
elements by specifying a query, $pullAll removes elements that match
the listed values.
the solution you should use $pull :
Reminder.update({alarms: {$lte: date}},{$pull:{alarms: { $lte : new Date()}}},
function(err, updated){
if(err){
console.log(err);
}
else {
console.log(updated);
}
});
https://docs.mongodb.org/manual/reference/operator/update/pullAll/

Related

Mongo - Update nested array of objects by _id

How can I access an array of object with their own _id and update it with Mongo/Mongoose?
Take a look to my update query and check if there's something wrong, because this code doesn't return any error, but it doesn't really update the field
modelUser.findOneAndUpdate(
{ userName: body.author, "portfolio._id": body.id },
{ new: true },
{
$set: { //I thing the problem it's over here
"portfolio.$.profitLoss": profitLoss,
"portfolio.$.percentage": percentage
}
},
(err, user) => {
if (err) {
console.log(err);
}
console.log(`Done`);
}
);
This is my User Schema:
const userSchema = new Schema({
...stuff,
portfolio: [
{
coin: String,
amount: String,
price: String,
bought: Date,
profitLoss: String,
percentage: String
}
],
});
Basically i think mongo just don't know which of these sub documents should update, I don't know if there's something like another findOneAndUpdate for sub object/document by id.
Just changed findOneAndUpdate to updateOne and everything works.

Mongoose: updating array in document not working

I'm trying to update an array in document by adding object if it doesn't exist, and replacing the object in array otherwise. But nothing ($push, $addToSet) except the $set parameter does anything, and $set works as expected - overwrites the whole array.
My mongoose schema:
var cartSchema = mongoose.Schema({
mail: String,
items: Array
});
The post request handler:
app.post('/addToCart', function(req, res) {
var request = req.body;
Cart.findOneAndUpdate({
"mail": request.mail
}, {
$addToSet: {
"items": request.item
}
}, {
upsert: true
},
function(err, result) {
console.log(result);
}
);
res.send(true);
});
The data that I'm sending from the client:
{
"mail":"test#gmail.com",
"item":{
"_id":"59da78db7e9e0433280578ec",
"manufacturer":"Schecter",
"referenceNo":"Daemon-412",
"type":"Gitare",
"image":"images/ba9727909d6c3c26412341907e7e12041507489988265.jpeg",
"__v":0,
"subcategories":[
"Elektricne"
]
}
}
EDIT:
I also get this log when I trigger 'addToCart' request:
{ MongoError: The field 'items' must be an array but is of type object in
document {_id: ObjectId('5a19ae2884d236048c8c91e2')}
The comparison in $addToSet would succeeded only if the existing document has the exact same fields and values, and the fields are in the same order. Otherwise the operator will fail.
So in your case, request.item always need to be exactly the same.
I would recommend creating a model of "item". Then, your cart schema would be like:
var cartSchema = mongoose.Schema({
mail: String,
items: [{
type: ObjectId,
ref: 'item',
}],
});
And let MongoDB determine if the item exist.
this should work you just need to implement objectExits function that test if the item is that one you're looking for :
Cart.findOne({ "mail": request.mail })
.exec()
.then(cart => {
var replaced = cart.items.some((item, i) => {
if (item._id == request.item._id)) {
cart.items[i] = request.item;
return true;
}
})
if (!replaced) {
cart.items.push(request.item);
}
cart.save();
return cart;
})
.catch(err => {
console.log(err)
});

How to add object to collection inside another collection in MongoDB using Node.js

I know how to add object to collection in MongoDB using Node.js, for example:
router.post('/addProduct', function (req, res) {
Partner.findByIdAndUpdate({ _id: req.body.partnerId }, { $push: { "products": { name: req.body.dataProduct.name } } }, { safe: true }, function (err, response) {
if (err) throw err;
res.json(response);
});
});
but what if in product will be another table? How can I simply add object there?
Let's say this is my schema:
var partnerSchema = new mongoose.Schema({
name: String,
products: [
{
name: String,
campaignList: [
{
name: String,
type: String,
startDate: Date,
endDate: Date,
paymentMethod: String,
partnerPayout: Number,
ourPayout: Number
}
]
}]
});
ID in each partner and product are default ._id eg. partner._id and product._id. That's why aren't in schema above. However I sending them from FrontEnd to BackEnd as a req.parameter - normally thing but i wanted to say it for sure :)
Your best bet would bet to define the Schema & Model for the campaign on its own, and add it to the Partner by reference using the _id
var partnerSchema = new mongoose.Schema({
name: String,
products: [
{
name: String,
campaignList: [
{ type : mongoose.Schema.Types.ObjectId, ref : 'campaignModel' }
]
}]
});
var campaignSchema = new mongoose.Schema({
name: String,
type: String,
startDate: Date,
endDate: Date,
paymentMethod: String,
partnerPayout: Number,
ourPayout: Number
});
var campaignModel = mongoose.model('campaignModel', campaignSchema);
var partnerModel = mongoose.model('partnerSchema', partnerSchema);
A good practice is to look for times where you're trying nest semi-complex data, or objects with more than two or three keys, and extract them into their own collection. Not only does it make it easier to search for those documents, it makes it easier to use them in conjunction with other objects.
Be sure to call .populate() during your query so that MongoDB knows to nest the documents from the other collections, otherwise, you'll just have an array of ObjectId.
First match the required products array position. You can confirm this by testing a simple find like:
Partner.find({_id: req.body.partnerId), 'products.name': req.body.dataProduct.name }, { 'products.$': 1})
Use the positional $ operator to push the new object into the array in the matched product element:
Partner.update({_id: req.body.partnerId), 'products.name': req.body.dataProduct.name }, { $push: { 'products.$.campaignList': { name: 'new campaign' }}})
Reference https://docs.mongodb.com/manual/reference/operator/update/positional/
try this:
router.post('/addProduct', function (req, res) {
Partner.findOneAndUpdate({ _id: req.body.partnerId }, { $push: { "products": { name: req.body.dataProduct.name, $push: {"campaignList": {name: req.body.name}} } } }, { safe: true }, function (err, response) {
if (err) throw err;
res.json(response);
});
});
i hope it helps you

Mongoose - delete subdocument array item

I have this little schema for users:
{
username: String,
contacts: Array
}
So for example some user's contacts will look like this:
{
username: "user",
contacts: [{'id': ObjectId('525.....etc'), 'approved': false}, {'id':ObjectId('534.....etc'), 'approved': true}]
}
Now I need to delete an item from contacts so I do:
model.findByIdAndUpdate(23, {'$pull': {
'contacts':{'id':'525.....etc'}
}});
but seems not working, no errors but it doesn't gets deleted, I just would like to return this document for the user:
{
username: "user",
contacts: [{'id':ObjectId('534.....etc'), 'approved': false}]
}
how to achieve this?
The $pull operator actually just performs the conditions on the array element on which it is operating. It seems that your question might not actually show that you are probably working with an ObjectId value that mongoose creates by default for all array fields.
So you could to your query like this, after importing the ObjectId creation method:
model.findByIdAndUpdate(23, {
'$pull': {
'contacts':{ '_id': new ObjectId(someStringValue) }
}
});
Or in fact you can actually define your "schema" a little better, and mongoose will actually "autocast" the ObjectId for you based on the "type" defined in the schema:
var contactSchema = new Schema({
approved: Boolean
});
var userSchema = new Schema({
username: String,
contacts: [contactSchema]
});
This allows mongoose to "follow the rules" for strictly typed field definitions. So now it knows that you actually have an _id field for each element of the contacts array, and the "type" of that field is actually an ObjectId so it will automatically re-cast "String" values supplied as a true ObjectId.
finaly!
MongoDB:
"imgs" : {"other" : [ {
"crop" : "../uploads/584251f58148e3150fa5c1a7/photo_2016-11-09_21-38-55.jpg",
"origin" : "../uploads/584251f58148e3150fa5c1a7/o-photo_2016-11-09_21-38-55.jpg",
"_id" : ObjectId("58433bdcf75adf27cb1e8608")
}
]
},
router.get('/obj/:id', function(req, res) {
var id = req.params.id;
Model.findOne({'imgs.other._id': id}, function (err, result) {
result.imgs.other.id(id).remove();
result.save();
});

MongoDB and nodejs, find throught list of ids

I have two collections:
users:
{
_id: ObjectId('123...'),
docs: [
ObjectId('512d5793abb900bf3e000002'),
ObjectId('512d5793abb900bf3e000001')
]
}
docs:
{
_id: ObjectId('512d5793abb900bf3e000002'),
name: 'qwe',
...
}
{
_id: ObjectId('512d5793abb900bf3e000001'),
name: 'qwe2',
...
}
I want to get docs from ids. I try this solution, but I get this message:
{ db: { domain: null,
_events: {},
_maxListeners: 10,
databaseName: 'test', ...
Your message looks like a mongodb cursor returned from find by native mongodb driver.
To get actual data you should use toArray function of the cursor:
var ObjectID = require('mongodb').ObjectID;
// you shall wrap each id in ObjectID
var idsProjects = [
ObjectID('512d5793abb900bf3e000002'),
ObjectID('512d5793abb900bf3e000001')
];
collectionProjects.find({
_id: { $in: idsProjects }
},{
_id: -1, // use -1 to skip a field
name: 1
}).toArray(function (err, docs) {
// docs array here contains all queried docs
if (err) throw err;
console.log(docs);
});
But I recommend you to switch from native mongodb driver to some wrapper around it like monk.
If you care about the order of the list, the answer of Mr.Leonid may not work as expected to do.
That's because find gets the docs that have _id equals to any _ids $in the list so the output docs will be ordered by the main order of the collection itself not the order of the input list.
To solve that you can just use the normal findOne with a for loop to the list.
The code will look like:
var ObjectID = require('mongodb').ObjectID;
var idsProjects = [
'512d5793abb900bf3e000002',
'512d5793abb900bf3e000001'
];
let usersList = new Array();
for (let index = 0; index < idsProjects.length; index++) {
const myID = idsProjects[index];
const query = { _id: ObjectID(myID) };
const options = {
projection: {name: 1 };
var user= await collectionProjects.findOne(query,options);
usersList.push(user);
}
// that's it,
// here we have a list of users 'usersList'
//with same order of the input ids' list.
console.log(usersList);

Categories