Mongoose doesn't update my fields - node.js

I'm trying to update an entries, if they are found in my Mongo DB:
exports.insert = function(project, data) {
data.forEach(function(d){
d.project = project;
var asset = new Asset(d);
Asset.findOne({
project: asset.project,
ip: asset.ip
}, function(err, match) {
if (err) { return next(err); }
if (match) {
console.log('asset found, updating');
match.mac = 'blablah';
match.save();
}
});
});
};
I also tried to update like this:
asset.mac = 'blalah';
match.update(asset);
In both cases my fields don't update in the DB. I see no change.
There may be smarter ways to do this but I need to be able to use save or update to do it for future.
NB: Although findOneAndUpdate may be the preferred way of doing this, I'm really curious to know why save() or update() do not work in my case. I would like to know how to use those methods to update my document.

If you're trying to find one entry and update it, you should use findOneAndUpdate:
Asset.findOneAndUpdate({
project: asset.project,
ip: asset.ip
}, {
mac: 'blablah'
}, function (err, docThatWasUpdated) {
....
})

Related

Optional parameters on sequelize query

Good morning.
I'm quite new to NodeJS / sequelize world and I'm currently facing a problem while trying to display a dashboard on screen.
This dashboard has three filters: two dates (period), client name, and employee name. The user can select none, one, two, or all the filters and my database needs to work accordingly.
That being said, my problem is with Sequelize because I don't know how to treat this problem of parameters not being "always" there.
I've seen this question:
Sequelize optional where clause parameters?
but this answer doesn't work anymore. I also tried another way of building the where clause, but I failed on it as well (mainly due to sequelize operators).
The last thing I tried was to make a single query with all parameters included but try to find some value (or flag) that would make sequelize ignore the parameter, for the case when the parameter was no there*, but it looks like Sequelize doesn't have anything like that.
* I've read a question here that has an answer saying that {} would do the trick but I tried that as well but didn't work.
In summary: I need to make a query that can "change" over time, for example:
Foo.findAll({
where: {
id : 1,
}
});
Foo.findAll({
where: {
id {
[Op.in] : [1,2,3,4,5]
},
name: "palmeiira",
}
});
Do you know a way of doing it without the need of using a lot if / switch statements?
I'm currently using Sequelize v. 5.5.1.
Update
I tried doing as suggested by #Anatoly and created a function to build the parameters. It was something like that. (I tried a "smaller" version just to test)
async function test() {
const where = {};
where[Op.and] = [];
where[Op.eq].push({
id: {
[Op.in]: [1,2,3]
}
});
return where;
}
I setted the return value to a const:
const query = await test()
And tried console.log(query)
The result was: { [Symbol(and)]: [ { id: [Object] } ] }, which made me believe that the problem was parsing the Op part so i tried using 'Op.and' and 'Op.in' to avoid that and it solved this problem, but led to another on sequelize that said Invalid value
Do you have any idea where is my error ?
P.S.: #Anatoly very nice idea you gave me on original answer. Thank you very much.
If these three conditions should work together then you can use Op.and with an array of conditions:
const where = {}
if (datesFilter || clientNameFilter || employeenameFilter) {
where[Op.and] = []
if (datesFilter) {
where[Op.and].push({
dateField: {
[Op.between]: [datesFilter.start, datesFilter.finish]
}
})
}
if (clientNameFilter) {
where[Op.and].push({
name: {
[Op.iLike]: `%${clientNameFilter.value}%`
}
})
}
if (employeenameFilter) {
where[Op.and].push({
employeeName: {
[Op.iLike]: `%${employeenameFilter.value}%`
}
})
}
}
const dashboardItems = await DashboardItem.findAll({ where }, {
// some options here
})
If the conditions should work as alternatives then just replace Op.and with Op.or

findOneAndUpdate with Upsert always inserting a new user

I want to do is update a record and insert it if it doesn't exist with mongoose. Here is my code:
module.exports.upsertUser = function(user) {
var options = {userName : 'Ricardo'}
Users.findOneAndUpdate({email: user.email}, options, {upsert:true}).exec();
}
And:
var promise = Users.upsertUser(user);
promise
.then(function(results){
...
}
.catch(function(err){
...
}
When I execute the promise, each time a new user is created with the same email.
I'm not sure if I'm performing the update incorrectly. I've tried it in the same way but with update and it does not work either.
Can you help me? Thanks!
You need to put the return without the exec:
module.exports.upsertUser = function(user) {
var options = {userName : 'Ricardo'}
return Users.findOneAndUpdate({email: user.email}, options, {upsert:true, new: true});
}
var promise = Users.upsertUser(user);
promise.then(function(results){
if(results) {
console.log(results);
} else {
console.log('!results');
}
}.catch(function(err){
...
}
FYI:
The default is to return the unaltered document. If you want the new, updated document to be returned you have to pass an additional argument: {new:true} with the upsert option.
according to your coding what findOneAndUpdate() does is that it finds this document/user's document that you are looking for to edit which in this case what you are using to search for this user's document that you want to edit it's document is with his email. Now your second argument modifies that part of the document that you wanted to modify, but this option has to be attached with some mongodb operators (pay a visit to this link for a list of them: https://docs.mongodb.com/manual/reference/operator/update/) but in your case what you need is the $set operator.so i would modify your code like this:
module.exports.upsertUser = function(user) {
var options =
Users.findOneAndUpdate({email: user.email}, {$set:{
userName : 'Ricardo'
}}, {
returnOriginal: true}).exec();
};
so try the above modification let's see how it works

Loopback Find then update attribute or delete by id

Been trying to find samples usage for some of the static methods for a persistedModel in Loopback.
https://apidocs.strongloop.com/loopback/#persistedmodel-prototype-updateattribute
it just says:
persistedModel.updateAttributes(data, callback)
But how you I choose the which record I want to update? this is not working for me.
var order = Order.setId('whateverrecordId');
order.updateAttributes({name:'new name'},callback)
Loving loopback.. but their doc, sucks.. :(
You can use those on event listener like AfterSave
example:
Model.observe('after save', function(ctx, next) {
ctx.instance.updateAttribute(fieldname:'new value');
next();
});
1- What you did was right but i do not advise this method it's used for instance methods and generally to update fields like date for all the collection that you have so you don't need an id for it.
But you can try to make an array containing data to update containing also the ids and then make a comparison to fill in data for the ids that you have. (in #dosomething)
order.find().then(function(orders) {
orders.forEach(function(element) {
order.setId(element.id);
#DoSomething
order.updateAttribute({new: data}, function(err, instance) {
console.log(instance);
})
});
})
2- You can use updateAll to update one or many attribute.
PersistedModel.updateAll([where], data, callback)
var Updates = [{id : 1, name: name1}, ...]
Updates.forEach(function(element) {
order.updateAll({id : element.id}, {name :element.name}, function(err, count) {
if (err) {
console.error(err);
}
console.log(count); // number of data updated
})
})

Overriding mongoose query for a specific model

I want to automatically add a query option for all queries related for a specific mongoose model without affecting other models
I saw this answer where Mongoose.Query is patched and that will affect all mongoose models.
I was able to do this for my soft deleted items. Haven't tested it extensively yet though.
function findNotDeletedMiddleware(next) {
this.where('deleted').equals(false);
next();
}
MySchema.pre('find', findNotDeletedMiddleware);
MySchema.pre('findOne', findNotDeletedMiddleware);
MySchema.pre('findOneAndUpdate', findNotDeletedMiddleware);
MySchema.pre('count', findNotDeletedMiddleware);
I see two possible easy ways to do this:
Alternative #1
Add a static dict with the options you want to be applied to your specific Mongoose schema:
FooSchema.statics.options = {
...
};
Now, when you query you need to do:
Foo.find({}, null, Foo.options, function(err, foos) {
...
});
Alternative #2
Implement a wrapper to the find method that always uses your specific options:
FooSchema.statics.findWithOptions = function(query, next) {
var options = { ... };
this.find(query, null, options, next);
};
And use this method like so:
Foo.findWithOptions({}, function(err, foos) {
...
})
Reusability
To make these wrapper methods more reusable, you can make a dict with all your wrappers:
var withOptionsWrappers = {
findWithOptions: function(query, next) {
this.find(query, null, this.options, next);
},
findByIdWithOptions: ...
findOneWithOptions: ...
...
};
Since we're referring to this there will be no problem reusing this. And now have this be applied to all your schemas along with your schema specific options:
FooSchema.statics = withOptionsWrappers;
FooSchema.statics.options = {
...
};
BarSchema.statics = withOptionsWrappers;
BarSchema.statics.options = {
...
};

MongoDB Upsert add to array

I'm trying to insert/update an array of strings in a mongodb document using some typescript code running in NodeJS.
The following code is typescript but I guess JS developers will get it w/o any problems:
export function addEvents(entityId: string,
events: string[] ,
callback: () => void) {
db.collection('events', function(error, eventCollection) {
if(error) {
console.error(error); return;
}
eventCollection.update({ _id: entityId }, { "$pushAll ":
{ events: events }},
function(error, result) {
if(error) {
console.error(error); return;
}
callback();
});
});
}
the document have the following structure:
{
_id : string
events : ["array","of","strings"]
}
I simply want to append an array strings at the end of the existing array for a specific _id.
I don't quite get if I should use update,save, $push ,$pushall etc.
Can someone explain?
If I understood correctly the problem is that pushAll does nothing or update returns error? Maybe copy-paste mistake in your example but I think you have typo here.
{ "$pushAll ": { events: events }}
It should be
{ $pushAll: { events: events }}
Your combination of update and $pushAll looks like the best choice for what you're doing here -- it's for appending an array to an existing array. $push is for adding an element to an array. save would involve getting the existing events array, appending to it, then saving the document.
The extra space in "$pushAll " needs to be removed. It may have quotes: "$pushAll".
Found the problem, I needed to pass "{ upsert = true }" as a third argument to the update function.
To achieve 'upsert' semantics in this case, you'd need to use $addToSet. If you have an array of values to add, you'd need to throw in the $each modifier. From mongo shell:
db.events.update(
{ _id: entityId },
{ $addToSet: { $each: events } }
)

Resources