Query (updateOne) is creating data multiplied in MongoDB - node.js

Hello devs. Without further ado...
I have a problem updating a column in the database. When performing an update on a column it is multiplying the data. It doesn't make any sense since the multiplied data contains the same _id. And that _id is a unique value.
Here is a part of the code that contains the query:
const addressId = await new Promise(async function(resolve, reject) {
await Cadastros.updateOne({_id: user_id}, {$push: {enderecos: endereco}}, (err, raw) => {
if(err){
if(!err._id) return reject(false);
if(err._id) return resolve(err._id);
}
return resolve(false);
});
})
This algorithm is responsible for adding a new address to the user's registration in the database using the $push method.
Follows the Schema JSON:
{
cpf: {
type: String,
required: true,
unique: true
},
email: {
type: String,
unique: true,
required: true,
max: 100,
lowercase: true
},
senha: {
type: String,
max: 100
},
nome_completo: {
type: String,
max: 100
},
data_de_nascimento: {
type: Date
},
sexo: {
type: String,
default: null
},
enderecos: [
{
nome_completo: {
type: String
},
cep: {
type: Number
},
endereco: {
type: String,
max: 60
},
num: {
type: Number
},
bairro: {
type: String,
max: 60
},
cidade: {
type: String,
max: 30
},
estado: {
type: String,
max: 2
},
complemento: {
type: String,
max: 21,
default: ""
},
referencia_end: {
type: String,
max: 60,
default: null
},
celular: {
type: Number
},
principal:{
type: Boolean,
default: false,
}
}
],
ip: {
type: String,
max: 45
},
user_agent: {
type: String
},
fingerprint: {
type: String
},
device_info: {
type: String
},
exists: {
type: Boolean,
default: false
},
data_criacao: {
type: Date,
default: Date.now()
}
}
It is also the only middleware in the same table:
CadastrosSchema.post('updateOne', async function(doc, next){
const data = this.getUpdate();
if(data.$push ? data.$push.enderecos : false) return next({_id: data.$push.enderecos._id})
next()
})
And the middleware part was a workaround, but it was the only way I found to return the _id generated from the added subdocument.
Here is the multiplied subdocument:
There are two subdocuments with the same _id. It didn't make any sense to me.
Anyone who knows what is going on and can bring a solution is grateful. There may also be other people with the same problem, so that would help not only me but other people. If there's a problem with that code, I can't see it.

Change $push for $addToSet solved my problem.

Related

Trying to push the value to non-existing field but it won't push using Mongoose

I've been trying updateOne, findOneAndUpdate, and update. Nothing has worked. findOne() operation returns the correct documents.
userProfileModel.updateOne(
{ userEmail },
{
$push: {
userFavLocation: payload,
},
},
(err, result) => {
console.log(err);
console.log(result);
}
);
I get this but no change in my document.
{ ok: 0, n: 0, nModified: 0 }
userEmail and payload have the correct value. When I do findOneAndUpdate, it returns correct document but won't push the value.
This is the Schem for the user profile
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserProfileSchema = new Schema(
{
userEmail: {
type: String,
required: true,
unique: true,
},
userProfilePictureUrl: {
type: String,
},
userSignUpDate: {
type: Date,
},
userId: {
type: String,
required: true,
unique: true,
},
userFirstName: {
type: String,
required: true,
},
userLastName: {
type: String,
required: true,
},
userGender: {
type: String,
required: true,
},
userBirthday: {
type: Date,
required: true,
},
userCoordinates: {
type: {
type: String,
default: 'Point',
},
coordinates: {
type: [Number],
},
},
userFavFacilities: {
type: Object,
},
userHometown: {
address: Object,
},
userContact: {
friends: Object,
},
userOrganizations: {
organizations: Object,
},
userMessages: {
type: Object,
},
userAlerts: {
type: Object,
},
userRoles: Object,
userFacilities: Object,
},
{ collection: 'userprofilemodels' }
);
UserProfileSchema.index({ location: '2dsphere' });
module.exports = UserProfile = mongoose.model(
'userprofilemodels',
UserProfileSchema
);
You have to add the userFavLocation field to your schema or mongoose won't perform the update.
const UserProfileSchema = new Schema(
{
userEmail: {
type: String,
required: true,
unique: true,
},
userFavLocation: [PUT_ARRAY_ITEM_TYPE_HERE],
...
}
}

How to keep id on mongoose findByIdAndUpdate

I am trying to update a 'Board' model in mongoose using findByIdAndUpdate and the model has an array of 'items' (which are objects) on the model. I probably do not understand mongoose well enough but for some reason each item in the array gets an id generated, along with the Board. This is not a problem, it's quite handy actually, however, after doing a findByIdAndUpdate the id on each item has changed. This was quite surprising to me, I really thought they would stay the same. Could this be caused by updating all items in the array? Maybe mongoose is just throwing out the entire array and creating a new one when updating (maybe someone knows?). Anyways, my question is: Is there a way to update the model without changing these id's. I would really like them to stay consistent. The code I am using for update is
exports.updateBoard = asyncHandler(async (req, res, next) => {
let board = await Board.findById(req.params.id);
if (!board) {
return next(new CustomError(`Board not found with id of ${req.params.id}`, 404));
}
// Authorize user
if (board.user.toString() !== req.user.id) {
return next(new CustomError(`User ${req.user.id} is not authorized to update board ${board._id}`, 401));
}
req.body.lastUpdated = Date.now();
board = await Board.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true })
.select('-__v')
.populate({
path: 'user',
select: 'name avatar',
});
// 200 - success
res.status(200).json({ success: true, data: board });
});
and BoardSchema:
const BoardSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: [true, 'Board must have a user'],
},
name: {
type: String,
required: true,
trim: true,
},
description: {
type: String,
required: false,
trim: true,
},
items: [
{
title: {
type: String,
required: true,
trim: true,
},
description: {
type: String,
required: false,
trim: true,
},
dateCreated: {
type: Date,
default: Date.now,
},
lastUpdated: {
type: Date,
default: Date.now,
},
},
],
columns: [
{
name: {
type: String,
required: true,
},
index: {
type: Number,
required: true,
},
show: {
type: Boolean,
required: true,
},
},
],
dateCreated: {
type: Date,
default: Date.now,
},
lastUpdated: {
type: Date,
default: Date.now,
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
},
);

Mongoose upsert doesn't set defaults for Schema.types.mixed properties, setDefaultsOnInsert is true

I'm using an upsert function to serialize users with my backend. I'm using Mongoose's "findOneAndUpdate" with "upsert: true" and "setDefaultsOnInsert: true" set, so that all default values defined in my Schema ought to be used for all parameters that I don't pass to the upsert.
This is my upsert:
let serialize = (params, cb) => {
User.findOneAndUpdate({"fbId" : params.fbId}, params, { upsert: true, setDefaultsOnInsert: true, new: true }, (err, doc) => {
if (err) {
//winston.log
return cb (ErrorTypes.serverError(), null);
}
else if (doc) {
return cb(null, doc);
}
else {
return cb(ErrorTypes.serverError(), null);
}
});
}
For this schema:
const userSchema = new Mongoose.Schema({
fbId: {
type: String,
index: true,
required: "Users must have a facebook ID.",
maxlength: 40
},
firstName: {
type: String,
required: "Users must have a name.",
maxlength: 40
},
lastName: {
type: String,
required: "Users must have a name.",
maxlength: 40
},
gender: {
type: String,
required: "Users must have a gender",
default: "Unknown"
},
ageRange: {
type: String,
default: "Unknown",
maxlength: 25
},
pictureUrl: {
type: String,
required: "Users must have a profile picture."
},
dateJoined: {
type: Number,
required: "Users must have a joined date.",
default: date.getTime()
},
pushId: String,
bookmarks: [{
item: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Item',
required: "An item id is required to create a bookmark."
},
lastOfferMade: Number,
timeOfferMade: Number,
}], default: [],
offersInCategory: {
type: Object,
validate: object => {
let allowedKeys = ['Furniture', 'Household', 'Electronics', 'Other'];
let correctKeys = Object.keys(object).every(key => allowedKeys.includes(key));
let min = 0;
let correctValues = Object.values(object).every(value => value >= min);
return correctKeys && correctValues;
}
}, default: {
'Furniture': 0,
'Household': 0,
'Electronics': 0,
'Other': 0
},
rejectionsInCategory: {
type: Object,
validate: object => {
let allowedKeys = ['Furniture', 'Household', 'Electronics', 'Other'];
let correctKeys = Object.keys(object).every(key => allowedKeys.includes(key));
let min = 0;
let correctValues = Object.values(object).every(value => value >= min );
return correctKeys && correctValues;
}
}, default: {
'Furniture': 0,
'Household': 0,
'Electronics': 0,
'Other': 0
},
blockedUsers: [{
userId: String,
blockedOn: Number
}], default: [],
blockedBy: [{
userId: String,
blockedOn: Number
}], default: [],
hasSeenWalkThrough: { type: Boolean, default: false },
hasBookmarkedItem: { type: Boolean, default: false },
hasSeenSearchPrompt: { type: Boolean, default: false }
});
Passing in these parameters:
var upsertNewParams = {
fbId: "newUpsertFbId",
firstName: "New upsert - User",
lastName: "Test Last Name - User",
gender: "Male",
ageRange: "18-22",
pictureUrl: "url",
pushId: "12345"
}
My upsert function returns this document:
{ _id: 5931b0efbeadf32d858a7578,
fbId: 'newUpsertFbId',
__v: 0,
firstName: 'New upsert - User',
lastName: 'Test Last Name - User',
pictureUrl: 'url',
pushId: '12345',
hasSeenSearchPrompt: false,
hasBookmarkedItem: false,
hasSeenWalkThrough: false,
blockedBy: [],
blockedUsers: [],
default: [],
bookmarks: [],
dateJoined: 1496429065565,
ageRange: '18-22',
gender: 'Male' }
I can clearly see all other defaults such as "hasSeenWalkthrough" and "blockedBy/blockedUsers/bookmarks" get set, but my "offersInCategory" and "rejectionsInCategory" for whatever reason don't, though I provide default values for those properties like any others. How do I set these default values using an upsert?

Mongoose get value from embedded document

i have a scheme like this
var WFWorkItemDocument = new Schema({
id: { type: String, required: true, unique: true, default: uuid.v1 },
description: { type: String },
period: [{
id: { type: String, default: uuid.v1 },
start: { type: Date, default: Date.now }
due: { type: Number, integer: true },
new: { type: Number, integer: true },
}],
i want to get the period's due value for that i used a method like
WorkItem.findOne({ id: idUpdate }, function(err, WorkItem) {
if (err) {
console.log("invlaid id");
//return res.send(404, { error: 'invalid id' });
}
if (WorkItem) {
console.log("id");
console.log(WorkItem.period.due);
} else {
//res.send(404, new Error('Workitem not found'));
}
});
but it doesn't work how can i get the due value??
This is the result for console.log(WorkItem)
Change the schema to embed one object. Unless you need embedded array.
var WFWorkItemDocument = new Schema({
id: { type: String, required: true, unique: true, default: uuid.v1 },
description: { type: String },
period: {
id: { type: String, default: uuid.v1 },
start: { type: Date, default: Date.now }
due: { type: Number, integer: true },
new: { type: Number, integer: true },
},
And if you define it as an embedded array, you can access like :
WorkItem.period[index].due

Mongoose Schema.update doesn't update boolean

I have tried updating other fields and it works just fine.
The command I am using in my API:
User.update({ email: targetUser.email }, { $set: { isAdmin: true }, $push: { 'log.updated': new Date() } }, function (err, user) {
if (err) {
responseObject.err = err;
responseObject.data = null;
responseObject.code = 422;
return res.json(responseObject);
}
return res.json(responseObject);
});
To clarify, when I try to run this, the API returns a code 200, meaning everything worked fine, but when I check the database the isAdmin value wasn't changed.
Any suggestions would be helpful, running out of ideas here!
User Schema as requested:
var UserSchema = new Schema({
name: { type: String, default: "", index: 'text' },
email: { type: String, lowercase: true },
role: { type: String, default: "" },
meta: {
skills: { type: Array, default: [], index: 'text' },
about: { type: String, default: "", index: 'text' },
education: { type: Array, default: [], index: 'text' },
location: {
address: {
a: { type: String, default: "" },
p: { type: String, default: "" },
c: { type: String, default: "" }
},
geo: {
lat: { type: Number, default: 0 },
lng: { type: Number, default: 0 }
}
}
},
compMeta:
{
departments: { type: Array, default: [], index: 'text' },
employees:
[
{
emId: Number,
empName: String,
empDep: String // Dunno if i should use Dep name or Dep ID gonna look in to that later
}
],
}
,
settings: {
search: {
distance: {
n: { type: Number, default: 100 },
t: { type: String, default: "km" }
}
}
},
created: {
type: Date,
default: Date.now
},
//Rating is an array of objects that consist of rateing 0-100 , job database id , comments from the Company
rating:
[
{
rate: Number,
jobId: Number,
jobComments: String
}
],
/*rating:
{
userTotalRating: {type: Number, default: 0},
ratingCounter : {type: Number, default: 0}
}*/
sensitive: {
cpr_cvr: String,
},
stripe: { type: String },
facebook: {},
linkedin: {},
log: {
updated: { type: Array, default: [] }
},
hashedPassword: String,
provider: { type: String, default: 'local' },
salt: String
});
UPDATE:
Mongodb version: 3.0.7
Turns out I just forgot to add the isAdmin field to my User Schema! Also, my call to the update was wrong, I changed it to this:
User.update({ email: targetUser.email }, { $set: { isAdmin: true }}, { $push: { 'log.updated': new Date() } }, function (err, user) {
if (err) {
responseObject.err = err;
responseObject.data = null;
responseObject.code = 422;
return res.json(responseObject);
}
return res.json(responseObject);
});
Thanks to everyone that put an effort to help me! :)
I encountered a similar problem. The solution was to add the callback.
This doesn't work:
Ride.updateOne({driver:req.body.id},{$set:{isBusy:true}});
This works:
Ride.updateOne({driver:req.body.id},{$set:{isBusy:true}},(e,s)=>{});
Try updating two fields with $set
User.update({ email: targetUser.email }, { $set: { isAdmin: true, 'log.updated': new Date() } }, function (err, user) {
if (err) {
responseObject.err = err;
responseObject.data = null;
responseObject.code = 422;
return res.json(responseObject);
}
return res.json(responseObject);
});
Hope it's works.
There is easier way to handle the issue. As per the documentation, the second parameter is the object where you can update the statement.
A.findByIdAndUpdate(id, update, options, callback)
So you just need to take everything inside the update object.
User.update({ email: targetUser.email, $set: {isAdmin: true}} // ... etc

Resources