identify modified fields by an update query without running it - mongodb

Is there a lib or a method in mongo/mongoose that would be able to simulate the results of an update query and let me know the what the end result would be, and whether or not a field/path was modified
const currentModel = new AwesomeLib({
car: {
make: 'nissan',
model: 'leaf',
colour: 'white',
tags:[ 'electric' ]
},
driver: {
name: 'mino',
status: 'happy'
}
});
currentModel.update({
car: {
model: 'altima',
year: 2018
},
$set: {
driver: {
status: 'sad'
},
'car.tags.1': 'plug in'
}
});
/*
this should result in an object that looks like this:
{
car: {
make: 'nissan',
model: 'altima',
colour: 'white',
year: 2018,
tags:[ 'electric', 'plug in' ]
},
driver: {
status: 'sad'
}
}
*/
currentModel.isModified('driver.name'); // true
currentModel.isModified('driver'); // true
currentModel.isModified('car'); // true
currentModel.isModified('car.colour'); // false
currentModel.isModified('car.tags'); // true

You can use pre hooks in mongoose to acheive this. code would be something like this
Schema.pre('update', function(done) { //be sure not to use an arrow function.
this.isModified('password');
} else {
return done();
}
});

Related

Change field in object in array of object

I have a field achivment with an array of objects. I need to update the field currentPoints in one object that I will find by field name in the array.
Code model of mongoose:
const achive = new Schema(
{
achiveId: ObjectId,
name: { type: String, required: true },
finishedPoints: { type: Number, required: true },
currentPoints: {
type: Number,
default: 0,
set: function (v) {
if (v >= this.finishedPoints) this.isFinished = true;
return v;
}
},
isFinished: { type: Boolean, default: false }
},
{ _id: false }
);
const achivesSchema = new Schema({
userId: ObjectId,
achivement: [achive]
});
Code query:
export async function progressAchive(req, res) {
const value = 3;
try {
const test = await Achives.updateOne(
{
userId: req.user._id,
achivement: { $elemMatch: { name: req.params.nameAchive } }
},
{ $set: { achivement: { currentPoints: value } } },
{ new: true }
);
res.json(test);
} catch (e) {
console.log(e);
}
}
Instead of updating, it removes all objects from the array and leaves them one object with the currentPoint field. How can I update this like I want?
You should use the following for update
const test = await Achives.updateOne(
{
userId: req.user._id,
},
{
$set:{"achivement.$[el].currentPoints": value}
},
{
arrayFilters:[{
"el.name": req.params.nameAchive
}],
new: true
}
);

Mongoose - Update/Find Specific Object in an Array Not Working As Expected

I am following the docs without luck and am at a standstill while trying to update an object in an object in an array using MongoDB and Mongoose.
Here is my document:
{
fields: [
{ id: 603d63086db2db00ab09f50f, data: [Object] },
{ id: 603d63086db2db00ab09f510, data: [Object] },
{ id: 603d63086db2db00ab09f511, data: [Object] },
{ id: 603d63086db2db00ab09f512, data: [Object] },
{ id: 603d63086db2db00ab09f513, data: [Object] },
{ id: 603d63086db2db00ab09f514, data: [Object] },
{ id: 603d63086db2db00ab09f515, data: [Object] }
],
layouts: [],
_id: 603d631a6db2db00ab09f517,
bandId: '603d63146db2db00ab09f516',
eventType: 'private',
ownerId: '6039354906410800c14934c1',
__v: 0
}
I am trying to updateOne of the fields.data in the fields array. fields.data is an object as well.
I call my Express/Node Backend to this route.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.updateOne(
{ _id: req.params.id, "fields.id": req.body.id },
{
"$set": {
"fields.$.data": req.body.data,
},
}
);
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
Where req.body is:
{ id: '603d63086db2db00ab09f50f', data: { type: 1, rate: '200.30' } }
**Just in case it's helpful, here is what one of the fields objects looks like in the document,
{"id":"603d63086db2db00ab09f50f","data":{"type":1,"rate":300}}
I have even tried changing my route to find this document - which I have confirmed exists - Truly at a loss why it won't find the document.
Here is how I changed the above route to find the document.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.find(
{ _id: req.params.id, "fields.id": req.body.id },
);
console.log(updating) //returns []
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
The Model
//Create Schema - QG
const QuoteGeneratorSchema = new Schema({
bandId: {
type: String,
required: true,
},
ownerId: {
type: String,
required: true,
},
fields: {
type: Array,
default: defaultFields,
required: true,
},
eventType: {
type: String,
required: false,
},
layouts: {
type: Array,
required: false,
},
});
let QuoteGenerator = mongoose.model("QuoteGenerator", QuoteGeneratorSchema);
module.exports = QuoteGenerator;
Any nudge in the right direction to replacing that data object with a new data object would be extremely helpful! Thanks!

fetching data from mongodb and electron js using ipc renderer

vue file
this.$electron.ipcRenderer.send('get-result')
this.$electron.ipcRenderer.on('got-it', (event, data) => {
if (data.status) {
this.allResult = data.result
}
else{
this.allResult = ''
}
})
renderer file
ipcMain.on('get-result', (event) => {
todolist.find({}, null, {sort: { creationDate: -1 }}, (err, result) => {
if (!err && result.length) {
event.sender.send('got-it', {
status: true,
result: result
});
} else {
event.sender.send('got-it', {
status: false
});
}
});
});
IN CMD results look like this which is OK
[ { _id: 5dd01fff35ad336558153f8c,
notes: 'hello 3',
creationDate: 2019-11-16T16:12:47.190Z,
__v: 0 },
{ _id: 5dd01efdca8cdf61daa07fcf,
notes: 'Hello Again',
creationDate: 2019-11-16T16:08:29.190Z,
__v: 0 },
{ _id: 5dd01d7a2a4b995f68d36f7c,
notes: 'Hello Mongo Atlas',
creationDate: 2019-11-16T16:02:02.998Z,
__v: 0 },
{ _id: 5dd01c72d43db25eb93c0267,
notes: 'hello mongo',
creationDate: 2019-11-16T15:57:38.799Z,
__v: 0 } ]
But after getting result from renderer browser console look like this
0:$__:(...)
$init:(...)
$locals:(...)
isNew:(...)
_doc:(...)
__ob__
:Observer {value: {…}, dep: Dep, vmCount: 0}
get $__:ƒ reactiveGetter()
set $__:ƒ reactiveSetter(newVal)
get $init:ƒ reactiveGetter()
set $init:ƒ reactiveSetter(newVal)
get $locals:ƒ reactiveGetter()
set $locals:ƒ reactiveSetter(newVal)
and final results are in under the _doc of every index like 0, 1, 2
why is that ? I think it supposed to return just simple array like CMD's printed result.
Is that anything left to fetching organize result or i need to do something else ?
Thank you
Best way ignore this type of format is, after getting result from DB encode in json and then of font-end decode json format which will generate desire result. Like this-
render.js
ipcMain.on('get-result', (event) => {
todolist.aggregate([
{
$sort:{
_id:-1
}
}
])
.exec((err, result) => {
if (!err && result.length) {
event.sender.send('got-it', {
status: true,
result: JSON.stringify(result)
});
} else {
event.sender.send('got-it', {
status: false
});
}
});
});
component.vue
this.$electron.ipcRenderer.send('get-result')
this.$electron.ipcRenderer.on('got-it', (event, data) => {
if (data.status) {
this.allResult = JSON.parse(data.result)
}
else{
this.allResult = ''
}
})

Updating array of objects in mongodb

I'm trying to update an array of objects in my simple-schema. Currently, it removes everything in the database and leaves behind:
"careerHistoryPositions": []
My simple-schema looks like:
const ProfileCandidateSchema = new SimpleSchema({
userId: {
type: String,
regEx: SimpleSchema.RegEx.Id
},
careerHistoryPositions: { type: Array, optional: true },
'careerHistoryPositions.$': { type: Object, optional: true },
'careerHistoryPositions.$.uniqueId': { type: String, optional: true },
'careerHistoryPositions.$.company': { type: String, optional: true },
'careerHistoryPositions.$.title': { type: String, optional: true }
});
If console.log form data looks like:
careerHistoryPositions: [Object, Object],
0: Object
company: "Test company"
title: "Test Title"
uniqueId: 1498004350350
1: Object
company: "Test company 2"
title: "Test Title 2"
uniqueId: 149800433221
My update function:
handleFormSubmit(event) {
event.preventDefault();
const { careerHistoryPositions } = this.state;
ProfileCandidate.update({ _id: this.state.profileCandidateCollectionId },
{ $set: {
careerHistoryPositions
}
}
);
}
I managed to fix this by mapping over my object and running 2 separate updates. The first removes the old element and the second adds the updated version. I'm sure there is a better way to do this, however, this does seem to work.
handleFormSubmit(event) {
event.preventDefault();
const { careerHistoryPositions } = this.state;
ProfileCandidate.update({_id: this.state.profileCandidateCollectionId}, { $unset: {
'careerHistoryPositions': {}
}
})
const updatePosition = this.state.careerHistoryPositions.map((position) => {
ProfileCandidate.update({_id: this.state.profileCandidateCollectionId}, { $push: {
'careerHistoryPositions': {
company: position.company,
title: position.title,
uniqueId: position.uniqueId
}
}
})

How to save/update object in collection and embed object in object?

I have two collections Colors and Cars.
In the car possibly to choose the color.
How to save/update object in collection so that embed Color object in Car object?
Cars = new Mongo.Collection('cars');
Cars.attachSchema(new SimpleSchema({
colorId: {
label: 'Color',
type: String,
autoform: {
options: function () {
return Colors.find().map(function (p) {
return {label: p.colorName, value: p._id};
});
},
label: false
}
},
color: {
type: Object,
},
'color._id': {
type: String,
autoform: {
omit: true,
},
},
'color.colorName': {
type: String,
autoform: {
omit: true,
},
},
'color.colorCode': {
type: String,
autoform: {
omit: true,
},
},
}));
Colors = new Mongo.Collection('colors');
Colors.attachSchema(new SimpleSchema({
colorName: {
type: String,
label: "Color Name",
max: 20,
},
colorCode: {
type: String,
optional: true,
label: "Color Code",
autoform: {
afFieldInput: {
type: "color"
}
}
},
}));
I try use
AutoForm.hooks({ insertCarForm: {before: {
but it did not work
There are several ways that you can achieve this and the solution largly depends on any relevant packages that you might be using. It's hard to give a working example without seeing your existing code that creates new 'cards'. Nevertheless, here is an example using the core Meteor API.
Assuming you have some form Template defined (which I have called 'manageCar'), you would do something like this.
Define a Meteor Method to handle inserting/updating the Car.
Meteor.methods({
updateCar: function(carDoc) {
check(carDoc, { /* carDoc schema */ });
const color = Colors.findOne(carDoc.colorId);
carDoc.color = color;
if (carDoc._id) {
Cars.update(carDoc._id, {
$set: {
colorId: carDoc.colorId,
color: carDoc.color,
}
})
} else {
Cars.insert(carDoc);
}
},
});
Add an event handler for the form submission that calls the defined Method.
Template.manageCar.events({
'click .js-save-car'(event, instance) {
const data = {
_id: event.target._id.value,
colorId: event.target.colorId.value
};
Meteor.call('updateCar', data, function(error, result) {
if (!error) {
alert('Car updated successfully');
}
});
}
});
Long story short, you just need to make sure you have access to the Color id that you are saving for the Car and then make sure you perform a find on the Color collection to retrieve the necessary Color document, then use that for your Car insert or update.
Let me know if you have any questions or need further explanation.