Mongoose: OverwriteModelError: Cannot overwrite `users` model once compiled - node.js

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

Related

Missing Schema Error in connecting to mongodb database from node

I am simply trying to add data to a collection in a mongodb database. I think technically I am trying to create a document in a new collection (the textbook I am using mentions CRUD (create, read, update, and delete). I am doing this with the npm - mongoose. Here is my code:
In a file called user.js (in a models folder)
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: String,
username: String,
password: String,
});
const User = mongoose.model(("User", userSchema));
module.exports = User;
Then, in a file called 'test.js' in the app directory:
const mongoose = require("mongoose");
const User = require("./models/user");
mongoose.connect("mongodb://localhost/my_database", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
User.create(
{
name: "Leon White",
username: "lwhite",
password: "password",
},
(error, user) => {
console.log(error, user);
}
);
The error I am getting is
"MissingSchemaError: Schema hasn't been registered for model "[object Object]".
Use mongoose.model(name, schema)"
I have checked and have it exactly as the textbook indicates and have looked at similar questions with this error code, but I can't figure out where in my code I have gone wrong. Any help?

How does mongoose know what collection I am accessing?

I'm having a trouble grasping a concept in Mongoose.
I'm using MongoDB atlas, got a cluster , a database and 2 collections.
users, characters.
Through a guide I've learned that a good way to write your stuff is to have a model (I use the naming schema) as a file, importing it into your Database module/class
and using it there to perform a query...
const mongoose = require("mongoose");
const process = require("./config.env");
db = () => {
return mongoose
.connect(process.env.URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: true,
})
.then((response) => {
console.log(`Connected to Databse : ${response.connection.host}`);
})
.catch((err) => {
console.log("DB_ERROR:", err);
process.exit(1);
});
};
module.exports = db;
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
});
const User = mongoose.model("User", UserSchema);
module.exports = User;
const User = require("../schemas/User");
const db = require("../config/db");
class Database {
constructor(db, collection) {
this.db = db;
this.collection = collection;
this.User = User;
}
connect() {
return db();
}
}
module.exports = Database;
one file to handle the db connection..another file as the User schema and a third file to handle every function i might use globally...
One thing I cannot wrap my mind around is
how is the findOne() function able to locate the collection I am using without me telling it what collection i want it to search in?
is it somehow translating the
const User = mongoose.model("User", UserSchema);
line and searching for "users" as well? I just can't understand the magic behind this...
what if I want to search specifically in the characters collection...?
Mongoose uses the model name, as passed when it was created: mongoose.model("User", UserSchema), converted to lower case and with an 's' appended.
For the model User it uses the collection users by default. You can change this by explicitly specifying the collection name in the schema.

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);

Mongoose - inserting subdocuments

I have a user model, and a log model. The log model is a subdocument of user model. So in my user model I have:
var mongoose = require('mongoose');
var Log = require('../models/log');
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
Log
]
});
Then in my 'Log' model I have:
var mongoose = require('mongoose');
var logSchema = new mongoose.Schema({
logComment: {
type: String,
},
});
module.exports = mongoose.model('Log', logSchema);
So upon creation of a 'user', the 'logsHeld' always begins empty. I want to know how to add subdocuments to this user model.
I've tried doing this POST method:
router.post('/createNewLog', function(req, res) {
var user = new User ({
logssHeld: [{
logComment: req.body.logComment
}]
});
user.save(function(err) {
if(err) {
req.flash('error', 'Log was not added due to error');
return res.redirect('/home');
} else {
req.flash('success', 'Log was successfully added!');
return res.redirect('/home');
}
});
});
But this doesn't work. It also includes a 'new User' line, which I don't think I need given this would be for an existing user.
You need to use the logSchema instead of the Log model as your subdocument schema in User model. You can access the schema as follows:
var mongoose = require('mongoose');
/* access the Log schema via its Model.schema property */
var LogSchema = require('../models/log').schema; // <-- access the schema with this
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [LogSchema]
});
Picking up from your comments in another answer where you are facing another issue
WriteError({"code":11000,"index":0,"errmsg":"E11000 duplicate key
error index: testDB.users.$email_1 dup key:
you are getting this because there's already a document in your users collection that has most probably a null value on the email field. Even though your schema does not explicitly specify an email field, you may have an existing old and unused unique index on users.email.
You can confirm this with
testDB.users.getIndexes()
If that is the case and manually remove the unwanted index with
testDB.users.dropIndex(<index_name_as_specified_above>)
and carry on with the POST to see if that has rectified the error, I bet my $0.02 that there is an old unused unique index in your users collection which is the main issue.
Try using logSchema which references only the subdocument schema, Log refers to the entire contents of ../models/log
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
logSchema
]
});
Documentation: http://mongoosejs.com/docs/subdocs.html
Try push to insert item in array in mongoose
var user = new User;
user.logssHeld.push({
logComment: req.body.logComment
});
user.save(function(err, doc) {
//DO whatever you want
});
see the docs here

How do I find missing schema fields on a Mongoose query

If I add new fields directly to my MongoDB database and I forget to add them to my Mongoose schema, how can I alert myself to the problem without it failing silently.
The following example shows that all fields are returned from a query (regardless of the schema) but undefined if you access the key.
var mongoose = require('mongoose');
var user_conn = mongoose.createConnection('mongodb://db/user');
var userSchema = mongoose.Schema({
email: String,
// location: String,
admin: Boolean
});
var User = user_conn.model('User', userSchema);
User.findOne({email: 'foo#bar.com.au'}, function (err, doc) {
if (err) return console.log(err);
console.log(doc);
console.log(doc.email);
console.log(doc.location);
});
Result:
{ _id: 57ce17800c6b25d4139d1f95,
email: 'foo#bar.com.au',
location: 'Australia',
admin: true,
__v: 0 } // <-- console.log(doc);
foo#bar.com.au // <-- console.log(doc.email);
undefined // <-- console.log(doc.location);
I could read each doc key and throw an error if undefined, but is this the only way?
Versions
Node.js: 6.5.0
Mongoose: 4.6.0
You can set strict to false on the schema so it will save all properties even if they are not in schema:
var userSchema = mongoose.Schema({
email: String,
admin: Boolean
}, {strict: false});
http://mongoosejs.com/docs/guide.html#strict
In order to get a property which is not in schema you need to use doc.toObject() and use the returned object, or to use doc.get('location')
Following on from Amiram's answer. I now use lean() to get the object during a query but when I need to update or save it still looks up the schema.
User.findOne({email: 'foo#bar.com.au'}).lean().exec(function (err, doc) {
console.log(doc);
console.log(doc.email);
console.log(doc.location);
});

Resources