mongoose findOne() is not a function - node.js

gooModel.js
//user model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const GooSchema = new Schema(
{
goo_id: {
type: String,
required: true,
unique: true,
},
},
{ collection: 'goo_collection' }
);
module.exports = Goo = mongoose.model(
'goo',
GooSchema
);
auth_controller.js
//new user
const NewGoo = new Goo({
goo_id,
})
//function
await NewGoo.findOne({ goo })}
I am getting findOne() is not a function error. I have no idea where I have made a mistake. It creates the collection on my mongo atlas DB (but it is empty). I have exactly the same model in the different route that works. I am super clueless right now.

I think you should call findOne on the model itself, not on the instance/document which NewGoo refers to. So try changing it to:
await Goo.findOne({ goo })}

Related

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.

Mongoose MissingSchemaError: Schema hasn't been registered

Hi I am learning to use mongoose middleware.
What I want is:
When I create a user, it automatically create a userPreference document for that user.
That's why I define a 'save' middleware.
Here is my code:
const mongoose = require('mongoose')
var Schema = mongoose.Schema;
//I define the schema here
const userInfo = new Schema({
Id:{ type: String,unique:true },
key:{ type: String, unique: true },
})
const userPreference = new Schema({
Id:{ type: String,unique:true },
date:Date,
color:String
})
//This is where I define the Model UserPreference , but my code says I didn't define it?
const UserPreference = mongoose.model('userPreference',userPreference);
//Here I add the middleware
userInfo.post('save', async function() {
console.log('Middleware begins')
await this.model("UserPreference").create({Id:this.Id})
});
const UserInfo = mongoose.model('userInfo',userInfo);
//Here I connectToServer and try to create the user
connectToServer( function( err, res ) {
if (err) {console.log(err);}
createUser();
})
async function createUser(){
var Id = '12345';
await UserInfo.create({Id:Id,key:'My-key'});
}
When I run my code, the console outputs the Middleware begins (so the middleware fires successfully).UserInfo also creates successfully.
However then userPreference fails to create and I always get this error:
MissingSchemaError: Schema hasn't been registered for model >"UserPreference".Use mongoose.model(name, schema)
I double check I define UserPreference in my code, and it's also before I use that UserPreference model.
const UserPreference = mongoose.model('userPreference',userPreference);
Because you registered your model with userPreference in:
const UserPreference = mongoose.model('userPreference',userPreference)
So you need to call it in this way:
this.model("userPreference").create({Id:this.Id})
Or:
UserPreference.create({Id:this.Id})

Mongoose Populate cant get this to work?

book.schema.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const BookSchema = new Schema({
name: {
type: String,
required: true
}
})
module.exports = BookSchema
book.model.js
const mongoose = require('mongoose')
const BookSchema = require('../schema/book.schema')
const Book = mongoose.model('Book', BookSchema)
module.exports = Book
novel.schema.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const NovelSchema = new Schema({
name: {
type: String,
required: true
},
type: {
type: Schema.Types.ObjectId,
ref: 'Book'
}
})
module.exports = NovelSchema
novel.model.js
const mongoose = require('mongoose')
const NovelSchema = require('../schema/novel.schema')
const Novel = mongoose.model('Novel', NovelSchema)
module.exports = Novel
query
// Mongoose Populate cant get this to work
// I am at a loss
Novel.findById('5b87310d41073743856a7c4a').populate({
path: 'books'
})
mongoose populate not working。
Please let me know what I am doing wrong or give me a simple explanation...Thank you...
You are right to use the populate for loading the subdocument, but you need to pass the name of the book field in the novel schema, in your case the name is type.
Here is the link to documentation with some examples: Mongoose Populate
And below is the more one solution to your problem:
let book = await new BookModel({name:"book test"}).save();
console.log('-----------BOOK ITEM-------------');
console.log(book);
let novel = await new NovelModel({name:"novel test",type:book._id}).save();
console.log('-----------NOVEL ITEM-------------');
console.log(novel);
let itemPopulated = await NovelModel.findById(novel._id)
.populate('type')
.then((result) => {
return result;
}).catch((err) => {
console.log(err);
});
console.log('-----------ITEM POPULATED-------------');
console.log(itemPopulated);
And the execution output:
The parameter on the function populate() is the path of the field you want to populate, and also when you use populate or any chained function in mongoose you have to use the exec() function in the ending, so the correct way to do it would be:
Novel.findById('5b87310d41073743856a7c4a').populate({
path: 'type'
}).exec()

Cannot overwrite 'user' model once compliled when adding a second schema to a database. [duplicate]

Not Sure what I'm doing wrong, here is my check.js
var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));
var a1= db.once('open',function(){
var user = mongoose.model('users',{
name:String,
email:String,
password:String,
phone:Number,
_enabled:Boolean
});
user.find({},{},function (err, users) {
mongoose.connection.close();
console.log("Username supplied"+username);
//doSomethingHere })
});
and here is my insert.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/event-db')
var user = mongoose.model('users',{
name:String,
email:String,
password: String,
phone:Number,
_enabled:Boolean
});
var new_user = new user({
name:req.body.name,
email: req.body.email,
password: req.body.password,
phone: req.body.phone,
_enabled:false
});
new_user.save(function(err){
if(err) console.log(err);
});
Whenever I'm trying to run check.js, I'm getting this error
Cannot overwrite 'users' model once compiled.
I understand that this error comes due to mismatching of Schema, but I cannot see where this is happening ? I'm pretty new to mongoose and nodeJS.
Here is what I'm getting from the client interface of my MongoDB:
MongoDB shell version: 2.4.6 connecting to: test
> use event-db
switched to db event-db
> db.users.find()
{ "_id" : ObjectId("52457d8718f83293205aaa95"),
"name" : "MyName",
"email" : "myemail#me.com",
"password" : "myPassword",
"phone" : 900001123,
"_enable" : true
}
>
Another reason you might get this error is if you use the same model in different files but your require path has a different case.
For example, in my situation I had require('./models/User') in one file, and then in another file where I needed access to the User model, I had require('./models/user').
I guess the lookup for modules & mongoose is treating it as a different file. Once I made sure the case matched in both it was no longer an issue.
The error is occurring because you already have a schema defined, and then you are defining the schema again. Generally what you should do is instantiate the schema once, and then have a global object call it when it needs it.
For example:
user_model.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
name:String,
email:String,
password:String,
phone:Number,
_enabled:Boolean
});
module.exports = mongoose.model('users', userSchema);
check.js
var mongoose = require('mongoose');
var User = require('./user_model.js');
var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));
var a1= db.once('open',function(){
User.find({},{},function (err, users) {
mongoose.connection.close();
console.log("Username supplied"+username);
//doSomethingHere
})
});
insert.js
var mongoose = require('mongoose');
var User = require('./user_model.js');
mongoose.connect('mongodb://localhost/event-db');
var new_user = new User({
name:req.body.name
, email: req.body.email
, password: req.body.password
, phone: req.body.phone
, _enabled:false
});
new_user.save(function(err){
if(err) console.log(err);
});
I had this issue while 'watching' tests.
When the tests were edited, the watch re-ran the tests, but they failed due to this very reason.
I fixed it by checking if the model exists then use it, else create it.
import mongoose from 'mongoose';
import user from './schemas/user';
export const User = mongoose.models.User || mongoose.model('User', user);
I had this issue while unit testing.
The first time you call the model creation function, mongoose stores the model under the key you provide (e.g. 'users'). If you call the model creation function with the same key more than once, mongoose won't let you overwrite the existing model.
You can check if the model already exists in mongoose with:
let users = mongoose.model('users')
This will throw an error if the model does not exist, so you can wrap it in a try/catch in order to either get the model, or create it:
let users
try {
users = mongoose.model('users')
} catch (error) {
users = mongoose.model('users', <UsersSchema...>)
}
If you are using Serverless offline and don't want to use --skipCacheInvalidation, you can very well use:
module.exports = mongoose.models.Users || mongoose.model('Users', UsersSchema);
I have been experiencing this issue & it was not because of the schema definitions but rather of serverless offline mode - I just managed to resolve it with this:
serverless offline --skipCacheInvalidation
Which is mentioned here https://github.com/dherault/serverless-offline/issues/258
Hopefully that helps someone else who is building their project on serverless and running offline mode.
If you made it here it is possible that you had the same problem i did.
My issue was that i was defining another model with the same name.
I called my gallery and my file model "File". Darn you copy and paste!
I solved this by adding
mongoose.models = {}
before the line :
mongoose.model(<MODEL_NAME>, <MODEL_SCHEMA>)
Hope it solves your problem
This happened to me when I write like this:
import User from '../myuser/User.js';
However, the true path is '../myUser/User.js'
Click here! Official example.
Most important! thing is to export like this
export default mongoose.models.Item || mongoose.model('Item', itemsSchema)
To Solve this check if the model exists before to do the creation:
if (!mongoose.models[entityDBName]) {
return mongoose.model(entityDBName, entitySchema);
}
else {
return mongoose.models[entityDBName];
}
I know there is an accepted solution but I feel that the current solution results in a lot of boilerplate just so that you can test Models. My solution is essentially to take you model and place it inside of a function resulting in returning the new Model if the Model has not been registered but returning the existing Model if it has.
function getDemo () {
// Create your Schema
const DemoSchema = new mongoose.Schema({
name: String,
email: String
}, {
collection: 'demo'
})
// Check to see if the model has been registered with mongoose
// if it exists return that model
if (mongoose.models && mongoose.models.Demo) return mongoose.models.Demo
// if no current model exists register and return new model
return mongoose.model('Demo', DemoSchema)
}
export const Demo = getDemo()
Opening and closing connections all over the place is frustrating and does not compress well.
This way if I were to require the model two different places or more specifically in my tests I would not get errors and all the correct information is being returned.
This may give a hit for some, but I got the error as well and realized that I just misspelled the user model on importing.
wrong: const User = require('./UserModel');
correct: const User = require('./userModel');
Unbelievable but consider it.
Here is one more reason why this can happen. Perhaps this can help someone else. Notice the difference, Members vs Member. They must be the same...
export default mongoose.models.Members || mongoose.model('Member', FamilySchema)
Change to:
export default mongoose.models.Member || mongoose.model('Member', FamilySchema)
What you can also do is at your export, make sure to export an existing instance if one exists.
Typescript solution:
import { Schema, Document, model, models } from 'mongoose';
const UserSchema: Schema = new Schema({
name: {
type: String
}
});
export interface IUser extends Document {
name: string
}
export default models.Users || model<IUser>('Users', UserSchema);
This problem might occur if you define 2 different schema's with same Collection name
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: String,
});
// Trying to get the existing model to avoid OverwriteModelError
module.exports = mongoose.model("user") || mongoose.model('user', userSchema);
You can easily solve this by doing
delete mongoose.connection.models['users'];
const usersSchema = mongoose.Schema({...});
export default mongoose.model('users', usersSchema);
There is another way to throw this error.
Keep in mind that the path to the model is case sensitive.
In this similar example involving the "Category" model, the error was thrown under these conditions:
1) The require statement was mentioned in two files: ..category.js and ..index.js
2) I the first, the case was correct, in the second file it was not as follows:
category.js
index.js
I solved this issue by doing this
// Created Schema - Users
// models/Users.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
export const userSchema = new Schema({
// ...
});
Then in other files
// Another file
// index.js
import { userSchema } from "../models/Users";
const conn = mongoose.createConnection(process.env.CONNECTION_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
conn.models = {};
const Users = conn.model("Users", userSchema);
const results = await Users.find({});
Better Solution
let User;
try {
User = mongoose.model("User");
} catch {
User = mongoose.model("User", userSchema);
}
I hope this helps...
I faced this issue using Next.js and TypeScript. The top answers made it such that typings would not work.
This is what works for me:
const { Schema } = mongoose
export interface IUser {
name: string
email: string
}
const UserSchema = new Schema<IUser>({
name: { type: String, required: true },
email: { type: String, required: true },
})
const UserModel = () => mongoose.model<IUser>('User', UserSchema)
export default (mongoose.models.User || UserModel()) as ReturnType<
typeof UserModel
>
I faced the same Issue with NextJS and MongoDB atlas. I had a models folder
with the model of session stored, but the problem was not that I defined the Schema twice.
Make sure the Collection is empty and does not have a previous Document
If it does, then Simply declare a Model without Schema, like this:
const Session = mongoose.model("user_session_collection")
You can delete the previous records or backup them, create the schema and then apply query on the database.
Hope it helped
Below is the full solution to similar problem when using Mongoose with Pagination in combination with Nuxt and Typescript:
import {model, models, Schema, PaginateModel, Document } from 'mongoose';
import { default as mongoosePaginate } from 'mongoose-paginate-v2';
export interface IUser extends Document {
name: string;
}
const UserSchema: Schema = new Schema({
name: String
});
UserSchema.plugin(mongoosePaginate)
interface User<T extends Document> extends PaginateModel<T> {}
const User: User<IUser> = models['User'] as User<IUser> || model<IUser>('User', UserSchema) as User<IUser>;
export default User
tsconfig.json:
{
"compilerOptions": {
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"#/*": ["./*"]
},
"types": ["#types/node", "#nuxt/types"]
},
"exclude": ["node_modules"]
}
To make pagination working you will also need to install
#types/mongoose-paginate-v2
The above solution should also deal with problems related to hot reloading with Nuxt (ServerMiddleware errors) and pagination plugin registration.
A solution that worked for me was just to check if an instance of the model exists before creating and exporting the model.
import mongoose from "mongoose";
const { Schema } = mongoose;
const mongoosePaginate = require("mongoose-paginate");
const articleSchema = new Schema({
title: String, // String is shorthand for {type: String}
summary: String,
data: String,
comments: [{ body: String, date: Date }],
date: { type: Date, default: Date.now },
published: { type: Boolean, default: true },
tags: [{ name: String }],
category: String,
_id: String,
});
const Post = mongoose.models.Post ? mongoose.models.Post : mongoose.model("Post",articleSchema);
export default Post;
The schema definition should be unique for a collection, it should not be more then one schema for a collection.
If you want to overwrite the existing class for different collection using typescript
then you have to inherit the existing class from different class.
export class User extends Typegoose{
#prop
username?:string
password?:string
}
export class newUser extends User{
constructor() {
super();
}
}
export const UserModel = new User ().getModelForClass(User , { schemaOptions: { collection: "collection1" } });
export const newUserModel = new newUser ().getModelForClass(newUser , { schemaOptions: { collection: "collection2" } });
I had the same problem,
reason was I defined schema an model in a JS function, they should be defined globally in a node module, not in a function.
just export like this
exports.User = mongoose.models.User || mongoose.model('User', userSchema);
ther are so many good answer but for checking we can do easier job.
i mean in most popular answer there is check.js ,our guy made it so much complicated ,i suggest:
function connectToDB() {
if (mongoose.connection.readyState === 1) {
console.log("already connected");
return;
}
mongoose.connect(
process.env.MONGODB_URL,
{
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true,
},
(err) => {
if (err) throw err;
console.log("DB connected");
},
);
}
readyState== 1 means connected
so does not try to connect again
so you won't get the error
i think it because of connecting while it is connected
it is another way of connecting to db
Make sure you are not using the same model name for two different schemas.
Example:
// course model
const mongoose = require("mongoose");
const courseSchema = new mongoose.Schema({
course: {
type: String,
required: true,
},
course_category: {
type: String,
required: true,
}
});
module.exports = mongoose.model("course", courseSchema);
// student model
const mongoose = require("mongoose");
const studentSchema = new mongoose.Schema({
first_name: {
type: String,
required: true,
},
last_name: {
type: String,
required: true,
}
});
module.exports = mongoose.model("course", studentSchema);

How to set ObjectId as a data type in mongoose

Using node.js, mongodb on mongoHQ and mongoose. I'm setting a schema for Categories. I would like to use the document ObjectId as my categoryId.
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
categoryId : ObjectId,
title : String,
sortIndex : String
});
I then run
var Category = mongoose.model('Schema_Category');
var category = new Category();
category.title = "Bicycles";
category.sortIndex = "3";
category.save(function(err) {
if (err) { throw err; }
console.log('saved');
mongoose.disconnect();
});
Notice that I don't provide a value for categoryId. I assumed mongoose will use the schema to generate it but the document has the usual "_id" and not "categoryId". What am I doing wrong?
Unlike traditional RBDMs, mongoDB doesn't allow you to define any random field as the primary key, the _id field MUST exist for all standard documents.
For this reason, it doesn't make sense to create a separate uuid field.
In mongoose, the ObjectId type is used not to create a new uuid, rather it is mostly used to reference other documents.
Here is an example:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Product = new Schema({
categoryId : ObjectId, // a product references a category _id with type ObjectId
title : String,
price : Number
});
As you can see, it wouldn't make much sense to populate categoryId with a ObjectId.
However, if you do want a nicely named uuid field, mongoose provides virtual properties that allow you to proxy (reference) a field.
Check it out:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
title : String,
sortIndex : String
});
Schema_Category.virtual('categoryId').get(function() {
return this._id;
});
So now, whenever you call category.categoryId, mongoose just returns the _id instead.
You can also create a "set" method so that you can set virtual properties, check out this link
for more info
I was looking for a different answer for the question title, so maybe other people will be too.
To set type as an ObjectId (so you may reference author as the author of book, for example), you may do like:
const Book = mongoose.model('Book', {
author: {
type: mongoose.Schema.Types.ObjectId, // here you set the author ID
// from the Author colection,
// so you can reference it
required: true
},
title: {
type: String,
required: true
}
});
My solution on using ObjectId
// usermodel.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
UserSchema.set('autoIndex', true)
module.exports = mongoose.model('User', UserSchema)
Using mongoose's populate method
// controller.js
const mongoose = require('mongoose')
const User = require('./usermodel.js')
let query = User.findOne({ name: "Person" })
query.exec((err, user) => {
if (err) {
console.log(err)
}
user.events = events
// user.events is now an array of events
})
The solution provided by #dex worked for me. But I want to add something else that also worked for me: Use
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
if what you want to create is an Array reference. But if what you want is an Object reference, which is what I think you might be looking for anyway, remove the brackets from the value prop, like this:
let UserSchema = new Schema({
username: {
type: String
},
events: {
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}
})
Look at the 2 snippets well. In the second case, the value prop of key events does not have brackets over the object def.
You can directly define the ObjectId
var Schema = new mongoose.Schema({
categoryId : mongoose.Schema.Types.ObjectId,
title : String,
sortIndex : String
})
Note: You need to import the mongoose module
Another possible way is to transform your _id to something you like.
Here's an example with a Page-Document that I implemented for a project:
interface PageAttrs {
label: string
// ...
}
const pageSchema = new mongoose.Schema<PageDoc>(
{
label: {
type: String,
required: true
}
// ...
},
{
toJSON: {
transform(doc, ret) {
// modify ret directly
ret.id = ret._id
delete ret._id
}
}
}
)
pageSchema.statics.build = (attrs: PageAttrs) => {
return new Page({
label: attrs.label,
// ...
})
}
const Page = mongoose.model<PageDoc, PageModel>('Page', pageSchema)
Now you can directly access the property 'id', e.g. in a unit test like so:
it('implements optimistic concurrency', async () => {
const page = Page.build({
label: 'Root Page'
// ...
})
await page.save()
const firstInstance = await Page.findById(page.id)
const secondInstance = await Page.findById(page.id)
firstInstance!.set({ label: 'Main Page' })
secondInstance!.set({ label: 'Home Page' })
await firstInstance!.save()
try {
await secondInstance!.save()
} catch (err) {
console.error('Error:', err)
return
}
throw new Error('Should not reach this point')
})

Resources