How to make Mongoose Schema id generate a 16 digits random number - node.js

I'm new to mongoose. I'm creating a model (creditnote) that consists of a 16 digits unique reference (serves as primary key) and an integer that represents the number of credits.
I want the reference to be the model's id and to be an auto-generated 16 digits number. How can I achieve this?
Right now I have the following code:
var mongoose = require('mongoose');
// Setup schema
var creditnoteSchema = mongoose.Schema({
reference: {
type: mongoose.ObjectId,
required: true,
default: function(){
// Generate 16 digits random number
number = (Math.random()+' ').substring(2,10)+(Math.random()+' ').substring(2,10);
}
},
amount: {
type: Number,
required: true,
validate: {
validator: Number.isInteger,
message: '{VALUE} is not an integer value'
}
}
});

All objects created in MongoDB have a property _id which serves as a unique ID. You can also use ObjectId to generate a unique key, like this:
const {ObjectId} = require('mongodb');
console.log(ObjectId());

Related

E11000 duplicate key error collection: archiwiz.clients index: cnic_1 dup key: { cnic: null }

Why do I get this error?When I don't have the Cnic field in my DB anymore.
Here is my schema....
const mongoose = require('mongoose')
const uuidv1 = require("uuidv1")
const crypto = require("crypto")
const { ObjectId } = mongoose.Schema
const clientSchema = new mongoose.Schema({
name:{
type:String,
trim: true,
required: true,
// match:[
// new RegExp('^[a-z]+$', 'i'),
// 'Name Should have alphabets'
// ]
},
phone:{
type: String,
trim: true,
required: true,
unique: true
},
email:{
type: String,
trim: true,
required: true,
unique:true
},
hashed_password:{
type: String,
required: true
},
salt:String,
created:{
type: Date,
default: Date.now
},
createdBy:{
type: ObjectId,
ref: "Admin"
},
updated: Date
})
clientSchema.virtual('password')
.set(function(password){
this._password = password
// generate a timestamp
this.salt = uuidv1()
// encrypt password
this.hashed_password = this.encryptPassword(password)
})
.get(function(){
return this._password
})
clientSchema.methods = {
authenticate: function(plainText){
return this.encryptPassword(plainText) === this.hashed_password
},
encryptPassword: function(password){
if(!password) return "";
try{
return crypto
.createHmac("sha1", this.salt)
.update(password)
.digest('hex');
}catch (err){
return ""
}
}
}
module.exports = mongoose.model("Client", clientSchema)
I have made some changes , before this I had the Cnic field which was Unique...I have deleted that field.When I create a first user it gets created successfully but when I create a second user it gives the error above in the title...What is happening and how to solve it..?
------------------------SOLUTION----------------------------------------
I dropped a collection in my DB and it worked for me. Detailed answer is available in the comment section provided my #Scott Gnile
What happens is that MongoDB does not store the schema in the collection as if it were a table in a SQL Database, but it does store the indexes.
When you made the field unique, MongoDb created an index on that collection for that field.
So after inserting one document without that field, an entry in the index list goes as null. When you insert another document without that field, you are trying to insert another document with the same value null in the collection, and it fails because that field is supposed to be unique.
I've done the step by step and took a screenshot for you, see it below:
If you had a lot of data in that collection, dropping it wouldn't be an option, so you could drop the index, and it would work fine.
First, you list the indexes of such collection
Then, you identify which index to drop
You drop the index
Then you insert the same document that previously failed
See it below:
But there's an intermediate solution if you want to have an optional field that is unique across documents. This means that such a field is not present all the time, but it has to be unique when it is.
The solution for that is to create a sparse index. In the image below, you'll see that:
A new index (sparse and unique) is created on the same collection
The index creation happens successfully, which wouldn't happen if there were duplicate values for the unique field.
A document without the unique field is inserted with no issues
A document is inserted with a value for the unique field with success
Another document is attempted to be inserted in the collection with the same value for the unique field as the previous document, and as expected, it fails
Then, finally, another document is successfully inserted with a different value for that field.

avoid duplicates on MongoDB using Mongoose

Hi I'm new to MongoDB and Moongoose I'm trying to avoid my api's users to store on the Mongo database duplicated contact's name but seems like it's not working at all.
This is how I'm trying to do it right now the name and the phone number are mandatory and also the name must be unique otherwise it should throw an error.
const contactSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true
},
number: {
type: Number,
required: true
}
});
app.post('/api/persons', (request, response) => {
const body = request.body;
const person = new Contact({
name: body.name,
number: +body.number
});
person.save()
.then(saved => {
response.json(saved);
})
.catch(error => {
return response.status(400).json({
error: 'content missing'
});
});
})
If I send a post request with missing name or number it already throws an error but seems like it's not gettin the unique value validation.
Finally found a package that allows me to avoid duplicted entries on Mongo. I used this package following the documentation instructions:
https://github.com/blakehaswell/mongoose-unique-validator#readme
This is the code I had to write:
const uniqueValidator = require('mongoose-unique-validator');
const contactSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true
},
number: {
type: Number,
required: true
}
});
contactSchema.plugin(uniqueValidator);
the error of unique validation is weird so you can use unique-validator plugin, after that when you send a post request with missing name or number, the error is about required: true
Refer to validation
Validators are not run on undefined values. The only exception is the required validator.
Since both the fields(name and number) in your DB are required.
Instead of directly passing the request body to the query, you can do something like this.
const name = body.name;
const number = body.number;
if(!name || !number) {
// Return response saying either of the fields is empty.
// It's not a good practice to hit the DB with undefined values.
}
let personDetails = {
"name": name,
"contact": contact
};
const person = new Contact(personDetails);
Regarding the unique validation either you can use the unique-validator plugin as suggested by Mohammad Yaser Ahmadi or you can make a DB call to check if the name and number are unique and then hit the save method if that's is feasible for your database.
If you want both the fields name and number to be combined unique you can create Compound Index as follows:
contactSchema.index({ name: 1, number: 1 }, { unique: true });
You can read more on Compound Indexes here: https://docs.mongodb.com/manual/core/index-compound/

Access other fields in getter

I'm trying to make it that every time I get subtotal, it adds 2 other fields, productTotal, and tax.
Here's my code:
const cost = new mongoose.Schema({
payment: {
productTotal: Number,
tax: Number,
subtotal: Number, // (productTotal + tax)
}
});
const Cost = mongoose.model('Cost', cost);
How can I add 2 feilds from the same schema when getting a different field?
You can achieve that in mongoose by creating a virtual field for subtotal. The mongoose reference documents this pretty well here: https://mongoosejs.com/docs/tutorials/virtuals.html.
EDIT:
The code snippet below shows how you can define a cost schema that has a subtotal virtual on the payment subdocument:
const PaymentSchema = new Schema({
productTotal: { type: Number, default: 0 },
tax: { type: Number, default: 0 },
});
PaymentSchema.virtual('subtotal').get(function () { return this.productTotal + this.tax; });
const CostSchema = new Schema({
payment: PaymentSchema,
});
From the above snippet you can get the subtotal from a Cost document instance via cost.payment.subtotal.

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 });

mongoose default value equal to other value

For my project i've created an userSchema which simplified looks like the following:
var userSchema = new Schema({
_id: String,
screenname: {type: String, required: false, default: "equal _id"},
});
The user has an _id that is a string which also is his username.
Everything works so far until i tried to add an extra field screenname. What i want is when the user creates an account, his screenname equals the value of _id. Later he can adjust it but by default it should equal the value of _id. i've also tried :
screenname: {type: String, required: false, default: _id},
But than ofcourse _id is not defined.
How should i set the default value to equal another value ?
use the pre middleware explained here
userSchema.pre('save', function (next) {
this.screenname = this.get('_id'); // considering _id is input by client
next();
});
You can pass a function to default, following is a schema field excerpt:
username: {
type: String,
required: true,
// fix for missing usernames causing validation fail
default: function() {
const _t = this as any; // tslint:disable-line
return _t.name || _t.subEmail;
}
},

Resources