Mongoose one to many relationship - node.js

I have mongoose schema with User data:
// user schema
const User = new Schema(
{
name: {type: String},
email: {type: String, unique: true},
// other fields
})
And User's daily statistics schema:
// Stats schema
const Stats = new Schema(
{
dateCreated: {type: Date, default: Date.now()},
stepsWalked: {type: Number, default: 0},
// other fields
userId: String // user id field
})
When i trying to generate multiple Stats schema objects with the same user id like this:
for (let i = 0; i < 40; ++i) {
statsData = await Stats.create({
userId: userData._id
})
}
I'm getting mongoose duplicate exception on second iteration of the loop.
Stack trace:
MongoError: E11000 duplicate key error collection: 5909aed3df9db12e2b71a579_.stats index: userId_1 dup key: { : "5991c027a572690bfd322c08" }
at Function.MongoError.create (node_modules/mongodb-core/lib/error.js:31:11)
at toError (node_modules/mongodb/lib/utils.js:139:22)
at node_modules/mongodb/lib/collection.js:669:23
at handleCallback (node_modules/mongodb/lib/utils.js:120:56)
at node_modules/mongodb/lib/bulk/unordered.js:465:9
at handleCallback (node_modules/mongodb/lib/utils.js:120:56)
at resultHandler (node_modules/mongodb/lib/bulk/unordered.js:413:5)
at node_modules/mongodb-core/lib/connection/pool.js:469:18
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
How can i implement one-to-many relationship with mongoose ?
I have huge amount of stats data for single user, so i can't store stats data as part of User schema like this:
// user schema
const User = new Schema(
{
name: {type: String, default: 'NaN'},
email: {type: String, unique: true, default: 'NaN'},
// other fields
stats: [Stats] // to many docs to store array in schema
})

I had a similar issue where I was getting duplicate key errors. What happened for me was in a subdocument, I had previously assigned a unique constraint on one field. After correcting that, I continued to get the error. So I could create one instance just fine, but would always get an error when creating a second instance.
The fix for me, which is what another commenter here has mentioned, is to drop that collection. After I dropped the collection, new document and subdocument creation worked just fine.

Related

Foreign Key relationship in Mongoose with customized key

I am kinda new to MongoDB and nextjs space. I have 2 schemas :
User
Fav Dishes
Schema for user is :
import mongoose from 'mongoose'
require('mongoose-type-url');
const UserSchema = new mongoose.Schema({
name: { type: String, unique: true}
address: {type: String},
},{ timestamps: {} })
UserSchema.pre("save", function (next) {
let brand = this;
user.name = user.name.replace(/ /g,"_")
next();
})
Another schema is
const FavDish = new mongoose.Schema({
name: String,
user: {type: mongoose.Schema.Types.ObjectId, ref : 'User'})
So the reference in FavDish collection want to use is userName instead mongo created ObjectID. and while populating the data for FavDish get the details for user as well. How can i achieve this? please help me.
So, first of all, if the relationship between the Schemas are 1to1, consider embedding the favDish inside the user Schema and not referencing (each user has a name, an address and a favDish name).
So the userSchema will look like:
const UserSchema = new mongoose.Schema({
name: { type: String, unique: true}
address: String,
fevDish: String
},{ timestamps: {} })
If you want to keep it like the way you wanted (with two schemas and referencing) you will need to populate after finding the fevDish:
const populatedDish = await FavDishModel.findOne({name: 'pasta'}).populate('user');
/**
* populatedDish - {
* name: 'pasta',
* user: {
* name: 'Justin',
* address: 'NY'
* }
* }
*
*/
this way while finding the dish you want, mongoose will put the user object instead of the _id of it.
Hope that helps.

mongoose autoincrement counter per unique value

I have the two models, entity and post, and I'm trying to create an auto incremented counter on post for each unique entity:
//entity
var entitySchema = mongoose.Schema({
name: String,
counter: Number,
});
//post
var postSchema = mongoose.Schema({
test: String,
entity: {
type: Schema.Types.ObjectId,
ref: 'entity'
},
ticketNumber: Number //this needs to auto increment starting at zero PER user, so entity A has a 1+, entity 2 has a 1+, etc
});
I can have a sequence on entity, check it every time I create a post and use it, but that could possibly have duplicates.
I found a post suggesting an even on pre post'save', but that wouldn't be unique to each entity, just unique overall.
Any way to get this working on the model itself / a better way of doing this?
You can use the npm package called mongoose-auto-increment.
Your connection would look like this:
var autoIncrement = require('mongoose-auto-increment');
var connection = mongoose.connect('YOUR CONNECTION');
autoIncrement.initialize(connection);
your Schema would look like this:
var postSchema = mongoose.Schema({
test: String,
entity: {
type: Schema.Types.ObjectId,
ref: 'entity'
},
ticketNumber: Number
});
postSchema.plugin(autoIncrement.plugin, { model: 'NAME YOUR MODEL', field: 'ticketNumber', startAt: 0, incrementBy: 1 });

When creating a object from a model. the object is undefined

Hello for a project i need to log the export of .csv downloads.
I have searched a lot but still cannot find the answer.
I created a collection: 'tokens' in my mongoDB
The model is located in /src/models/token.coffee
the app is located in /src/app.coffee
controller located in /src/controllers/token.coffee
This is my model:
mongoose = require('mongoose')
timestamps = require('mongoose-timestamp')
enums = require './enums'
schema = mongoose.Schema
# Schema definition
TokenSchema = new schema
user:
type: mongoose.Schema.Types.ObjectId
ref: 'User'
required: true
first_name:
type: String
required: true
last_name:
type: String
required: true
status:
type: String
enums: enums.TokenStatuses.__values
default: enums.TokenStatuses.running
# Plugins
TokenSchema.plugin timestamps, createdAt: 'created_at', updatedAt: 'changed_at'
try
mongoose.model 'Token', TokenSchema
i call the following function from the controller:
create_tokens_record = (user_id) ->
User.findOne {_id: user_id}, (err, user) ->
obj =
user: user._id
first_name: user.first_name
last_name: user.last_name
token = new models.Token(obj)
console.log token
token.save (err) ->
return err if err
And the error is:
events.js:72
throw er; // Unhandled 'error' event
^
TypeError: undefined is not a function
at c:\Users\Daan\api\src\controllers\user.coffee:239:15
at Query.<anonymous> (c:\Users\Daan\api\src\node_modules\mongoose\lib\model.js:3435:16)
at c:\Users\Daan\api\src\node_modules\mongoose\node_modules\kareem\index.js:273:21
at c:\Users\Daan\api\src\node_modules\mongoose\node_modules\kareem\index.js:127:16
at process._tickDomainCallback (node.js:492:13)
I have no idea why my model is still undefined. Hope anyone can help me out!
I found the answer:
In my project there was a index.coffee where all the models were exported.
I forgot to add the newly created model to this file.

How to get around E11000 MongoError without deleting 'unique: true'

I am trying to build a forum in order to learn the MEAN stack. I ran into an issue while using mongoose...
I have this...
var UserSchema = new Schema({
id: ObjectId,
firstName: String,
lastName: String,
role: String,
email: {
type: String,
unique: true
},
password: String,
workers: [WorkerSchema]
});
var TopicSchema = new Schema({
id: ObjectId,
title: String,
moderator: UserSchema,
posts: [PostSchema]
});
var Topic = mongoose.model('Topic', TopicSchema);
app.post('/topics', requireLogin, function(req, res) {
User.findOne({"email": req.session.user.email}, function(err, user) {
if (user.role == "moderator" || user.role == "admin") {
var topic = new Topic({
title: req.body.title,
moderator: req.session.user,
posts: []
});
topic.save(function(err) {
if (err) console.log(err);
res.status(204).end();
});
}
});
});
My issue is this... When I POST a topic to /topics, it works the first time, populating the topics collection with one item. But then, when I POST to /topics again, from the same user, I get an E11000 MongoError that looks like this:
message: 'E11000 duplicate key error index: MY_MONGO_DB.topics.$moderator.email_1 dup key: { : "myuser#example.com" }'
I know that removing the 'unique: true' property from the email field of UserSchema would fix this issue, but I don't want to remove that uniqueness property since I use it elsewhere in my code to ensure that users are unique by email.
Is there any way around this? In other words, is there any way to keep the 'unique: true' property and also retain the ability of users to be able to post multiple topics without triggering the E11000 error?
What you did was to embed the user. In your database, the resulting document would look something like
{
...
moderator: {..., email: "john#example.com"}
}
Which, of course, would violate the unique constraint if you have the same person as a moderator twice.
What you should do instead is to reference the user in your schema:
var user = mongoose.model('User', UserSchema);
var TopicSchema = new Schema({
id: ObjectId,
title: String,
moderator: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
posts: [PostSchema]
});

Why Mongoose Populate Required?

Using mongoose populate:
http://mongoosejs.com/docs/populate.html
It seams that mongoose is forcing me to declare a ref value for populate when I first create the document but in my case i don't have the ref info yet. When I try to create a new document while providing an empty string I get to my developer field I get:
{"message":"Cast to ObjectId failed for value \"\" at path \"developer\"","name":"CastError","type":"ObjectId","value":"","path":"developer"}
Object that I'm saving through mongoose:
var Project = {
name: 'Coolproject',
status: 'pending',
developer: '',
type: 'basic',
};
Project.create(req.body, function(err, project) {
if(err) { return handleError(res, err); }
return
});
My Model:
var ProjectSchema = new Schema({
name: String,
status: {type:String, default:'pending'},
developer:{type: Schema.Types.ObjectId, ref: 'User'},
type:String
});
Basically I need to set it later, but it doesn't seam like this is possible. Currently my work around is populate it with a dummy user until later but this is less than desirable.
Thoughts?
Update
Realized that if i provide a object id like value (55132a418b3cde5546b01b37) it lets me save the document. Very odd. Guess it just figured it can find the document moves on. Wondering why this doesn't happen for a blank value.
The problem is explained in the error message. You cannot save an Empty String in the place of an ObjectId. The field is not listed as 'required', so there is no problem leaving it out and saving the document.
Code correction:
// you can save this
var Project = {
name: 'Coolproject',
status: 'pending',
type: 'basic',
};
You need to use the sparse index in model.
So, the valid model can have developer equal to nil
var ProjectSchema = new Schema({
name: String,
status: {type:String, default:'pending'},
developer:{type: Schema.Types.ObjectId, ref: 'User', sparse:true},
type:String
});
See
http://mongoosejs.com/docs/api.html#schematype_SchemaType-sparse and
http://docs.mongodb.org/manual/core/index-sparse/
for additional info

Resources