I have an existing collection named users with the following schema. I want to add a new auto incremented field named userNumber in it. I have seen the counter based solution but failed to implement those mainly because I don't see the working where it will do the numbering for the existing documents plus where to place that code. So my question is how to add userNumber field with auto incrementing values and how to populate values for this column in existing records
user.model
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
userType: {
type: String,
required: false,
},
uid: {
type: String,
required: false,
},
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
},
email: {
type: String,
lowercase: true,
},
});
module.exports = mongoose.model('User', userSchema);
here is how it works with counter:
export const CounterSchema = new mongoose.Schema({
_id: {type: String, required: true},
seq: {type: Number, default: 0}
},
the _id could be anything you want. In that case i would name it user
the seq (sequence) is your current count, starting at 0.
to update the counter you will have to call it every time you create a new user.
const callBack: MongooseDocument = await counterModel.findOneAndUpdate({_id:
'user'}, {$inc: {seq: 1}}, {new: true});
to access the new count you call: newCount = callBack.toJSON().seq
to make it work for you current situation, you will have to loop through your users and update them one by one. When that is done, you update your counter and after that you do it whenever you create a user..
First you can add userNumber to your User schema and when you want to create new user then add value to userNumber automatically by get old value from database and incremented.
Related
I am trying to create a database for my Giveaway Bot. It consist of 2 collections, Main (holding settings) and Giveaway which is nested in under the Main collection. I can create my giveaway's without problems. However I want to add some data later on using findOneAndUpdate.
Running the code below I always get this error: Updating the path 'giveaways.duration' would create a conflict at 'giveaways'. Can anyone help solving this issue ?
schema.js
const giveawaySchema = new mongoose.Schema({
_id: String,
destination: String,
duration: String,
winners: String,
price: String,
})
const mainSchema = new mongoose.Schema({
_id: String,
log_channel_id: String,
admin_roles: [],
giveaways: [giveawaySchema],
const Main = mongoose.model("mainSchema", mainSchema);
const Giveaway = mongoose.model("giveawaySchema", giveawaySchema);
module.exports = { Main, Giveaway }
});
Part of my code used for updating:
const mongoose = require("mongoose")
const {Main, Giveaway} = require("../models/schema.js")
const newestGiveaway = await Main.findOneAndUpdate(
{
_id: guildId,
'giveaways._id': giveaway_id,
},
{
"$set":{
"giveaways.duration": "3d",
"giveaways.winners": "20",
"giveaways.price": "Price to Win",
},
},
{
upsert: true,
}
Thank you for your help :)
A small side question. I have fetched the Main document (the parent) before already can I make my search cheaper/ more efficent by only searching through this instead of running the findOneandUpdate method on the whole database ?
Edit 1:
I found that it is neccesary to use the $ operator and have updated my code. However I still get the same error:
{
$set:{
"giveaways.$.duration": "3d",
"giveaways.$.winners": "20,
"giveaways.$.price": "Price to Win",
},
},
Edit 2:
Just to clarify, the creation and nesting of the giveawaySchemas works but I am not able to update the nested document by using the code above.
My child component is already created by using the code below. I now want to update this child (newGiveaway with _id of 1)
const currentGuild = await Main.findOne({_id: guildId})
const newGiveaway = await Giveaway.create({
_id: 1,
destination: 12345678,
});
currentGuild.giveaways.push(newGiveaway)
You can change your schema declaration to use a ref to the giveawaySchema:
const giveawaySchema = new mongoose.Schema({
_id: String,
destination: String,
duration: String,
winners: String,
price: String,
})
const mainSchema = new mongoose.Schema({
_id: String,
log_channel_id: String,
admin_roles: [],
giveaways: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'giveawaySchema'
}],
const Main = mongoose.model("mainSchema", mainSchema);
const Giveaway = mongoose.model("giveawaySchema", giveawaySchema);
module.exports = { Main, Giveaway }
Then, you will just need to update your giveaways directy:
const mongoose = require('mongoose');
const { Giveaway } = require('../models/schema.js');
const newestGiveaway = await Main.findByIdAndUpdate(
giveaway_id,
{
duration: '3d',
winners: '20',
price: 'Price to Win',
},
{
new: true,
}
);
In the mainSchema you define giveaways field as an array of giveawaySchema object. So, you have to treat it as an array, not an object. If you want to treat it as an object, you will have to update mainSchema by removing square bracket at giveawaysSchema.
Relevant Question for how to pushing item into mongo array
I have three mongo schemas, each nest into one another. The main one has a nested JSON, which also has a nested JSON inside that. However, when the User is saved using the main Schema, the other two nested schemas aren't being saved with their default values, why? Here's an example of my three schemas (just an example data structure):
const userContacts = new mongoose.Schema({
user1PhoneNumber: {
type: Number,
default: 0
},
user2PhoneNumber: {
type: Number,
default: 0
}
})
const contact = new mongoose.Schema({
phoneNumber: {
type: Number,
default: 0
},
contacts: {
type: userContacts
}
})
const finalUserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
max: 255
},
email: {
type: String,
required: true,
max: 255,
},
password: {
type: String,
required: true,
min: 10,
max: 1024,
},
contactDetails: {
type: contact
}
})
module.exports = mongoose.model('User', finalUserSchema);
When the user is first created using the mongoose model, the values aren't set as they will be used later on...but they should still appear in the database with their default values:
const user = new User({
username: req.body.username,
email: req.body.email,
password: hashedPassword,
});
What gets saved in the database:
Where is the contactDetials nested JSON object with it's default values, I shouldn't have to provide any data when I first save the user as it should just use the data model with its default values?
May you should try this:
contactDetails: contact
instead of:
contactDetails: {type: contact}
same for Contacts
I have two Schema for user & todo. Every todo has an owner as a user, every user has an array of todos.
// user.js
const TodoSchema = require('./todo').TodoSchema;
var UserSchema = mongoose.Schema({
name: {
type: String,
required: true
},
todos: {
type: [TodoSchema]
}
});
module.exports.UserSchema = UserSchema;
module.exports.UserModel = mongoose.model('UserModel', UserSchema);
// todo.js
var TodoSchema = mongoose.Schema({
body: {
type: String, required: true
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: 'UserModel',
required: true
}
});
module.exports.TodoSchema = TodoSchema;
module.exports.TodoModel = mongoose.model('TodoModel', TodoSchema);
I entered data like this.
var nUser = new UserModel({
name: "Alex
)};
nUser.save().then(user => {
var t = new TodoModel({
body: "my new todo",
owner: user._id
});
t.save().then();
});
But the problem is I want to get all the todos from a specific user, something like this...What is the correct way?
UserModel.findOne({name: "Alex"})
.then(user => {
// user.todos
});
P.S.
I can do this like TodoModel.find({owner: specific_user._id}), but I want it from UserModel.
Since you're asking for the proper way of doing it, I am gonna start with your User Schema. If you want to find all the todos of a user, then putting the todo documents inside an array in the User document is not required. So you should probably remove that from your schema.
After that you can use a simple aggregation to get your desired outcome.
UserModel.aggregate([
{
$match:{
name:"Alex"
}
},
{
$lookup:{
from:"todomodels",
localField:"$_id",
foreignField:"$owner",
as:"todos"
}
}
])
this will return all the todos for that user in an array of the same name.
I am trying to save template based on user id , How can i make sure when template save it save with user id _id ? i added reference to the templateSchema for User.
user.model.js
var UserSchema = new mongoose.Schema({
_id: { type: String, required: true, index: {unique: true}},
firstName: String,
lastName: String,
type: String,
groups:[{type: String, ref: 'Group', required: false}]
},
{
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
});
export default mongoose.model('User', UserSchema);
template.model.js
var User = require('../user/user.model.js');
var TemplateSchema = new mongoose.Schema({
_id: { type: String, required: true},
name: String,
id: String,
appliesTo: [],
properties: [],
createdBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User'}
});
export default mongoose.model('Templates', TemplateSchema);
template.controller.js
var eTemplate = require('./template.model');
export function create(req, res) {
console.log(req.body);
eTemplate.createAsync(req.body)
.then(responseWithResult(res, 201))
.catch(handleError(res));
}
Mongoose has two built-in functions that are called before (pre) and after (post) you save a document. My advice is to make use of them. Here is an example of my code in which I search for an sequence number before saving the user document. You can do the same: When you save the template, make a request for the user id to the database (Or vice-versa). You can even save one, get the id and save the other.
Bellow follows my code for the sequence and the user.
var UserSchema = new Schema({
username: { type: String, required: true, unique: true },
id: { type: String },
...
});
UserSchema.pre('save', function(next) {
let doc = this;
let id = 'userSeq'
Sequence.findByIdAndUpdate(id, { $inc : {nextSId : 1} }, function(error,data) {
if(error)
next(error)
doc.id = data.nextSId-1;
next();
})
});
I hope my answer was useful for you. Just a remark, pre and post are not called in the event of updates for the document.
I'm trying to specify the schema of my db in mongoose. At the moment I do this:
var Schema = mongoose.Schema;
var today = new Date(2011, 11, 12, 0, 0, 0, 0);
var personSchema = new Schema({
_id : Number,
name: { type: String, required: true },
tel: { type: String, required: true },
email: { type: String, required: true },
newsitems: [{ type: Schema.Types.ObjectId, ref:'NewsItem'}]
});
var taskSchema = new Schema({
_id: Number,
description: { type: String, required: true },
startDate: { type: Date, required: true },
newsitems: [{ type: Schema.Types.ObjectId, ref:'NewsItem'}]
});
var newsSchema = new Schema({
_id: Number,
creator : { type: Schema.Types.ObjectId, ref: 'Person' },
task : { type: Schema.Types.ObjectId, ref: 'Task' },
date: { type: Date, required:true },
loc: {type: String, required: true }
});
var NewsItem = mongoose.model('NewsItem', newsSchema);
var Person = mongoose.model('Person', personSchema);
var Task = mongoose.model('Task', taskSchema);
var tony = new Person({_id:0, name: "Tony Stark", tel:"234234234", email:"tony#starkindustries.com" });
var firstTask = new Task({_id:0, description:"Get an interview with the president", startDate:today});
var newsItem1 = new NewsItem({_id:0, creator: tony.id, task: firstTask.id, date: today, loc: "NY"});
newsItem1.save(function (err) {
if (err) console.log(err);
firstTask.save(function (err) {
if (err) console.log(err);
});
tony.save(function (err) {
if (err) console.log(err);
});
});
NewsItem
.findOne({ loc: "NY" })
.populate('creator')
.populate('task')
.exec(function (err, newsitem) {
if (err) console.log(err)
console.log('The creator is %s', newsitem.creator.name);
})
I create the schemas and try to save some data.
The error:
{ message: 'Cast to ObjectId failed for value "0" at path "creator"',
name: 'CastError',
type: 'ObjectId',
value: '0',
path: 'creator' }
I wrote this code based on : http://mongoosejs.com/docs/populate.html#gsc.tab=0
The db I try to create looks like this: Specify schema in mongoose .
How can I fix this?
The example from the mongoose docs you referenced uses Number for the personSchema._id field, and ObjectId for the others.
I presume they do this in the example only to demonstrate that it's possible to use either. If you do not specify _id in the schema, ObjectId will be the default.
Here, all your records have an _id field which is an ObjectId, yet you're treating them like numbers. Furthermore, fields like personID and taskID do not exist, unless you've left out the part where you define them.
If you did want to use numbers for all your _id fields, you'd have to define that in the schemas.
var newsSchema = new Schema({
_id: Number,
_creator: {type: ObjectId, ref: "Person"},
// ...
})
var personSchema = new Schema({
_id: Number,
// ...
})
Then to create a news item with a particular ID, and assign it to a creator:
var tony = new Person({_id: 0});
var newsItem = new NewsItem({_id: 0, creator: tony.id});
However the thing to note here is that when you use something other than ObjectId as the _id field, you're taking on the responsibility of managing these values yourself. ObjectIds are autogenerated and require no extra management.
Edit: I also noticed that you're storing refs on both sides of your associations. This is totally valid and you may want to do it sometimes, but note that you'd have to take care of storing the references yourself in the pre hook.
I was receiving this error after creating a schema:
CastError: Cast to ObjectId failed for value “[object Object]” at path “_id”
Then modifying it and couldn't track it down. I deleted all the documents in the collection and I could add 1 object but not a second. I ended up deleting the collection in Mongo and that worked as Mongoose recreated the collection.