MongoDB - Error: document must have an _id before saving - node.js

I've been struggling so much with this project. I am following a tutorial that is out of date in some areas, for instance their version of Jquery used a totally different format for some functions and I had to do a lot of changing around. But I think I am down to one last major problem that I can't seem to find a fix for. In my Schema variable I've got the _id, username, and password types
var UserSchema = new mongoose.Schema({
_id: mongoose.Schema.ObjectId,
username: String,
password: String
});
but when I go to try to add a new user to my app, instead of getting the alert I am supposed to get, it pops up as [object Object] and nothing gets added to the database. Then this error pops up in the mongo cmd
"Error: document must have an _id before saving".
I've tried commenting out the _id line and I get the right message but still nothing shows up in my database.

Its pretty simple:
If you have declared _id field explicitly in schema, you must initialize it explicitly
If you have not declared it in schema, MongoDB will declare and initialize it.
What you can't do, is to have it in the schema but not initialize it. It will throw the error you are talking about

NestJS with mongoose (#nestjs/mongoose) solution
I fixed the error by
Removing #Prop() above _id
Add mongoose.Types.ObjectId as type to _id
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import mongoose from 'mongoose';
import { Document } from 'mongoose';
export type CompanyDocument = Company & Document;
#Schema()
export class Company {
_id: mongoose.Types.ObjectId;
#Prop({ unique: true })
name: string;
}
export const CompanySchema = SchemaFactory.createForClass(Company);

You can write your model without _id so it will be autogenerated
or
you can use .init() to initialize the document in your DB.
Like:
const mongoose = require('mongoose');
const UserSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
username: String,
password: String
})
module.exports = mongoose.model('User', UserSchema);
and then
const User = require('../models/user');
router.post('/addUser',function(req,res,next){
User.init() // <- document gets generated
const user = new User({
username: req.body.username,
password: req.body.password
})
user.save().then((data)=>{
console.log('save data: ',data)
// what you want to do after saving like res.render
})
}

If you are using mongoose with nest js and GraphQL, I have fixed it by changing the id to _id and removing the #prop above it even the null value of the id problem has vanished. example on github
import { ObjectType, Field, Int, ID } from '#nestjs/graphql';
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
import { User } from 'src/user/entities/user.entity';
import * as mongoose from 'mongoose';
export type SchoolDocument = School & Document;
#ObjectType()
#Schema()
export class School {
#Prop()//remove this
#Field(() => ID,{ nullable: true })
_id: string;
#Prop()
#Field(() => String,{ nullable: true })
name: string;
#Field(()=>[User],{nullable:true})
users:User[];
}
export const SchoolSchema= SchemaFactory.createForClass(School);

Try below snippet I wanted to name _id as userId you can do without it as well.
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var UserSchema = new Schema({
username: String,
password: String
});
UserSchema.virtual('userId').get(function(){
return this._id;
});

_id is added automatically by MongoDb.
If you want to keep _id on your data structure be sure to initialize correctly:
import { Types } from "mongoose";
const obj = new UserSchema({
"_id": new Types.ObjectId(),
"username": "Bill",
"password" : "...."
});

In my case, I accidentally had the following at the end of my Schema. Removing that worked:
{ _id: false }

Look the way i fixed was i put just id in json post request and not _id.

No need to specify the document _id in your model. The system generates the id automatically if you leave out the _id like so:
var UserSchema = new mongoose.Schema({
username: String,
password: String
});
That being said, if you still want to generate the _id yourself, see the answers above.

Related

Mongoose: using the Model.create() method and new Model() constructor seem to ignore parameters

I am trying to save a new document to a collection, but rather than taking the parameters from the Model() constructor or Model.create() method, an empty object is created.
I am probably doing something wrong or missing a small detail somewhere but I am currently stuck. My mongoDB database is hosted locally on mongodb for windows.
I have a schema and model:
import mongoose from 'mongoose';
const CardSchema = new mongoose.Schema({
sideA: String,
sideB: String,
})
export const CardSetSchema = new mongoose.Schema({
user_email: String,
name: String,
cards: [CardSchema],
});
const CardSet = mongoose.model('CardSet', CardSchema);
export default CardSet
I have an endpoint trying to make a new document:
.post(async (req, res) => {
const obj = { user_email: req.user_email, name: req.body.name, cards: [] };
const cardSet = new CardSet(obj);
await cardSet.save();
res.status(201).json(cardSet);
})
When looking at the data with console.log the object and cardSet look the following:
{ user_email: 'example-email#gmail.com', name: 'wa', cards: [] }
{ _id: new ObjectId("62481f4964d4b1789c3110c3") }
My connection URL looks like this:
mongodb://localhost:27017/flash-card-test
When I check MongoDB Compass the collection is indeed being populated with empty objects.
Does anyone have any idea what could be going wrong here? Many thanks!
It was a mistake. I built a model from the CardSchema rather than the CardSet schema.

How to insert data in mongodb using mongoose in typescript?

I had implemented a typescript code for a crud API but currently, I'm facing an issue while inserting data using API using the mongoose package. AS my database is MongoDB so I have used this package.
import Transaction from 'mongoose-transactions-typescript';
I am working on typescript project so that's why I have used this package
public async createAffiliateUser(res_data: any){
console.log("Function");
const transaction = new Transaction();
console.log("Function123");
const AffiliateUserModelName = 'affiliateusers'; // COLLECTION name
console.log(res_data);
await transaction.insert(AffiliateUserModelName, {
name: res_data.name,
userName: res_data.userName,
groupId: res_data.groupId,
commissionCount: res_data.commissionCount,
commissionAmount: res_data.commissionAmount,
is_active: res_data.is_active
});
return await transaction.run();
}
In the above code highlighted line throwing an error like this
TypeError:mongoose_transactions_typescript_1.default is not a constructor
In the above function when I tried to use default create method of mongoose it inserting only single column data even though passing full data as below
{
"name": "Test",
"userName":"test123",
"groupId": "1",
"commissionCount": 1,
"commissionAmount": 2,
"is_active": true
}
So if anyone knows how to insert data in MongoDB using typescript or a solution for the above problem then pls help me to resolve this?
Thank you
I don't know why you are this code structure in order to use mongoose.
The steps that I follow in order to use correctly mongodb documents with mongoose are these:
create a mongoose model schema like this:
// I usually like create this file in a database folder
import mongoose, { Document, Model } from "mongoose";
const Schema = mongoose.Schema;
// creating the actual mongoose schema
const UserSchema = new Schema(
{
firstName: {
type: String,
},
lastName: {
type: String,
},
username: {
type: String,
},
lang: {
type: String,
default: "it",
},
},
{ timestamps: true }
);
// exporting the type in order to have all the correct linting
export interface IUser extends Document {
id: string;
firstName: string;
lastName?: string;
username?: string;
createdAt: Date | number;
updatedAt: Date | number;
}
// registering in mongoose models the schema with the relative interface
const User =
(mongoose.models.User as Model<IUser>) ||
mongoose.model<IUser>("User", UserSchema);
export default User;
at this point let's suppose that you have a tree similar to this:
root_folder
|-- database
| |-- User.ts
|
|-- controllers
|-- addUser.ts
creating the document in the collection:
import { User } from "../../database/User.ts"
async function addUser(){
const newUser = await new User({firstName: "foo", lastName: "bar", username:"testUser"}).save()
}
and now you should have your fresh document in the users collection

Getting id in mongoose post update One hook

This is my mongoose model:
import mongoose, { Schema, Document } from "mongoose";
export interface IUserModel extends Document {
username: string;
id: string;
}
const UserSchema: Schema = new Schema({
username: { type: String },
_id: { type: String }
});
UserSchema.post('updateOne', (doc) => {
console.log("doc._id",doc._id);
});
const UserModel = mongoose.model<IUserModel>("user", UserSchema);
export default UserModel;
I need to access the unique _id in the post hook after I have called updateOne method to call some logic with that id.
However doc._id prints undefined as does doc.id.
when I console.log this it prints to the console:
{ default: Model { user } }
But when I try to access this["default"] it again gives error.
I am calling update method like this :
await UserModel.updateOne({_id: id},userModel)
mongoose version : "^5.11.10"
#types/mongoose version : "^5.10.3"
Any help will be greatly appreciated.

Mongoose - Array being saved as string

In my schema file I'm trying to set the type property to be an array. As seen here
import mongoose from "mongoose";
const model = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
"type" : Array,
})
export const Stocks = mongoose.model("stocks", model)
Although for some reason, when I try to save the document it saves the document as a string even if its an element in an array it still returns a string. This is my code for saving the document;
export async function sendStock(data) {
if (!data) throw new Error("Missing Stock API Data")
let stock = {
_id: mongoose.Types.ObjectId(),
"type": [ data["put_call"] ],
}
Stocks.findOne({"ticker": stock.ticker}, (err, res) => {
if (!res) return new Stocks(stock).save();
})
}
This is the mongoose schema viewed in the website
https://i.stack.imgur.com/TACoo.png
I'm not the best at mongoose but, I'm sure something is up but I'm not saving the document any where else but still save a string.
Your model should be like this
import mongoose from "mongoose";
const model = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
type : [String], //<=== I'm assuming you are receiving string array
})
export const Stocks = mongoose.model("stocks", model)
Also you might want to format the value you are saving as if it's not coming in as an array it will fail.
Try this:
import mongoose from "mongoose";
const model = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
type : { type: []},
})
export const Stocks = mongoose.model("stocks", model)

Mongoose: Property 'pull' does not exist on type 'Address[]'

I'm experiencing difficulties with Mongoose 5.10.0
Consider the following classes
Account:
export class Account {
_id: string;
email: string;
password: string;
phone: string;
address?: Address[] = [];
}
Address:
export class Address {
_id: string;
streetName: string;
zipCode: string;
city: string;
default: boolean | undefined;
}
And the appropriate DB models
Account:
import mongoose, { Schema, Document } from "mongoose";
import { Account } from "../classes/account.class";
import { addressSchema } from "./address.model";
export const accountSchema = new Schema({
_id: Schema.Types.ObjectId,
email: String,
phone: String,
password: String,
address: [addressSchema],
);
export type AccountModel = Document & Account;
export const Accounts = mongoose.model<AccountModel>("Account", accountSchema);
Address:
import mongoose, { Schema, Document } from "mongoose";
import { Address } from "../classes/address.class";
export const addressSchema: Schema = new Schema({
_id: Schema.Types.ObjectId,
streetName: String,
zipCode: String,
city: String,
default: Boolean,
});
export type AddressModel = Document & Address;
export const Addresses = mongoose.model<AddressModel>("Address", addressSchema);
Now when I get an account from the DB and try perform a pull against the address of the result, the pull() - method is not found at all and I get Property 'pull' does not exist on type 'Address[]'.
Is there something fundamentally wrong in the way the schema's are defined? Where am I going wrong?
In your code you're expecting MongoDB's pull operator to be a JavaScript method. You have two ways to achieve what you're trying to do:
You can either use .findOne() to retrieve the document, use JS code to modify the array (e.g. slice) and then run .save() to synchronize these changes with your db.
However if pulling an address is all you want to do then you can run an update operation directly on the database:
await Accounts.update({_id: idToFind}, {"$pull": { "address": { _id: addressId } } })
You'll end up having only one database call when using this 2nd approach
import { Types } from "mongoose";
import { AddressModel } from './Address';
const account = await Accounts.findOne({_id: idToFind});
const accountAddressArray = account.address as Types.DocumentArray<AddressModel>
accountAddress.pull(...)
according to #ZefirTheFear suggestion I tried somethings and it worked for me.
Actually I am using Nestjs. In my entity file I changed only one line:
It was like that.
#Prop({type: [UserAnimalsSchema]})
#Type(()=>UserAnimals)
userAnimals: UserAnimals[]
I changed it like that :
#Prop({type: [UserAnimalsSchema]})
#Type(()=>Types.DocumentArray<UserAnimals>)
userAnimals: Types.DocumentArray<UserAnimals>

Resources