Mongoose - Array being saved as string - node.js

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)

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 can I updateMany mongodb

I have three chatrooms and I would like to update the document with MongoDB only if the user matches with the members.user_id. I don't understand, it's updating to all the member's devices the same token. If someone has an idea?
It's my Node.js code :
const _id = req.params._id;
const token = req.body.token;
const user_id = req.body.user_id;
try{
const updateDevice = await ChatRoom.updateMany(
{"members.$[].user_id" : user_id},
{$set:{"members.$[].devices":token}})
res.send(updateDevice)
}catch(err){console.log(err)}
const mongoose = require('mongoose');
const chatRoom_schema = new mongoose.Schema({
name:{
type:Array,
name:String,
},
members:{
user_id:String,
name:String,
devices:String,
type:Array,
required:true
},
lastMessage:{
content:String,
createdAt:Date,
type:Array,
send_by:String,
readBy:Array
}
}, {
collection: "chatRoom"
})
module.exports = chatRoom = mongoose.model("ChatRoom", chatRoom_schema);
According to the schema there is a object of members not an array of object so to access the specific elements only dot(.) operator is used. If you want to access any element from the array or want to update the specific object value in array of object then $ is used.
Try this query to solve the problem
ChatRoom.updateMany(
{"members.user_id" : user_id},
{$set:{"members.devices":token}})
Let there be a record like
members=[
{
"user_id":"1",
"name":"DD",
"type":"ADMIN"
},
{
"user_id":"2",
"name":"HH",
"type":"CUSTOMER"
}
]
To update the type of user_id(2) from CUSTOMER to ADMIN then $ operator can be useful.
The query can be
ChatRoom.updateMany({"members.$.user_id" : 2},
{$set:{"members.$.type":"ADMIN"}})

how can I ref a model that is in another folder?

for example the nexts schemas are in diferent folders like these:
cr/user
lms/content
this is the schemaContent
user: {
type: Schema.Types.ObjectId,
ref: 'cr/user'
}
How can I ref user from Content?
because when I used cr/user I get an error "Schema hasn't been registered for model"
I need just populate user from content like Content.find().populate({ path: 'users' }) Keeping user in the folder called cr and content in the folder called lms
You are trying to nest documents. Try below approach to nest document.
i will assume the directories cr and lms are inside the directory called demoproject
demoproject/cr/user.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name:{
type : String,
required: true
}
});
const User = mongoose.model("User",UserSchema);
module.exports = User;
demoproject/lms/content.js
./../cr/user - go back from lms directory and get into cr/user
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const User = require("./../cr/user");
const ContentSchema = new Schema({
type:{
type : String,
required: true
},
users : [{
type : Schema.Types.ObjectId,
ref : "User"
}]
});
const Content = mongoose.model("Content",ContentSchema);
module.exports = Content;
How can I ref user from Content?
i hope your question is clear now. Keep reading below
Moment of truth
Lets insert data by using these two models.
Create a file to write mongo queries (demoproject/index.js).
As User model is nested into Content model, we need to first save data into User model
Then save data into Content model
Now push data into users object present in Content Model contentdata.users.push(userdata);
Donot forget to save the contentdata after pushing the userdata object into contentdata contentdata.save();
demoproject/index.js
const User = require("./cr/user");
const Content = require("./lms/content");
const userobj = new User({name : "rockyjohnson"});
const contentobj = new Content({type : "gloves"});
userobj.save().then((userdata)=>{
contentobj.save().then((contentdata)=>{
contentdata.users.push(userdata);
contentdata.save();
}).catch((err)=>{
console.log("err while saving contentdata ", err);
})
}).catch((err)=>{
console.log("err while saving userdata ", err);
})
Mongodb output
That is all
Update: answer for the second question
find returns an array object in its promise. i'm able to extract users object present inside Content model here. Below is the query i used to verify
Content.find().then((data)=>{
data.forEach(element => {
console.log("element users ",element.users); // element.users contains all id's stored in the users array
});
}).catch((err)=>{
console.log("err ",err);
})

MongoDB - Error: document must have an _id before saving

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.

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