I'm working on a web app project using MEAN stack technology. I want to generate 'empId' (employee ID) automatically when a request to add a new employee is received. How can I do the same and store the generated 'empId' in the same document where I keep employee details?
Thanks in advance!
Unless there are other requirements that haven't been stated, you can simply generate empId when handling the request to add a new employee, and include it with other properties that may be supplied by the caller:
const schema = new Schema({ name: String, empId: String });
const Employee = model('Employee', schema);
// then when handling an "add employee" request:
const { name } = req.body; // properties from request
const empId = generateEmployeeId(); // e.g., "uuid()"
const employee = new Model({name, empId})
You can also automatically include an empId with each new Document by supplying it as a default in the mongoose Schema. Combine that with a library like nanoid (that gives you a bit more control over the ids generated) and you would have something like:
const schema = new Schema({
name: String,
empId: {
type: String,
default: () => nanoid()
}
})
Also keep in mind that mongodb will always create a unique identifier for each document in the _id property.
I solved the code using nanoid and presave feature of mongoose.
The code below generates a user friendly id that can be used for many different utilities.
The only potential issue is if the loop keeps finding same id due to the huge amount of users in db(this should not be an issue most apps), this issue can be reduced by either increasing the size of custom alphabets or by just adding more custom characters.
import mongoose from 'mongoose'
import { customAlphabet } from 'nanoid/async'
const schema = new mongoose.Schema({ name: String, empId: String });
const Employee = mongoose.model('Employee', schema);
employeeSchema.pre('save', async function (next) {
if (this.isNew) {
const nanoid = customAlphabet('1234567890abcdefABCDEF', 10)
let unique = true
let id = ''
while (unique) {
id = await nanoid()
const user = await Employee.findOne({ empId: id })
if (!user) {
unique = false
}
}
this.empId = id
}
next()
})
Related
My Schema
const mongoose = require('mongoose');
const test_schema = new mongoose.Schema({
Name: {
type: String
}
});
const chatting = mongoose.model('chat', test_schema);
module.exports = chatting;
Getting model of above give the schema
const chat = require('./models/chatting.js');
Save Variables
const one = new chat({
Name : "John"
})
const two = new chat({
Name : "John"
})
Now Saving
await chat.insertMany([one, two])
but i got duplicate name key error
You provided a wrong schema to us. (test_schema != chatting_schema). check your chatting schema and see if it is structured somewhat like this:
const chatting_schema = new mongoose.Schema({
Name: {
type: String
unique: true
}
});
if so, remove the unique property.
also, when you already created a document you can just use the .save() function like
await one.save();
I am creating a messaging app and I need to be able to display the users with which a given user has conversation history.
I have users and conversations. The models are below. (There is a messages collection where each document refers to a conversation)
//This is the conversation model.
const mongoose = require('mongoose');
const ConversationSchema = new mongoose.Schema({
participants: {
type: Array,
required: true
}
});
const Conversation = mongoose.model('Conversation', ConversationSchema);
//This is the users model (there's more to it, but this is the relevant stuff)
module.exports = Conversation;
const UserSchema = new mongoose.Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
Each has an _id that is generated.
I am sending an http request with a user ID.
In my controller, using mongoose, I am able to do the following:
Conversation.find({participants:req.params.user_id})
This returns an array of conversation objects, each of which contains a participants array(an array of user IDs).
What I then need to do is match all the participants to the "users" collection so I can get the user ID, first name and last name of each user.
In summary, I need to send an HTTP request with a user ID as a parameter and receive an array of users which have conversation history with the provided user.
What is the next step? How can I match the user IDs I get from the relevant conversations to the user IDs in the users collection?
Try with this
app.post('/:user_id', async(req, res) => {
const partisipant = await Conversation.find({id:req.params.user_id});
const userData = await User.find({id:req.params.user_id});
if(!userData || userData != partisipant){
throw ('no data match')
}
//...if found the data, do anything you want
})
Using async await to get collection data from database, and compare them.
Hope this clue can help you
Given a situation where you have a User Scheme that you use to create a base model called User. And then for user roles, you use mongoose discriminators to create inherited models called Admin, Employee and Client. Is there a way to programmatically determine how many discriminations/inheritances/roles of the User model are available, as well as the available names?
My question in terms of code:
File: models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var options = {discriminatorKey: 'role'};
var userSchema = mongoose.Schema({
name: String,
email: String,
password: String,
},options);
var User = mongoose.model('User', userSchema);
var Client = User.discriminator("Client", mongoose.Schema({
Address : String,
Tax_identification : String,
Phone_number : String,
Internal_Remarks : String,
CRM_status : String,
Recent_contact : String,
}));
var Employee = User.discriminator("Employee",mongoose.Schema({
Staff_Id: String,
}));
module.exports = {User: User, Client: Client, Employee: Employee };
File: controllers/usersController.js
var User = require('../models/user.js').User;
module.exports = {
registerRoutes: function(app){
app.get('user/create',this.userCreateCallback)
},
userCreateCallback: function(req,res){
//Get Available User Roles - The function below doesn't exist,
//Just what I hypothetically want to achieve:
User.geAvailableDiscriminators(function(err,roles){
res.render('user/create',{roles:roles})
});
}
};
I hope I managed to express what I want to do. Alternative approaches are also welcome.
Since v4.11.13, mongoose model has model.discriminators which is an array of models, keyed on the name of the discriminator model.
In your case if you do console.log(User.discriminators) you will get:
{
Client: {
....
},
Employee: {
}
}
As far as I can see, this is not documented anywhere.
Line 158 in lib.helpers.model.discriminators.js is where this is created.
I think you want to fetch the names and values of all the discriminators as for the names you can simply use
User.discriminators
but for finding values you can use this
return Promise.all(Object.keys(discriminators).map(i =>
discriminators[i].find({ userId: this._id }))
).then(promiseResults =>
promiseResults.reduce((arr, el) => arr.concat(el), [])
);
you need to put userId under each discriminators for that.
I've recently started using Mongoose with Express.js in a Node.js application and I have a question about a proper way to design my schemas.
I have several schemas that have some relationships, i.e. Location schema has an array of Objects (it's not a JS object in this context), and Object schema has its Location property. I've learned that relationships in Mongoose are resolved using population, but when I implemented this approach I noticed that I have to type a lot of duplicate code, i.e. whenever I want to create a new Object I have to also update the Location's array of Objects and then assign the Location to the Object's property. Wouldn't it be more trivial to just manually assemble all the Objects that has a locationId property equal to the Location that I want to get from the database in a separate query?
I have also considered just storing Objects in an array in a Location document (as subdocuments) but I decided that I want to be able to work with Objects (create, remove, update) separately from Locations (without querying a Location) so this approach doesn't fit my needs I guess. But then population has its drawbacks too in my case, so I guess it's really the best to just go with manually collecting Objects of a specific Location in a separate query by that Location's id.
I would like to hear an opinion of some professional or advanced user of this technology on designing Mongoose schemas so that I and others don't get into trouble later maintaining and scaling our applications.
Here are my current schemas in question:
var locationSchema = new mongoose.Schema({
title: String,
objects: [{ type: String, ref: 'object' }]
});
var objectSchema = new mongoose.Schema({
title: String,
location: { type: String, ref: 'location' }
});
Checkout this example
db/schemas.js:
const Schema = mongoose.Schema;
const ObjectSchema = {
title: Schema.Types.String
}
const LocationSchema = new Schema({
title: Schema.Types.String,
objects: [{type: Schema.Types.ObjectId, ref: 'Object'}]
})
module.exports = {
Object: ObjectSchema,
Location: LocationSchema
};
db/model.js:
const
mongoose = require('mongoose'),
schemas = require('./schemas');
module.exports = model => mongoose.model(model, schemas[model+'Schema']);
usage:
const
model = require('./db/model'),
LocationModel = model('Location');
LocationModel
.findOne({_id: 'some id here'})
.populate('objects')
.exec((err, LocationInstance) => {
console.log(LocationInstance.title, ' objects:', LocationInstance.objects);
});
when You create an object and want to relate to location:
const
model = require('./db/model'),
ObjectModel = model('Object'),
LocationModel = model('Location');
let
ObjectInstance = new ObjectModel({title: 'Something'});
ObjectInstance.save((err, result) => {
LocationModel
.findByIdAndUpdate(
'some id here',
{$push: {objects: ObjectInstance._id}},
(err) => {
console.log('Object:', ObjectInstance.title, ' added to location');
});
});
updating object data:
const
model = require('./db/model'),
ObjectModel = model('Object');
let id = 'id of object';
ObjectModel
.findByIdAndUpdate(
id,
{title: 'Something #2'},
(err) => {
console.log('Object title updated');
});
finding location by object:
const
model = require('./db/model'),
LocationModel = model('Object');
let id = 'id of object';
LocationModel
.findOne({objects: id})
.populate('objects')
.exec((err, LocationInstance) => {
console.log('Location objects:', LocationInstance.objects);
});
nothing special findOne({objects: id}) will search inside location documents that has relation by id in objects array
any other question welcome (:
I want to give users the ability to create collections in my Node app. I have really only seen example of hard coding in collections with mongoose. Anyone know if its possible to create collections dynamically with mongoose? If so an example would be very helpful.
Basically I want to be able to store data for different 'events' in different collections.
I.E.
Events:
event1,
event2,
...
eventN
Users can create there own custom event and store data in that collection. In the end each event might have hundreds/thousands of rows. I would like to give users the ability to perform CRUD operations on their events. Rather than store in one big collection I would like to store each events data in a different collection.
I don't really have an example of what I have tried as I have only created 'hard coded' collections with mongoose. I am not even sure I can create a new collection in mongoose that is dynamic based on a user request.
var mongoose = require('mongoose');
mongoose.connect('localhost', 'events');
var schema = mongoose.Schema({ name: 'string' });
var Event1 = mongoose.model('Event1', schema);
var event1= new Event1({ name: 'something' });
event1.save(function (err) {
if (err) // ...
console.log('meow');
});
Above works great if I hard code 'Event1' as a collection. Not sure I create a dynamic collection.
var mongoose = require('mongoose');
mongoose.connect('localhost', 'events');
...
var userDefinedEvent = //get this from a client side request
...
var schema = mongoose.Schema({ name: 'string' });
var userDefinedEvent = mongoose.model(userDefinedEvent, schema);
Can you do that?
I believe that this is a terrible idea to implement, but a question deserves an answer. You need to define a schema with a dynamic name that allows information of 'Any' type in it. A function to do this may be a little similar to this function:
var establishedModels = {};
function createModelForName(name) {
if (!(name in establishedModels)) {
var Any = new Schema({ any: Schema.Types.Mixed });
establishedModels[name] = mongoose.model(name, Any);
}
return establishedModels[name];
}
Now you can create models that allow information without any kind of restriction, including the name. I'm going to assume an object defined like this, {name: 'hello', content: {x: 1}}, which is provided by the 'user'. To save this, I can run the following code:
var stuff = {name: 'hello', content: {x: 1}}; // Define info.
var Model = createModelForName(name); // Create the model.
var model = Model(stuff.content); // Create a model instance.
model.save(function (err) { // Save
if (err) {
console.log(err);
}
});
Queries are very similar, fetch the model and then do a query:
var stuff = {name: 'hello', query: {x: {'$gt': 0}}}; // Define info.
var Model = createModelForName(name); // Create the model.
model.find(stuff.query, function (err, entries) {
// Do something with the matched entries.
});
You will have to implement code to protect your queries. You don't want the user to blow up your db.
From mongo docs here: data modeling
In certain situations, you might choose to store information in
several collections rather than in a single collection.
Consider a sample collection logs that stores log documents for
various environment and applications. The logs collection contains
documents of the following form:
{ log: "dev", ts: ..., info: ... } { log: "debug", ts: ..., info: ...}
If the total number of documents is low you may group documents into
collection by type. For logs, consider maintaining distinct log
collections, such as logs.dev and logs.debug. The logs.dev collection
would contain only the documents related to the dev environment.
Generally, having large number of collections has no significant
performance penalty and results in very good performance. Distinct
collections are very important for high-throughput batch processing.
Say I have 20 different events. Each event has 1 million entries... As such if this is all in one collection I will have to filter the collection by event for every CRUD op.
I would suggest you keep all events in the same collection, especially if event names depend on client code and are thus subject to change. Instead, index the name and user reference.
mongoose.Schema({
name: { type: String, index: true },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true }
});
Furthermore I think you came at the problem a bit backwards (but I might be mistaken). Are you finding events within the context of a user, or finding users within the context of an event name? I have a feeling it's the former, and you should be partitioning on user reference, not the event name in the first place.
If you do not need to find all events for a user and just need to deal with user and event name together you could go with a compound index:
schema.index({ user: 1, name: 1 });
If you are dealing with millions of documents, make sure to turn off auto index:
schema.set('autoIndex', false);
This post has interesting stuff about naming collections and using a specific schema as well:
How to access a preexisting collection with Mongoose?
You could try the following:
var createDB = function(name) {
var connection = mongoose.createConnection(
'mongodb://localhost:27017/' + name);
connection.on('open', function() {
connection.db.collectionNames(function(error) {
if (error) {
return console.log("error", error)
}
});
});
connection.on('error', function(error) {
return console.log("error", error)
});
}
It is important that you get the collections names with connection.db.collectionNames, otherwise the Database won't be created.
This method works best for me , This example creates dynamic collection for each users , each collection will hold only corresponding users information (login details), first declare the function dynamicModel in separate file : example model.js
/* model.js */
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
function dynamicModel(suffix) {
var addressSchema = new Schema(
{
"name" : {type: String, default: '',trim: true},
"login_time" : {type: Date},
"location" : {type: String, default: '',trim: true},
}
);
return mongoose.model('user_' + suffix, addressSchema);
}
module.exports = dynamicModel;
In controller File example user.js,first function to create dynamic collection and second function to save data to a particular collection
/* user.js */
var mongoose = require('mongoose'),
function CreateModel(user_name){//function to create collection , user_name argument contains collection name
var Model = require(path.resolve('./model.js'))(user_name);
}
function save_user_info(user_name,data){//function to save user info , data argument contains user info
var UserModel = mongoose.model(user_name) ;
var usermodel = UserModel(data);
usermodel.save(function (err) {
if (err) {
console.log(err);
} else {
console.log("\nSaved");
}
});
}
yes we can do that .I have tried it and its working.
REFERENCE CODE:
app.post("/",function(req,res){
var Cat=req.body.catg;
const link= req.body.link;
const rating=req.body.rating;
Cat=mongoose.model(Cat,schema);
const item=new Cat({
name:link,
age:rating
});
item.save();
res.render("\index");
});
I tried Magesh varan Reference Code ,
and this code works for me
router.post("/auto-create-collection", (req, res) => {
var reqData = req.body; // {"username":"123","password":"321","collectionName":"user_data"}
let userName = reqData.username;
let passWord = reqData.password;
let collectionName = reqData.collectionName;
// create schema
var mySchema = new mongoose.Schema({
userName: String,
passWord: String,
});
// create model
var myModel = mongoose.model(collectionName, mySchema);
const storeData = new myModel({
userName: userName,
passWord: passWord,
});
storeData.save();
res.json(storeData);
});
Create a dynamic.model.ts access from some where to achieve this feature.
import mongoose, { Schema } from "mongoose";
export default function dynamicModelName(collectionName: any) {
var dynamicSchema = new Schema({ any: Schema.Types.Mixed }, { strict: false });
return mongoose.model(collectionName, dynamicSchema);
}
Create dynamic model
import dynamicModelName from "../models/dynamic.model"
var stuff = { name: 'hello', content: { x: 1 } };
var Model = await dynamicModelName('test2')
let response = await new Model(stuff).save();
return res.send(response);
Get the value from the dynamic model
var Model = dynamicModelName('test2');
let response = await Model.find();
return res.send(response);