-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
170 lines (147 loc) · 5.86 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/* eslint-disable no-console */
import { setCustomMongooseOptions } from '@kikiutils/mongoose/options';
import s from '@kikiutils/mongoose/schema-builders';
import type {} from '@kikiutils/mongoose/types';
import type {} from '@kikiutils/mongoose/types/data';
import { buildMongooseModel } from '@kikiutils/mongoose/utils';
import { Schema } from 'mongoose';
import type {
ProjectionType,
QueryOptions,
Types,
} from 'mongoose';
import type { Except } from 'type-fest';
/**
* Set mongodb uri env.
*
* This is just an automatic default if you don't copy .env.example into .env,
* the actual project shouldn't have this line
* (unless you want to handle it the same way as the default).
*/
process.env.MONGODB_URI ||= 'mongodb://127.0.0.1:27017/kikiutils-mongoose-example?directConnection=true';
/**
* Set custom mongoose options.
*
* If you want to unify the processing of schema before model build,
* you can use this method to set it.
*
* Please make sure to set it before using buildMongooseModel,
* as it will not affect models that have already been built before setting it.
*/
setCustomMongooseOptions('beforeModelBuild', (_schema) => {
// console.log('building model with schema: ', schema);
});
/*
* Define data interface.
*
* This is the type of data on the front-end or elsewhere,
* usually populated and after calling toJSON,
* so the date field type is a string and the password is optional.
*
* If you don't need to distinguish between the two interfaces,
* you can use BaseMongooseDocType<{}> like the User interface below.
*
* BaseMongooseModelData accepts two boolean type parameters,
* the first controls the existence of the createdAt field and
* the second controls the updatedAt field.
*/
interface UserData extends BaseMongooseModelData {
account: string;
// To avoid precision issues use strings (Decimal128)
balance: string;
email?: string;
enabled: boolean;
// Date Fields toJSON followed by String
loginAt?: string;
// Passwords will not be exported in toJSON (the schema uses the private setting)
password?: string;
}
/*
* Define document and model interfaces and types.
*
* The second type parameter of BaseMongooseDocType
* controls the existence of the createdAt field
* and the third controls the updatedAt field.
*/
interface User extends BaseMongooseDocType<Except<UserData, 'loginAt'>> {
// Correctly set the type in the document state
loginAt?: Date;
}
interface UserMethodsAndOverrides {
// Correctly set the type in the document state
password: string;
// Document methods
verifyPassword: (password: string) => boolean;
}
interface UserModel extends BaseMongoosePaginateModel<User, UserMethodsAndOverrides> {
// Model static methods
findByAccount: (account: string, projection?: null | ProjectionType<User>, options?: null | QueryOptions<User>) => MongooseFindOneReturnType<User, UserDocument, object, UserMethodsAndOverrides>;
}
export type UserDocument = MongooseHydratedDocument<User, UserMethodsAndOverrides>;
// Define schema
const userSchema = new Schema<User, UserModel, UserMethodsAndOverrides>({
account: s.string().maxlength(16).trim.unique.required,
// @ts-expect-error Ignore this error.
// Use setRoundAndToFixedSetter to round up on save and setToStringGetter to convert to string on get
balance: s.decimal128().setRoundAndToFixedSetter().setToStringGetter.required,
email: s.string().lowercase.trim.nonRequired,
enabled: s.boolean().default(false).required,
password: s.string().private.required,
});
// Set methods
userSchema.method<UserDocument>('verifyPassword', function (password: string) {
return password === this.password;
});
// Set static methods
userSchema.static('findByAccount', function (account: string, projection?: null | ProjectionType<User>, options?: null | QueryOptions<User>) {
return this.findOne({ account }, projection, options);
});
// Build model
const UserModel = buildMongooseModel<User, UserModel, UserMethodsAndOverrides>('user.users', 'User', userSchema);
// Create document
console.log('creating user');
const user = await UserModel.create({
account: Array.from({ length: 8 }, () => String.fromCharCode((Math.random() > 0.5 ? 97 : 65) + Math.floor(Math.random() * 26))).join(''),
balance: '1000.501',
email: '[email protected]',
password: 'test-password',
});
console.log('created user: ', user);
console.log('created user (with toJSON): ', user.toJSON());
// Modify balance
console.log('increasing user balance');
await user.updateOne({ $inc: { balance: '100.105416' } });
console.log('increased user balance: ', (await UserModel.findById(user._id))?.balance);
// Verify password
console.log('verifying user password');
console.log('verified user password: ', user.verifyPassword('test-password'));
// Define user log data interface
export interface UserLogData extends BaseMongooseModelData<true, false> {
content: string;
type: number;
user: Partial<UserData>;
}
// Define document and model interfaces and types
interface UserLog extends BaseMongooseDocType<Except<UserLogData, 'user'>, true, false> {
user: Types.Decimal128;
}
export type UserLogDocument = MongooseHydratedDocument<UserLog>;
export type UserLogModel = BaseMongoosePaginateModel<UserLog>;
// Define schema
const userLogSchema = new Schema<UserLog, UserLogModel>({
content: s.string().trim.required,
type: s.number().required,
user: s.ref('User').required,
});
// Build model
const UserLogModel = buildMongooseModel<UserLog, UserLogModel>('user.logs', 'UserLog', userLogSchema);
// Create document
console.log('creating user log');
const userLog = await UserLogModel.create({
content: 'test content',
type: 1,
user: user._id,
});
console.log('created user log: ', userLog);
console.log('created user log (with toJSON): ', userLog.toJSON());
console.log('user log with populated user', await userLog.populate('user'));