Data required to create a schema
Schema completed when data is created
Lightweight schema and class-based form used when multiple data is queried
Class-based complete form of data
Statistical data that can be extracted when data is queried
Statistical data extracted periodically when data is monitored
Data that defines the method of querying and the query statement in advance
Sort key-sort value used when data is sorted
Importance of Schema Structure
@Model.Input('DroneInput')
export class DroneInput {
@Field.Prop(() => String) // String, Int, Boolean, Float, JSON...
name: string;
@Field.Prop(() => String, { default: 'ws://10.10.150.10:9091' })
wsUri: string;
}
new this.Drone({'{ name: "myDrone", wsUri: "ws://10.10.150.10:9091" }'});
this.createDrone({'{ name: "myDrone", wsUri: "ws://10.10.150.10:9091" }'});
fetch.createDrone({'{ name: "myDrone", wsUri: "ws://10.10.150.10:9091" }'});
fetch.updateDrone("droneId",{'{ name: "myDrone", wsUri: "ws://10.10.150.10:9091" }'});
// Template.ts
st.do.setNameOnDrone("myDrone"); // set state of form for create or update
const droneForm = st.use.droneForm(); // droneForm.name
// Util.ts
const drone = st.use.drone(); // drone.name
export const droneStatuses = ['active', 'offline', 'inactive'] as const;
export type DroneStatus = (typeof droneStatuses)[number];
@Model.Object('DroneObject')
export class DroneObject extends BaseModel(DroneInput) {
@Field.Prop(() => String, { enum: droneStatuses, default: 'offline' })
status: DroneStatus;
}
const drone = await this.Drone.pickById("droneId"); // drone.status
const drone = await this.getDrone("droneId"); // drone.status
const drone = await fetch.drone("droneId"); // drone.status
const drone = st.use.drone(); // drone.status
Performance Optimization Benefits
@Model.Light('LightDrone')
export class LightDrone extends Light(DroneObject, [
'name',
'status',
] as const) {
isConnected() {
return this.status !== 'offline';
}
}
const droneList = await fetch.droneList({'{ status: "active" }'});
// drone.name is possible, but drone.wsUri is not possible
const droneMap = st.use.droneMap(); // Map<string, LightDrone>
@Model.Full('Drone')
export class Drone extends Full(DroneObject, LightDrone) {
static isDronesAllConnected(droneList: LightDrone[]) {
return droneList.every((drone) => drone.isConnected());
}
isAvailable() {
return this.isConnected() && this.wsUri.startsWith("ws://");
}
}
const drone = await fetch.drone("droneId"); // drone.isAvailable()
const droneMap = st.use.droneMap(); // Map<string, LightDrone>
const droneList = [...droneMap.values()];
const isAllConnected = cnst.Drone.isDroneAllConnected(droneList);
@Model.Insight('DroneInsight')
export class DroneInsight {
@Field.Prop(() => Int, {"{ default: 0, accumulate: { $sum: 1 } }"})
count: number;
}
const droneInsight = await this.insight({"{ ...query }"}); // droneInsight.count
const droneInsight = await fetch.droneInsight({"{ ...query }"}); // droneInsight.count
const drone = st.use.droneInsight(); // droneInsight.count
@Model.Summary('DroneSummary')
export class DroneSummary {
@Field.Prop(() => Int, {"{ min: 0, default: 0, query: { status: { $ne: 'inactive' } } }"})
totalDrone: number;
}
const summary = await this.getActiveSummary(); // summary.totalDrone
const summary = await fetch.getActiveSummary(); // summary.totalDrone
const summary = st.use.summary(); // summary.totalDrone
export const droneQuery = {"{
...baseQueries,
byName: (name: string) => ({"{ name }"}),
}
const drone = await this.findByName("myDrone"); // db.Drone | null
const drone = await this.findIdByName("myDrone"); // string | null
const drone = await this.pickByName("myDrone"); // db.Drone
const drone = await this.pickIdByName("myDrone"); // string
const drones = await this.listByName("myDrone"); // db.Drone[]
const droneIds = await this.listByName("myDrone"); // string[]
const droneCount = await this.countByName("myDrone"); // number
const droneExists = await this.existsByName("myDrone"); // string | null
const droneInsight = await this.insightByName("myDrone"); // db.DroneInsight
const drone = await this.findByName("myDrone"); // db.Drone | null
const drone = await this.findIdByName("myDrone"); // string | null
const drone = await this.pickByName("myDrone"); // db.Drone
const drone = await this.pickIdByName("myDrone"); // string
const drones = await this.listByName("myDrone"); // db.Drone[]
const droneIds = await this.listByName("myDrone"); // string[]
const droneCount = await this.countByName("myDrone"); // number
const droneExists = await this.existsByName("myDrone"); // string | null
const droneInsight = await this.insightByName("myDrone"); // db.DroneInsight
export const droneSort = {"{
...baseSorts,
alphabetical: {"{ name: 1 }"},
}
const drone = await this.findByName("myDrone", { sort: "alphabetical" });
const drone = await this.findIdByName("myDrone", { sort: "alphabetical" });
const drone = await this.pickByName("myDrone", { sort: "alphabetical" });
const drone = await this.pickIdByName("myDrone", { sort: "alphabetical" });
const drones = await this.listByName("myDrone", { sort: "alphabetical" });
const droneIds = await this.listByName("myDrone", { sort: "alphabetical" });
const drone = await this.findByName("myDrone", { sort: "alphabetical" });
const drone = await this.findIdByName("myDrone", { sort: "alphabetical" });
const drone = await this.pickByName("myDrone", { sort: "alphabetical" });
const drone = await this.pickIdByName("myDrone", { sort: "alphabetical" });
const drones = await this.listByName("myDrone", { sort: "alphabetical" });
const droneIds = await this.listByName("myDrone", { sort: "alphabetical" });
@Model.Scalar('DronePhysicalState')
export class DronePhysicalState {
@Field.Prop(() => [Float], { default: [0, 0, 0] })
rpy: [number, number, number];
@Field.Prop(() => [Float], { default: [0, 0, 0] })
position: [number, number, number];
@Field.Prop(() => [Float], { default: [0, 0, 0] })
velocity: [number, number, number];
}
import { DronePhysicalState } from '../_lfg/lfg.constant';
// ... //
@Model.Object('DroneObject')
export class DroneObject extends BaseModel(DroneInput) {
@Field.Prop(() => DronePhysicalState)
physicalState: DronePhysicalState;
}
@Model.Input('MissionInput')
export class MissionInput {
@Field.Prop(() => ID, {"{ ref: 'drone' }"})
drone: string;
}
import { LightMission } from '../mission/mission.constant';
// ... //
@Model.Object('DroneObject')
export class DroneObject extends BaseModel(DroneInput) {
@Field.Prop(() => LightMission, { nullable: true })
mission: LightMission | null;
}
import {"{ LightMission }"} from "../cnst_";
import {"{ LightMission }"} from "../mission/mission.constant";
Option | Type | Default | Description | Example |
---|---|---|---|---|
nullable | boolean | false | Field value required, if nullable: true, data can be saved even if it is not present |
|
ref | string | - | Option for reference ID field, can specify reference collection name(e.g. drone) |
|
refPath | string | - | Option for reference ID field, if reference collection is variable, can specify reference collection name by referring to other field values |
|
default | any | - | Default value set when no value is entered, must be set for all fields except Model.Input |
|
type | email | password | url | - | Preset setting, default validation or example value is set |
|
immutable | boolean | false | Option that cannot be changed after document creation, after creation, changes to the field are not possible |
|
min | number | - | If the field's model is Int or Float, the minimum value can be limited in the save step |
|
max | number | - | If the field's model is Int or Float, the maximum value can be limited in the save step |
|
enum | any[] | - | If the field is enum, the const array can be specified to limit values other than the save step |
|
minlength | number | - | If the field's model is String, the minimum length of the string can be limited |
|
maxlength | number | - | If the field's model is String, the maximum length of the string can be limited |
|
query | object | - | Used in Model.Summary fields, the value obtained by performing the query is saved as the field's value |
|
accumulate | object | - | Used in Model.Insight fields, the value calculated in the aggregation step is saved as the field's value |
|
example | any | - | Can set the value that appears as an example when creating API Docs |
|
of | any | - | If the field's model is Map, the key value of the Map is string, and the model specified by of is set as the value type of the schema |
|
validate | any(value, model) => boolean | - | Can check validation in the save step.(unstable) |
|
Field value required, if nullable: true, data can be saved even if it is not present
@Field.Prop(()=> String, { default: "untitled" }) title: string;
Option for reference ID field, can specify reference collection name(e.g. drone)
@Field.Prop(()=> ID, { ref: "drone" }) drone: string;
Option for reference ID field, if reference collection is variable, can specify reference collection name by referring to other field values
@Field.Prop(()=> ID, { refPath: "rootType" }) root: string; @Field.Prop(()=> String, { enum: ["drone", "mission"] }) rootType: string;
Default value set when no value is entered, must be set for all fields except Model.Input
@Field.Prop(()=> String, { default: "untitled" }) title: string;
Preset setting, default validation or example value is set
@Field.Prop(()=> String, { type: "email" }) email: string;
Option that cannot be changed after document creation, after creation, changes to the field are not possible
@Field.Prop(()=> ID, { immutable: true }) creator: string;
If the field's model is Int or Float, the minimum value can be limited in the save step
@Field.Prop(()=> Int, { min: 0 }) progress: number;
If the field's model is Int or Float, the maximum value can be limited in the save step
@Field.Prop(()=> Float, { min: 0, max: 1 }) ratio: number;
If the field is enum, the const array can be specified to limit values other than the save step
@Field.Prop(()=> String, { enum: ["active", "inactive"] }) status: "active" | "inactive";
If the field's model is String, the minimum length of the string can be limited
@Field.Prop(()=> String, { minlength: 2 }) name: string;
If the field's model is String, the maximum length of the string can be limited
@Field.Prop(()=> String, { maxlength: 30 }) title: string;
Used in Model.Summary fields, the value obtained by performing the query is saved as the field's value
@Field.Prop(() => Int, { min: 0, default: 0, query: { status: { $ne: 'inactive' } } }) totalDrone: number;
Used in Model.Insight fields, the value calculated in the aggregation step is saved as the field's value
@Field.Prop(() => Int, { default: 0, accumulate: { $sum: 1 } }) count: number;
Can set the value that appears as an example when creating API Docs
@Field.Prop(()=> String, { example: "contact@akanjs.com" }) email: string;
If the field's model is Map, the key value of the Map is string, and the model specified by of is set as the value type of the schema
@Field.Prop(()=> Map, { of: Date }) readAts: Map<string, Dayjs>;
Can check validation in the save step.(unstable)
@Field.Prop(()=> String, { validate: (value)=> value.includes("@") }) email: string;