Sling Academy
Home/Node.js/How to Set Unsigned Integer Field in Mongoose

How to Set Unsigned Integer Field in Mongoose

Last updated: January 03, 2024

Overview

Mongoose is a robust Node.js library that provides a schema-based solution to model application data with MongoDB. Using unsigned integers can be crucial for some database schemas, as they allow storage of only non-negative numbers, thus ensuring data integrity. This tutorial will guide you through setting up unsigned integers in Mongoose schemas.

Understanding Mongoose Schemas

Before diving into unsigned integers, let’s revise how Mongoose schemas work. A schema in Mongoose is an outline of the structure of the document, including default values, validators, etc. Here is a basic example:

const mongoose = require('mongoose');
const { Schema } = mongoose;

const userSchema = new Schema({
  name: String,
  age: Number,
});

const User = mongoose.model('User', userSchema);

Setting Up Unsigned Integer

In Mongoose, there isn’t an explicit ‘unsigned’ type like in SQL-based databases. However, you can mimic the behavior using validators like so:

const userSchema = new Schema({
  positiveAge: {
    type: Number,
    min: [0, 'Age must be a positive number.']
  }
});

This will effectively create an unsigned integer field, as any negative number will fail validation.

Defining Custom Validators

For more control or complex validation logic, you can define custom validators. Here’s one for ensuring a number is an unsigned integer:

function isUnsignedInteger(value) {
  return Number.isInteger(value) && value ">= 0;
}

const productSchema = new Schema({
  stock: {
    type: Number,
    validate: [isUnsignedInteger, 'Stock must be an unsigned integer.']
  }
});

Handling Large Unsigned Integers

JavaScript’s Number type can only safely represent integers up to 2^53 – 1. If you need to handle larger unsigned integers, consider using the ‘bigint’ package and the following schema:

const bigint = require('bigint');

const highValueSchema = new Schema({
  highValue: {
    type: mongoose.Schema.Types.Decimal128,
    validate: {
      validator: function(v) {
        return bigint(v.toString()).ge(0);
      },
      message: 'highValue must be an unsigned integer.'
    }
  }
});

Note that ‘Decimal128’ is a BSON type that can handle big integers at the cost of some arithmetic convenience.

Advance Use Case: Schema Pre-hooks

Sometimes you might want to ensure an unsigned field also meets other criteria before saving. Pre-hooks allow you to run code prior to an event, in this case, a ‘save’ event:

productSchema.pre('save', function(next) {
  if (this.stock < 0) {
    next(new Error('Stock cannot be negative'));
  } else if (!Number.isInteger(this.stock)) {
    next(new Error('Stock must be an integer'));
  } else {
    next();
  }
});

Integrating Unsigned Integer Fields with Asynchronous Validators

If you have an asynchronous operation to perform as part of your validation, you’ll need to adapt your strategy. Here’s how to do it with async/await:

productSchema.path('stock').validate(async function(value) {
  if (!Number.isInteger(value)) {
    throw new Error('Stock must be an integer');
  }
  if (value < 0) {
    throw new Error('Stock cannot be negative');
  }
  // Add your asynchronous validations here
}, 'Invalid stock');

Conclusion

In this tutorial, we’ve explored how to work with unsigned integers in Mongoose, though they are not natively supported like in SQL databases. We covered validators, custom validation functions, handling large unsigned integers, pre-hooks, and async validators. Armed with this knowledge, you can now confidently use unsigned integers in your Mongoose schemas.

Next Article: Mongoose: Count Documents in a Collection (with examples)

Previous Article: Mongoose: Compare two dates in a query (before, after, between)

Series: Mongoose.js Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates