model.document.ts

Define the rules for storing and querying data in MongoDB and Redis databases. You can write query and update statements and apply them. You can handle Document method, Model statics, Middleware, indexing, etc. in the Mongoose library.

1. @Database.Input

database input is only to convert the ModelInput from model.constant and save the data structure. There is no additional option to set.
@Database.Input(() => cnst.DroneInput)
export class DroneInput extends Input(cnst.DroneInput) {}

2. @Database.Document

database document is a class that implements the Document of MongoDB. The methods declared in this class have the same functionality as the mongoose document method, and you can declare convenience functions that can be used when handling a single data on the backend.
2.1. How to use method?
@Database.Document(() => cnst.Drone)
export class Drone extends Document(cnst.Drone) {
 activate() {
   this.status = 'active';
   return this;
 }
 offline() {
   this.status = 'offline';
   return this;
 }
}
2.2. Predefined Document Methods
document has various convenience functions including data modification/saving, etc. including the schema in constant. You can use the Mongoose Document API by including the following methods.
refresh

refresh the data of the document from the database

await drone.refresh();
set

set the data of the document

drone.set({ name: "newName" });
save

save the document to the database (create or update)

await drone.save();

3. @Database.Model

database model is a class that implements the Model of MongoDB, Redis, Meilisearch databases. You can declare functions to manage the database of the domain.
3.1. Predefined methods
The functions of the Model have the following convenience functions for basic data lookup/modification/saving/deletion.
getModel(id: string)

load the document with the id, return null if not found

const drone = await this.getDrone("ObjectId");
loadModel(id: string)

load the document with the id, return null if not found

const drone = await this.loadDrone("ObjectId");
loadModelMany(ids: string[])

load the documents with the ids, return null if not found

const drones = await this.loadDroneMany(["ObjectId", "ObjectId"]);
createModel(data: db.ModelInput)

create the document with the data

const drone = await this.createDrone({ name: "myDrone" });
updateModel(id: string, data: db.ModelInput)

update the document with the id and the data

const drone = await this.updateDrone("ObjectId", { name: "newName" });
removeModel(id: string)

soft-delete the document with the id

const drone = await this.removeDrone("ObjectId"); // drone.status = "inactive"
3.2. Query based methods
In addition, for each query key declared in model.constant.ts, the following convenience functions are created and provided. This allows you to query and manage queries in a type-safe manner.
list<Query_KEY>(...queryArgs: QueryArgs, option?: ListQueryOption)

return the list of documents that match the query condition ListQueryOption = { skip?: number, limit?: number, sort?: Sort, sample?: boolean }

const drones = await this.listByName("myDrone"); // db.Drone[]
listIds<Query_KEY>(...queryArgs: QueryArgs, option?: ListQueryOption)

return the list of document ids that match the query condition

const droneIds = await this.listIdsByName("myDrone"); // string[]
find<Query_KEY>(...queryArgs: QueryArgs, option?: FindQueryOption)

return the document that matches the query condition, return null if not found FindQueryOption = { skip?: number, sort?: Sort, sample?: boolean }

const drone = await this.findByName("myDrone"); // db.Drone | null
findId<Query_KEY>(...queryArgs: QueryArgs, option?: FindQueryOption)

return the document id that matches the query condition, return null if not found

const droneId = await this.findIdByName("myDrone"); // string | null
pick<Query_KEY>(...queryArgs: QueryArgs, option?: FindQueryOption)

return the document that matches the query condition, throw error if not found

const drone = await this.pickByName("myDrone"); // db.Drone
pickId<Query_KEY>(...queryArgs: QueryArgs, option?: FindQueryOption)

return the document id that matches the query condition, throw error if not found

const droneId = await this.pickIdByName("myDrone"); // string
exists<Query_KEY>(...queryArgs: QueryArgs)

return true if the document exists, false if not found

const droneExists = await this.existsByName("myDrone"); // string | boolean
count<Query_KEY>(...queryArgs: QueryArgs)

return the number of documents that match the query condition

const droneCount = await this.countByName("myName"); // number
insight<Query_KEY>(...queryArgs: QueryArgs)

return the ModelInsight of the documents that match the query condition

const droneInsight = await this.insightByName("myDrone"); // cnst.DroneInsight
3.3. MongoDB Model this.Model
To use the Mongoose native Model in the database model, you need to access it through this.Model. The Model provides various convenience functions, and also provides the basic Mongoose Model API.
pickById

return the document with the id, throw error if not found

const drone = await this.Drone.pickById("ObjectId"); // db.Drone
pickOne

return the document that matches the query condition, throw error if not found

const drone = await this.Drone.pickOne({ name: "myDrone" }); // db.Drone
pickAndWrite

return the document with the id, update the data, throw error if not found

const drone = await this.Drone.pickAndWrite("ObjectId", { name: "newName" }); // db.Drone
pickOneAndWrite

return the document that matches the query condition, update the data, throw error if not found

const drone = await this.Drone.pickOneAndWrite({ name: "myDrone" }, { name: "newName" }); // db.Drone
addSummary

increment the value of the specific key in the summary model

await this.Drone.addSummary("active", 1);
moveSummary

increment/decrement the value of the specific key in the summary model

await this.Drone.moveSummary("active", "offline", 1);
subSummary

decrement the value of the specific key in the summary model

await this.Drone.subSummary("active", 1);
sample

sample the documents that match the query condition

const drones = await this.Drone.sample({ name: "myDrone" }, 5); // db.Drone[];
sampleOne

sample the document that matches the query condition, return null if not found

const drone = await this.Drone.sampleOne({ name: "myDrone" }); // db.Drone | null
3.4. Redis Cache this.modelCache
Not implemented yet
3.5. Meilisearch text search this.modelSearch
Not implemented yet
3.6. Loaders this.modelLoader
For performance-based data lookup, you can declare a dataloader. You can declare dataloader in three ways as shown below.
@Loader.ByField

dataloader that loads a single document by a specific field value

@Loader.ByField('name') droneNameLoader: Loader<string, Drone>;
async loadDroneByName(name: string) {
  const drone = await this.droneNameLoader.load("myDrone"); 
  return drone; // db.Drone | null
}
@Loader.ByArrayField

dataloader that loads multiple documents by a specific field value

@Loader.ByArrayField("name") droneNameArrayLoader: Loader<string, Drone[]>;
async loadDroneArrayByName(name: string) {
  const drones = await this.droneNameArrayLoader.load("myDrone");
  return drones; // db.Drone[] | null
}
@Loader.ByQuery

dataloader that loads a document by a query condition

@Loader.ByQuery(["name"]) droneNameQueryLoader: Loader<{ name: string }, Drone>;
async loadDroneWithQueryByName(name: string) {
  const drone = await this.droneNameQueryLoader.load({ name: "myDrone" });
  return drone; // db.Drone | null
}

predefined dataloader

In addition, the this.modelLoader is declared by default with id as the query key.
const drone = await this.droneLoader.load("ObjectId"); // db.Drone | null
const drones = await this.droneLoader.loadMany(["ObjectId", "ObjectId"]); // db.Drone[]

4. @Database.Middleware

In the Middleware, the Mongoose Middleware API is set up to allow you to use it to manage data consistently.
4.1. onSchema
You can manage data by using the following functions.

schema index

Query performance can be significantly improved through Schema Indexing in mongoDB.

pre save middleware

You can set the logic for data consistency before calling the document.save() function.

@Database.Middleware(() => cnst.Drone)
export class DroneMiddleware extends Middleware(DroneModel, Drone) {
 onSchema(schema: SchemaOf<DroneModel, Drone>) {
   schema.pre<Drone>("save", function (next) {
     const model = this.constructor as DroneModel["Drone"];
     if(drone.name === "myDrone") console.log("No more myDrone name");
     next();
   });
   schema.index({ name: 1, status: 1 });
 }
}

5. Tips

  • import type * as db from '../db'import pattern for type reference
  • Separate the roles of document and service: clearly separate data access and business logic
Released under the MIT License
Official Akan.js Consulting onAkansoft
Copyright © 2025 Akan.js. All rights reserved.
System managed bybassman