I am trying to "clear" my referenced field in my mongo db.
I am using mongoose, so i have a modelSchema like this.
const usersSchema = new Schema({
userName: { type: String, required: true },
password: { type: String, required: true },
isAdmin: { type: Boolean },
email: { type: String },
profile: {
type: mongoose.Schema.Types.ObjectId,
ref: 'profiles'
}
})
So my question is... if i already have an objectId added in my profile field...
how can i clear that field?
I am receiving from my client an update request with a json like this.
{ userName: 'admin',
password: '123',
email: 'admin#admin.com',
profile: '',
isAdmin: true }
and my controller do this.
usersCtrl.updateUser = (req, res) => {
userModel.findByIdAndUpdate(req.params.id, { $set: req.body }).then(() => {
res.json('updated')
}).catch((err) => {
console.log(err)
res.json(err)
})
}
but i get this error
CastError: Cast to ObjectId failed for value "" at path "profile"
thanks in advance
I could solve my problem, adding this code in my controller
usersCtrl.updateUser = (req, res) => {
if(req.body.profile == ''){
req.body.profile = null
}
userModel.findByIdAndUpdate(req.params.id, { $set: req.body }).then(() => {
res.json('updated')
}).catch((err) => {
console.log(err)
res.json(err)
})
}
Related
I have a User schema which has reference to a profile schema.
const UserSchema = new Schema(
{
_id: mongoose.Schema.Types.ObjectId,
email: {
....email props...
},
password: {
...password props...
},
profile: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Profile",
}],
},
);
const Profile = new Schema({
_user: {
type: Schema.Types.ObjectId, ref: 'User'
},
'displayName': {
type: String,
default: ''
},
'interestedActivities': ['Ping-pong'], <---- This bad boy/girl is an array
'memberSince': { type: Date, default: Date.now }
}
)
I'd like to create a route which can update the User properties AND the Profile properties in one shot—with a caveat one of the properties on the Profile model is an array!!!
I tried this....
handler
.use(auth)
.put((req, res, next) => {
emailValidator(req, res, next, 'email');
},
async (req, res, next) => {
await connectDB()
const {
profileDisplayName,
profileEmail,
interestedActivities } = req.body;
const update = {
email: profileEmail,
'profile.$.displayName': profileDisplayName,
'profile.$.interestedActivities': interestedActivities
}
const filter = { _id: req.user.id };
const updatedUser = await User.findOneAndUpdate(filter, update, { new: true })
try {
console.log("updatedUser ", updatedUser);
if (updatedUser) {
return res.status(200).send({
updatedUser,
msg: `You have updated your profile, good job!`
});
}
} catch (error) {
errorHandler(error, res)
}
})
export default handler;
My response is:
Status Code: 500 Internal Server Error
Cast to ObjectId failed for value "[
{
id: 'ae925393-0935-45da-93cb-7db509aedf20',
name: 'interestedActivities',
value: []
}
]" (type Array) at path "profile.$"
Does anyone know how I could also afford for the property which is an array?
Thank you in advance!
I´m rather new to this..
If I dont want the user to be able to add duplicated countries to visitedCountry, shoulden unique true work?
Or are there any easy way to block that in the patch?
const User = mongoose.model('User', {
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString('hex')
},
visitedCountries:[ {
country: {
type: Object,
ref: "Country",
unique: true
},
comments: String
}]
})
app.patch('/countries', authenticateUser)
app.patch('/countries', async (req, res) => {
const { username, visitedCountry } = req.body
try {
const countryByAlphaCode = await Country.findOne({ alphaCode: visitedCountry }).lean()
const updatedUser = await User.findOneAndUpdate({ username: username, }, {
$push: {
visitedCountries: { country: countryByAlphaCode, comments: "no comments yet"}
},
}, { new: true })
res.json({ success: true, updatedUser })
} catch (error) {
res.status(400).json({ success: false, message: "Invalid request", error })
}
})
The options unique works for all documents. It prevents two (or more) documents from having the same value for your indexed field. It's often used for the email or username.
For your case, I recommend you to perform a check on the user data before you call findOneAndUpdate.
I am trying to encrypt some passwords and get its salt before saving my model to mongoose in Nestjs, but simply using this to refer to the schema itself doesn't yield any results as it refers to the UserSchemaProvider object itself, instead of the current model I'm trying to save.
My schema provider:
export const UserSchemaProvider = {
name: 'User',
useFactory: (): mongoose.Model<User> => {
const UserSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
birthday: { type: Date, required: true },
celphoneNumber: String,
whatsapp: Boolean,
promo: Object,
status: String
});
UserSchema.pre<User>('save', async (next) => {
const user = this;
console.log(user);
if (user.password) {
const salt = await bcrypt.genSalt();
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) return next(err);
user.password = hash;
next();
});
}
});
return UserSchema;
},
};
and my user Module comes below:
#Module({
imports: [
MongooseModule.forFeatureAsync([
UserSchemaProvider]),
HttpModule
],
controllers: [UsersController],
providers: [UsersService, Validator, ValidationPipe, IsEmailInUseConstraint, GoogleRecaptchaV3Constraint],
})
:Nest Platform Information:
platform-express version: 6.10.14
mongoose version: 6.3.1
common version: 6.10.14
core version: 6.10.14
Your pre hook handler shouldn't be an arrow function () => {}. mongoose handler will need to have the execution context to point to a current document being saved. When using arrow function, your execution context of the pre hook is no longer the document, hence, this inside of the handler isn't the document itself anymore.
export const UserSchemaProvider = {
name: 'User',
useFactory: (): mongoose.Model<User> => {
const UserSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
birthday: { type: Date, required: true },
celphoneNumber: String,
whatsapp: Boolean,
promo: Object,
status: String
});
UserSchema.pre<User>('save', async function(next) { // <-- change to a function instead
const user = this;
console.log(user);
if (user.password) {
const salt = await bcrypt.genSalt();
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) return next(err);
user.password = hash;
next();
});
}
});
return UserSchema;
},
};
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I don't know why it doesn't work for me now, but it did work earlier.
I need to retrieve information from my db. I can easily save data using Model.create but when I want to get data I get:
Query {
_mongooseOptions: {},
_transforms: [],
_hooks: Kareem { _pres: Map {}, _posts: Map {} },
_executionCount: 0,
mongooseCollection: NativeCollection {
collection: Collection { s: [Object] },
Promise: [Function: Promise],
opts: {
bufferCommands: true,
capped: false,
Promise: [Function: Promise],
'$wasForceClosed': undefined
},
name: 'users',
collectionName: 'users',
conn: NativeConnection {
base: [Mongoose],
collections: [Object],
models: [Object],
config: [Object],
replica: false,
options: null,
otherDbs: [],
relatedDbs: {},
states: [Object],
_readyState: 1,
_closeCalled: false,
_hasOpened: true,
plugins: [],
_listening: false,
_connectionOptions: [Object],
client: [MongoClient],
'$initialConnection': [Promise],
_events: [Object: null prototype] {},
_eventsCount: 0,
name: 'test_name',
host: 'cocoondb-shard-00-02-qx9lu.mongodb.net',
port: 27017,
user: 'test',
pass: '1234',
db: [Db]
},
...
I have only one route and use graphql apollo server.
my express route is:
server.js (main file - enterpoint)
import confirmRoute from '../src/routes/confirm';
const app = express();
app.use('/confirm', confirmRoute);
confirm.js
import { Router } from 'express';
import SimpleCrypto from 'simple-crypto-js';
import { env } from '../../environment';
import { User } from '../models/user.model';
const secret = env.TOKEN_SECRET;
const router = Router();
router.get('/*', (req, res) => {
const crypter = new SimpleCrypto(secret);
const id = crypter.decrypt(req.url.slice(1));
const user = User.find({ id }, callback => callback);
res.status(200).send(`Hello, your email confirmed successfully : ${id}`);
})
module.exports = router;
schema
import { Schema, model } from 'mongoose';
const userSchema = new Schema({
firstname: { type: String, required: [false, 'firstname address required'] },
lastname: { type: String, required: [false, 'lastname address required'] },
email: { type: String, required: [true, 'email address required'] },
password: { type: String, required: [true, 'password required'] },
confirmed: { type: Boolean, default: false },
instagram: { type: String, default: "" },
facebook: { type: String, default: "" },
role: { type: String }
}, { timestamps: true });
export const User = model('user', userSchema, 'users');
What am I doing wrong here?
I apologise if my question is silly...
It seems you are not actually executing the query.
Please try one of this solutions to make it work.
Also I used findById, but it does not matter, you can continue to query with findOne also.
Alternative 1: then catch blocks:
router.get("/users/:id", (req, res) => {
User.findById(req.params.id)
.then(doc => {
res.send(doc);
})
.catch(err => {
console.log(err);
return res.status(500).send("something went wrong");
});
});
Alternative 2: callback
router.get("/users/:id", (req, res) => {
User.findById(req.params.id, (err, doc) => {
if (err) {
console.log(err);
return res.status(500).send("something went wrong");
}
return res.send(doc);
});
});
Alternative 3: async/await
router.get("/users/:id", async (req, res) => {
try {
let result = await User.findById(req.params.id);
res.send(result);
} catch (err) {
console.log(err);
return res.status(500).send("something went wrong");
}
});
To apply your case:
router.get("/*", (req, res) => {
const crypter = new SimpleCrypto(secret);
const id = crypter.decrypt(req.url.slice(1));
console.log("id: ", id);
User.findById(req.params.id)
.then(doc => {
console.log("doc: ", doc);
res.status(200).send(`Hello, your email confirmed successfully : ${id}`);
})
.catch(err => {
console.log(err);
return res.status(500).send("something went wrong");
});
});
What am I missing here? I want to add a sub document to the User schema I already have the schema predefined else where.
User.findById(req.body.id, function(err, user) {
if (err) return console.log(err);
reivews: [{
reviewer: req.body.name,
content: req.body.content
}]
user.save(function(err) {
if (err) console.log(err);
res.send('saved')
})
})
It's saying its saved but I don't see the review in the for the user with the id I tried to save to.
Schema
const Review = new Schema({
reviewer: String,
date : { type: Date, default: Date.now },
content : String,
isLive : { type: Boolean, default: false }
});
const User = new Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
reviews: [Review]
});
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('Review', Review);
module.exports = mongoose.model('User', User);
Please try this
User.findById(req.body.id, function(err, user) {
if (err) return console.log(err);
if (user.reviews === undefined || user.reviews.length == 0) {
user.reviews = [];
}
user.reviews.push({
reviewer: req.body.name,
content: req.body.content
});
user.save(function(err) {
if (err) console.log(err);
res.send('saved')
})
})
And ensure that data in user as per the defined Schema