Why is property of user object in mongoose showing as undefined? - node.js

This is my Schema:
var UserSchema = new Schema ({
username: String,
password: String,
nativeLanguage: {
type: String,
default: "English"
},
This route prints out the user object to the console.
router.get('/add', isLoggedIn, async (req, res) => {
const Users = await User.find({_id: req.params._id});
console.log(req.user);
res.render('add', {User});
});
What I am trying to do is render specific properties of the user on the add page, to test that I am first trying to access them in the console. Unfortunately User.nativeLanguage comes up as undefined and I don't know why.

User is the model, Users as you named it is the user record.

Lets suppose you export your model.
exports.User = UserSchema;
Then in your route you need.
var user = require('../userModel');
user.find({ _id: req.params._id }, (err, doc) => (
if (!err && doc){
res.render('add', { User: doc});
}
))
Also consider using findOne instead of find if you plan to get only one record.

Related

React-Admin displays data from mongoDB but I cannot Edit

I'm using React Admin for the first time, and my users (coming from mongoDB) are displayed just fine. The problem occurs when I click the "edit" (or "delete") button on a specific user: it says "GET http://localhost:3002/api/users/2a1a3a61-f73b-4a01-b609-ae4bb815f59e 404 (Not Found)"
I use "http://localhost:3002/api/users" to make the GET req to mongoDB: "app.use('/api/users', require('./api/GetUsers'))" and "2a1a3a61-f73b-4a01-b609-ae4bb815f59e" is the id of the user I clicked.
I remember when I first started testing React Admin, that I was using jsonplaceholder.typicode to get data and the edit function was working as well, although, of course, would not persist on refresh.
What am I missing? Is the problem the fact that my api's (http://localhost:3002/api/users) purpose is only getting the data and not post/put also, maybe?
/api/GetUsers
const express = require('express');
const mongoose = require('mongoose');
const ContactUser = require('../DB/ContactUser');
const router = express.Router();
const getUsers = async (req, res) => {
ContactUser.find()
.exec((err, user) => {
if(err){
res.json(err);
} else {
res.setHeader('Access-Control-Expose-Headers', 'Content-Range');
res.setHeader('Content-Range', 'users 0-20/20');
res.json(user);
}
})
};
router.route('/').get(getUsers);
module.exports = router;
/DB/ContactUser
const mongoose = require('mongoose');
const contactUser = new mongoose.Schema({
name: String,
email: String,
message: String,
id: String
});
module.exports = mongoose.model('contactUser', contactUser);
You're missing a second route to retrieve not a list of users, but a single user. It will use the id in the URL to find one user. Something like:
router.get('/:id', function(req, res) {
ContactUser
.findOne({ _id: req.params.id })
.exec((err, user) => err ? res.json(err) : res.json(user));
});

Mongoose: OverwriteModelError: Cannot overwrite `users` model once compiled

I'm using Mongoose to manage a MongoDB server, and all other solutions with this error have not helped. The models aren't defined anywhere else, and the only issue I can think of is that the tables/models already exist on the MongoDB server, but other than that I've never had this issue before.
My current code is this:
const mongoose = require('mongoose');
mongoose.connect(process.env.DATABASE_URL, {useNewUrlParser: true, useUnifiedTopology:true});
const users = require('../../models/Users.schema')
const accounts = require('../../models/Account.schema')
export default (req, res) => {
users.findOne({ email: req.body.email }, function (err, user) {
console.log(user)
accounts.findOne({ userId: user._id }, function (err, account) {
console.log(account)
return res.json({
error: false,
body: account
})
})
})
}
Users schema (mainly for an example, this issue happens with all schemas)
const mongoose = require('mongoose');
const usersSchema = new mongoose.Schema({
name: String,
email: String,
image: String,
createdAt: Date,
updatedAt: Date
})
module.exports.users = mongoose.model('users', usersSchema);
What I'm trying to do is get the data from NextAuth that isn't provided normally, such as the accessToken (session.accessToken is not the right accessToken).
I'm not sure what to do, and I'll take any help I can get.
Thanks!
The error is occurring because you already have a schema defined, and then you are defining the schema again

populate in mongoose returns empty array, i'm Stucked

i'm creating simulation for goodreads by MERN stack
and when i'm using populate to retrieve books of specific user it returns empty array, i've done alot of search but in vain
here's my model
const userSchema =new mongoose.Schema({
firstName:{
type:"string",required:true
},
books:[{
book:{type:mongoose.Schema.Types.ObjectId,ref:'Book'},rate:Number,shelve:''
}]});
and this is books model
const bookSchema =new mongoose.Schema({
title :{
type:"string",required:true
}});
and this is how i use populate
router.get("/one", (req, res, next) => {
User.find({firstName : "John"}).populate("books").exec(function (err, user) {
res.json(user)
});
})
and this is the resulted json
[{"_id":"5c70f299ef088c13a3ff3a2c","books":[]}]
you are referencing the object book inside books array, so you need to populate books.book.
User.find({firstName : "John"}).populate("books.book").exec(function (err, user) {
res.json(user)
});

Saving data to array in mongoose

Users are able to post items which other users can request. So, a user creates one item and many users can request it. So, I thought the best way would be to put an array of users into the product schema for who has requested it. And for now I just want to store that users ID and first name. Here is the schema:
const Schema = mongoose.Schema;
const productSchema = new Schema({
title: {
type: String,
required: true
},
category: {
type: String,
required: true
},
description: {
type: String,
required: true
},
userId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
requests: [
{
userId: {type: Object},
firstName: {type: String}
}
],
});
module.exports = mongoose.model('Product', productSchema);
In my controller I am first finding the item and then calling save().
exports.postRequest = (req, res, next) => {
const productId = req.body.productId;
const userId = req.body.userId;
const firstName = req.body.firstName;
const data = {userId: userId, firstName: firstName};
Product.findById(productId).then(product => {
product.requests.push(data);
return product
.save()
.then(() => {
res.status(200).json({ message: "success" });
})
.catch(err => {
res.status(500).json({message: 'Something went wrong'});
});
});
};
Firstly, is it okay to do it like this? I found a few posts about this but they don't find and call save, they use findByIdAndUpdate() and $push. Is it 'wrong' to do it how I have done it? This is the second way I tried it and I get the same result in the database:
exports.postRequest = (req, res, next) => {
const productId = req.body.productId;
const userId = req.body.userId;
const firstName = req.body.firstName;
const data = {userId: userId, firstName: firstName};
Product.findByIdAndUpdate(productId, {
$push: {requests: data}
})
.then(() => {
console.log('succes');
})
.catch(err => {
console.log(err);
})
};
And secondly, if you look at the screen shot is the data in the correct format and structure? I don't know why there is _id in there as well instead of just the user ID and first name.
Normally, Developers will save only the reference of other collection(users) in the collection(product). In addition, you had saved username also. Thats fine.
Both of your methods work. But, second method has been added in MongoDB exactly for your specific need. So, no harm in using second method.
There is nothing wrong doing it the way you have done it. using save after querying gives you the chance to validate some things in the data as well for one.
and you can add additional fields as well (if included in the Schema). for an example if your current json return doesn't have a field called last_name then you can add that and save the doc as well so that's a benefit..
When using findById() you don't actually have the power to make a change other than what you program it to do
One thing I noticed.. In your Schema, after you compile it using mongoose.modal()
export the compiled model so that you can use it everywhere it's required using import. like this..
const Product = module.exports = mongoose.model('Product', productSchema);

Check if ID exists in a collection with mongoose

For instance, I have a collection User:
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
email: String,
googleId: String,
facebookId: String,
displayName: String,
active: Boolean
});
module.exports = mongoose.model('User', UserSchema);
And then I have an ID:
var userID = "some-user-id"
What is the right way to just check if this id exists in the User collection. I don't need it to read the file or return it, I just need the true or false value.
Here is one way to achieve it:
User.findOne({
_id: userID
}, function (err, existingUser) {
But is there faster and more efficient way?
Use count rather than findOne.
This will (under the hood) cause mongoose to use find : http://docs.mongodb.org/manual/reference/method/db.collection.count
findOne() will read + return the document if it exists
On the other hand, find() just returns a cursor (or not) and only reads the data if you iterate over the cursor.
So in our case, we're not iterating over the cursor, merely counting the results returned.
User.countDocuments({_id: userID}, function (err, count){
if(count>0){
//document exists });
}
});
You can now use User.exists() as of September 2019 like so:
const doesUserExit = await User.exists({ _id: userID });
From the docs:
Under the hood, MyModel.exists({ answer: 42 }) is equivalent to
MyModel.findOne({ answer: 42 }).select({ _id: 1 }).lean().then(doc =>
!!doc)
The accepted answer is fine for small collections.
A faster way on larger collections is to simply use this:
const result = await User.findOne({ _id: userID }).select("_id").lean();
if (result) {
// user exists...
}
// or without "async/await":
User.findOne({ _id: userID }).select("_id").lean().then(result => {
if (result) {
// user exists...
}
});
It won't return all fields. I believe they are currently working on a new feature to support what you (and I) want.
In the meantime you could create a plugin, very simple and reusable.
Create an any.js file with this code:
module.exports = function any(schema, options) {
schema.statics.any = async function (query) {
const result = await this.findOne(query).select("_id").lean();
return result ? true : false;
};
}
Then in your model you do this:
var mongoose = require('mongoose');
const any = require('./plugins/any'); // I'm assuming you created a "plugins" folder for it
var UserSchema = new mongoose.Schema({
email: String,
googleId: String,
facebookId: String,
displayName: String,
active: Boolean
});
UserSchema.plugin(any);
module.exports = mongoose.model('User', UserSchema);
...and use it like this:
const result = await User.any({ _id: userID });
if (result) {
// user exists...
}
// or without using "async/await":
User.any({ _id: userID }).then(result => {
if (result) {
// user exists...
}
});
OR you can simply use exists function, without making any async/await:
myData = {_id: userID};
User.exists(myData,(error, result)=>{
if (error){
console.log(error)
} else {
console.log("result:", result) //result is true if myData already exists
}
});
You can play with the result now!
User.exists({ _id: userID }).then(exists => {
if (exists) {
res.redirect('/dashboard')
} else {
res.redirect('/login')
}
})
More info can be found at Mongoose docs.
The accepted answer is excellent, but I would really recommend using estimatedDocumentCount() if you are searching existing document by an indexed property (like _id of X).
On the other hand, this should actually work better and is cleaner.

Resources