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.
1@Database.Input(() => cnst.DroneInput)
2export 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?
1@Database.Document(() => cnst.Drone)
2export class Drone extends Document(cnst.Drone) {
3 activate() {
4   this.status = 'active';
5   return this;
6 }
7 offline() {
8   this.status = 'offline';
9   return this;
10 }
11}
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

1await drone.refresh();
set

set the data of the document

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

save the document to the database (create or update)

1await 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

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

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

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

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

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

create the document with the data

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

update the document with the id and the data

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

soft-delete the document with the id

1const 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 }

1const 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

1const 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 }

1const 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

1const 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

1const 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

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

return true if the document exists, false if not found

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

return the number of documents that match the query condition

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

return the ModelInsight of the documents that match the query condition

1const 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

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

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

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

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

1const 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

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

sample the documents that match the query condition

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

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

1const 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

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

dataloader that loads multiple documents by a specific field value

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

dataloader that loads a document by a query condition

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

predefined dataloader

In addition, the this.modelLoader is declared by default with id as the query key.
1const drone = await this.droneLoader.load("ObjectId"); // db.Drone | null
2const 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.

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

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