How do I prevent a field update in moongoose - node.js

I have a schema that looks like this :
"use strict"
const mongoose = require('mongoose');
const timestamp = require('mongoose-timestamp');
const CustomerSchema = new mongoose.Schema({
name : {
type : String,
required : true,
trim : true
},
email : {
type : String,
required : true,
trim : true
},
balance : {
type : Number ,
default : 0
}
})
//use timestamps to add created at and updated at
CustomerSchema.plugin(timestamp);
const Customer = mongoose.model('Customer',CustomerSchema);
module.exports = Customer;
When I want to run an update , I run this code
const Customer = require('../models/Customer');
const customer = await Customer.findOneAndUpdate({_id : req.params.id}, req.body);
so req.body carries the data that will be updated
The problem is that I do not want people to update the email field .
So my question is :
How do I lock the email field from being updated . Is there a way to lock it in the schema , so that it can only be added initially but not updated later ?
Thanks .

There is a property you can define at the schema level called immutable.
Reference to documentation: https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-immutable
So your schema would look like this:
const CustomerSchema = new mongoose.Schema({
name : {
type : String,
required : true,
trim : true
},
email : {
type : String,
required : true,
trim : true,
immutable: true // ADD THIS PROPERTY HERE
},
balance : {
type : Number ,
default : 0
}
}

There is no functionality to prevent from update
You can do by removing email object from body
delete req.body.email;
const customer = await Customer.findOneAndUpdate({_id : req.params.id}, req.body);

Related

Mongoose MongoError : 11000

I sent a create post signal through Postman. a once time, the post signal was a successfully. but next time it was failed . error message is like this.
Error creating new record : {
"driver": true,
"name": "MongoError",
"index": 0,
"code": 11000,
"keyPattern": {
"RoutineParts.userId": 1
},
"keyValue": {
"RoutineParts.userId": null
}
}
i cant understand the error message,
my post code and user model code is like that ,
// it's post code.
router.post('/',(req,res)=>{
const newRecord = User ({
username : req.body.username,
email : req.body.email,
password : req.body.password
})
newRecord.save((err,docs)=>{
if(!err) {
res.send(docs)
}else {
console.log('Error creating new record : ' + JSON.stringify(err,undefined,2))
}
})
})
// it's user model
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
username : {type:String},
email: { type: String, required: true},
password: { type: String, required: true, trim: true },
created_at : { type: Date, default: Date.now },
updated_at : { type: Date, default: Date.now },
}
)
const User = mongoose.model('User', userSchema);
module.exports = { User }
i can't understand. In fact 'RoutineParts' is a one of model, but i didn't write user's documents. and, controller path is corrected in app.js
how can i solve it?
it's RoutineParts model
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
routine_name : {type:String},
userId : { type: String, required: true},
exercise_name : { type: String},
order : { type: Number},
}
)
const RoutineParts = mongoose.model('RoutineParts', userSchema);
module.exports = { RoutineParts }
and it's app.js contents
// Connecting data to the MongoDB through Mongoose
require('./db')
const express = require('express')
const app = express()
const PORT = 5000
const bodyParser = require('body-parser')
const userRoute = require('./controller/user')
const routineRoute = require('./controller/routine')
const RP_Route = require('./controller/routineParts')
const EX_Route = require('./controller/excersise')
app.use(bodyParser.json())
app.get("/", function (req, res) {
res.status(201).send("<h1>Hey guys! Hello World !!</h1>");
});
app.listen(PORT, function () {
console.log(`start express server on port ${PORT}`);
});
app.use('/Users', userRoute)
app.use('/Routine', routineRoute)
app.use('/Excersise', EX_Route)
app.use('/RoutineParts', RP_Route)
What's going on
I took a look at your full code. The only real problem, which also fits with the error message you are seeing, is the following :
In your ./models/routineParts.js you've set a unique field. In your case userid. E.g. if your create a new routineParts document with userid "AAA" you can not create another document with the same userid "AAA". In short this means, every user can only create 1 single routineParts document.
The first time you did a POST request to your route localhost:5000/RoutineParts it created the first routineParts document. After that, every request will fail, because it already created 1 routineParts document. ( Read here about unique index with mongoose )
See your ./controller/routineParts.js . If you try to do the same request with a different userid it should work.
How to fix
1 : Remove unique: true from your ./models/routineParts Schema.
2 : ⚡ Drop the index . Mongoose most-likey already registered this index and you have to drop it specifically. Otherwise it will always treat the userid field as unique.
3 : You are still in development, so it shouldn't hurt to also drop the RoutineParts collection.
4 : Restart the app and try to hit the POST localhost:5000/RoutineParts endpoint a couple of times. You now should be able to create multiple routineParts documents under the same user.
How to drop the index
That's a different story. This question should help you with that. They are also using mongoose. If your are using the latest version of mongoose there is a method called cleanIndexes. Use this method after you've removed the unique:true from your model.
For solve this isuse you must remove index duplicate email_1 from index menu

Weird behavior with mongoose Schema $inc operator

I'm working a todo app, it has projects and each project has todolistsand todoListsCount .
When user creates a new todoList I want to increment the project's todoListsCount, the problem I found is that somehow after creating the first todoList the project's todoListsCount successfully increment but the new value is 2 I suppose to what I was expecting which is 2 :
let project= null
await Project.findOneAndUpdate(
{_id:projectId},
{ $inc:{ todoListsCount:1 }},
{ new: true},
(err, doc) => {
if (err)console.log("Something wrong when updating data!");
project =doc
}
);
const todoList = new TodoList({
title,
owner,
projectId,
orderInProject : project.todoListsCount,
created_at : new Date(),
progress : 0,
done_at : new Date(),
todos : [],
done : false,
})
await todoList.save()
as you can see in the Project schema default value of todoListsCount is 0
import mongoose from 'mongoose'
const Schema = mongoose.Schema;
const ProjectSchema = new Schema(
{
/..rest of fields
todosCount : {
type : Number ,
required :false ,
default: 0
},
todoListsCount : {
type : Number ,
required :false ,
default: 0
},
}
)
export const Project = mongoose.model('Project', ProjectSchema);
I guess you are using both await and callback for same function, can be the culprit. Try:
let project = await Project.findOneAndUpdate(
{_id:projectId},
{ $inc:{ todoListsCount:1 }},
{ new: true}).exec();

how to make a mongoose query to search for matching arrays

I am making a chat app where the user when first time sends a message to a person a new conversation is created in the database, But before creating a new one every time, I want to check whether the conversation exists, I'am building this in Node.js and using express framework & I'am using Mongoose to handle the database
const mongoose = require('mongoose');
const Conversation = new mongoose.Schema({
Users : [
{
user_id : {
type : String,
required : true
},
username : {
type : String,
required : true
},
name : {
type : String,
required : true
},
profileUrl : {
type : String,
required : true
}
}
],
createdAt : {
type : Date,
default : Date.now,
required : true
}
});
module.exports = mongoose.model("Conversation",Conversation);
Is there a query where I can check if there is an conversation which matches with the array I'm passing, I couldn't find a way to search for array with objects and I want all the fields to be equal not just single one
//Create a callback function on
module.exports.FindMessage= function (conditionQuery, callback) {
Users .find(conditionQuery, callback)
}
//than
User.FindMessage({$or:[{username:username}]},function(err,message){
if(err) throw err
if(message.length > 0 )
{
}
else
{
}

NodeJS Mongo - Mongoose - Dynamic collection name

So, I want to create a client side based paritioning schema, where I set the collection name as function(), my pseudo code is something like that:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
var ConvForUserSchema = new Schema({
user_id: Number,
conv_hash: String,
archived: Boolean,
unread: Boolean
}, function CollectionName() {
return (this.user_id % 10000);
});
Is this in any way possible through moongose such that both read and writes will work as expected?
Hello you just need to declare schema model with your dinamically name, like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// our schema
function dynamicSchema(prefix){
var addressSchema = new Schema({
dir : {type : String, required : true}, //los 2 nombres delimitados por coma (,) ej. Alberto,Andres
city : {type : String, required: true}, //la misma estructura que para los nombres ej. Acosta, Arteta
postal : {type : Number, required : true},
_home_type : {type : Schema.Types.ObjectId, required : true, ref : prefix + '.home_type'},
state : {type : String, required : true},
telefono : String,
registered : {type : Date, default: Date.now }
});
return mongoose.model(prefix + '.address', addressSchema);
}
//no we export dynamicSchema function
module.exports = dynamicSchema;
so in your code anywhere you can do this:
var userAdress = require('address.js')(id_user);
var usrAdrs1 = new userAddress({...});
userAdrs1.save();
Now go to your mongo shell & list collections (use mydb then show collections), you will see a new collection for address with uid prefix. In this way mongoose will create a new one collection address for each different user uid.
Use the function to get the model dynamically.
/*
* Define Schemas as you used to
*/
const ConvForUserSchema = new Schema({
user_id: Number,
conv_hash: String,
archived: Boolean,
unread: Boolean
},{
versionKey : false,
strict: false
});
/*
* Define the dynamic function
*/
const models = {};
const getModel = (collectionName) => {
if( !(collectionName in models) ){
models[collectionName] = connection.model(
collectionName, ConvForUserSchema, collectionName
);
}
return models[collectionName];
};
Then get the dynamic model using the function
const result = getModel("YourCollectionName").findOne({})
Collection name logic is hard coded all over the Moongose codebase such that client side partitioning is just not possible as things stands now.
My solution was to work directly with the mongo driver -
https://github.com/mongodb/node-mongodb-native
This proved great, the flexibility working with the driver directly allows for everything required and the Moongose overhead does not seem to add much in any case.
Implemented:
//Require Mongoose
const mongoose = require('mongoose');
const moment = require('moment');
//Define a schema
const Schema = mongoose.Schema;
const EntranceModelSchema = new Schema({
name: String,
birthday: Date,
gender: String,
phoneNumber: {type: String, require: true},
email: String,
address: String,
addressReference: String,
addressLatitude: {type: Number, require: true},
addressLongitude: {type: Number, require: true},
vehicleReference: String,
date: Date
});
//Export function to create "SomeModel" model class
module.exports = function(){
let dateSuffix = moment().format('MMMDoYYYY');
const collectionName = `Entrance${dateSuffix}`;
return mongoose.model(collectionName, EntranceModelSchema);
};
Gomosoft's solution works. It needs some amends but the idea works nicely.
The above solution works only the first time. If you are trying to send a second request to the same collection, it will throw an error for trying to overwrite the model that is already defined. So I had to tweak it as follows:
var Rating = require('./app/models/rating');
var myRating;
router.route('/ratings/:user_id')
.post(function(req,res){
var user_id = req.params.user_id;
if(myRating == undefined){
myRating = Rating(user_id);
}
...
rating.save(...);
});
Because I'm checking if myRating is undefined, it will create this reference only once. So no errors will occur.
Hope this helps.
I am adding to the answer by Javier Gomez, to give a solution to Exis Zang's "OverwriteModelError: Cannot overwrite xxx model once compiled" problem. The Schema model file can store an array of the dynamic models based on the Singleton pattern. If that model already exists return it, otherwise create it with new, store it and return it:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const Addresses = {}
// our schema
function DynamicSchema(prefix){
var addressSchema = new Schema({
dir : {type : String, required : true}, //los 2 nombres delimitados por coma (,) ej. Alberto,Andres
city : {type : String, required: true}, //la misma estructura que para los nombres ej. Acosta, Arteta
postal : {type : Number, required : true},
_home_type : {type : Schema.Types.ObjectId, required : true, ref : prefix + '.home_type'},
state : {type : String, required : true},
telefono : String,
registered : {type : Date, default: Date.now }
});
return mongoose.model(prefix + '.address', addressSchema);
}
// this function will store the model in the Addresses object
// on subsequent calls, if it exists, it will return it from the array
function getAddressModel(prefix) {
if (!Addresses[prefix]) {
Addresses[prefix] = new DynamicSchema(prefix)
}
return Addresses[prefix]
}
//now we export getAddressModel function
module.exports = getAddressModel;
To Create a dynamic collection follow the below steps,
const mongoose = require('mongoose');
function createCompanyDynamicSchema(prefix) {
let collectionName = prefix + '_company';
companySchema = new mongoose.Schema(
{
name: { type: String },
enabled: { type: Number, default: 1 },
},
{ timestamps: true },
{ versionKey: false },
{ strict: false }
);
collectionName = mongoose.model(collectionName, companySchema);
return collectionName;
}
module.exports = { createCompanyDynamicSchema };
To call this method from any file,
let companySchema = require('./schema');
_this.createCompany = function () {
return new Promise(async (resolve, reject) => {
let companyCollection = companySchema.createCompanyDynamicSchema('IO');
let addr = new companyCollection({ first_name: 'test' });
addr.save();
});
};
To query from dynamically created collection,
_this.getCompany = function () {
return new Promise(async (resolve, reject) => {
let companyCollection = companySchema.createCompanyDynamicSchema('IO');
let data = await companyCollection.model('IO_users').find();
console.log(data);
});
};

Node.js - How to create a unique id with mongoose db

I am working with Twitter authentication and want to store the twitter id as unique key in mongodb. However i see multiple entries with the same id. Here's my schema and code
Schema:
var TwitterSchema = new Schema({
accessToken: String,
accessTokenSecret: String,
name: String,
twitterId: { type: String, required: true, index: { unique: true, sparse: true } }
});
Code:
mongoose.connect('mongodb://localhost/twd')
mongoose.model('Post', TwitterSchema);
var Post = mongoose.model('Post');
var post = new Post();
post.accessToken = accessToken
post.accessTokenSecret = accessTokenSecret
post.name = twitterUserData.name
post.twitterId = twitterUserData.id
post.save(function(err){
if (err){
throw err;
promise.fail(err);
}
console.log('saved');
mongoose.disconnect();
});
promise.fulfill(post);
DB shell output
> db.posts.find();
{ "twitterId" : "21475255", "name" : "MMMK", "accessTokenSecret" : "ZYhiXMWfXvSr1aaCB93hgU243j8aapP0ALdSFlWEE", "accessToken" : "22475255-9YvKMceUInUIxcEtKAK0oMRRG2ZZxn5c52vnwPw", "_id" : ObjectId("4feddf6155203990e000001") }
{ "twitterId" : "21475255", "name" : "MMMK, "accessTokenSecret" : "ZYhiXMWfXvSr1aaCB93hgU2438aapP0ALdSFlWEE", "accessToken" : "22475255-9YvKMceUInUIxcEtKAK0oMRRG2ZZxn5c52vnwPw", "_id" : ObjectId("4feddf7b5905a1a10e000001") }
My guess is either the index isn't being created in MongoDB, or the index with the same name already exits. If it already exists, mongoose will use ensureIndex to create it, but that won't override/redefine it if it already exists. Use the mongo js shell to see if it exists, then try dropping it, restarting mongod, and running your node.js code again.
http://www.mongodb.org/display/DOCS/Indexes#Indexes-CreationOptions

Resources