Install with npm:
npm i -S lemon-js
import { TypedModel, model, property } from 'lemon-js';
@model
export class Permission extends TypedModel {
@property public name: string;
}
@model
export class User extends TypedModel {
@property public age: number;
@property({ index: true }) public email: string;
// Use either ref or refer.
@property([{ refer: Permission }]) public permissions: [Permission];
@property({ default: false }) public isActive: boolean;
@property({ unique: true }) public name: string;
public get displayName() {
return `${ this.name } <${ this.email }>`;
}
public static findByEmail(email: string): Query<User> {
return this.findOne({ email });
}
}
@model
export class Post extends TypedModel {
@property public title: string;
@property public body: string;
@property({ default: 0 }) public readCount: number;
@property public creator: User;
public static findByTitle(title: string): Query<Post> {
return this.findOne({ title });
}
}
@model
export class TypedUser extends User {
@property({ default: 'user' }) public type: string;
}
const write = new Permission({
name: 'write'
});
await write.save();
const read = new Permission({
name: 'read'
});
await read.save();
const user = new User({
age: 20,
email: '[email protected]',
name: 'User 1',
permissions: [read._id, write._id]
});
await user.save();
const post = new Post({
body: 'Post body',
creator: user._id,
title: 'Post 1',
});
await post.save();
const post = await Post.findByTitle('Post 1').populate('creator').exec();
expect(post.title).to.be.equal('Post 1');
expect(post.creator.displayName).to.be.equal('User 1 <[email protected]>');
const user = await User.findByEmail('[email protected]').populate('permissions').exec();
expect(user.name).to.be.equal('User 1');
expect(user.permissions.length).to.be.equal(2);
expect(user.permissions[0].name).to.be.equal('read');
expect(user.permissions[1].name).to.be.equal('write');
export interface IColor {
r: number;
g: number;
b: number;
}
export interface ICar {
make: string;
model: string;
color: IColor;
users?: User[];
}
export interface IRoom {
color: IColor;
name: string;
owner: User;
windows: IWindow[];
computer: IComputer;
}
export interface IWindow {
color: IColor;
installer: User;
}
export interface IComputer {
color: IColor;
users: User[];
}
export interface IHouse {
name: string;
car: ICar;
rooms: IRoom[];
}
@model
export class House extends TypedModel implements IHouse {
@property name: string;
@property({
subdoc: true,
make: String,
model: String,
color: {
r: Number,
g: Number,
b: Number
},
users: [{ ref: User }]
})
car: ICar;
@property([{
subdoc: true,
name: String,
color: {
r: Number,
g: Number,
b: Number
},
owner: { refer: User },
windows: [{
// Nested subdoc
subdoc: true,
installer: { ref: User }
}],
computer: {
// Nested subdoc
subdoc: true,
users: [{ ref: User }]
}
}])
rooms: IRoom[];
}
const wife = new User({
age: 20,
email: '[email protected]',
name: 'Wife',
permissions: []
});
await wife.save();
const husband = new User({
age: 25,
email: '[email protected]',
name: 'Husband',
permissions: []
});
await husband.save();
const house = new House({
name: 'home',
car: {
make: 'BMW',
model: '550i',
color: {
r: 255,
g: 20,
b: 20
},
users: [wife._id, husband._id]
},
rooms: [{
name: 'bedroom 1',
color: {
r: 20,
g: 255,
b: 20
},
owner: wife._id
}, {
name: 'bedroom 2',
color: {
r: 20,
g: 20,
b: 255
},
owner: husband._id
}]
});
await house.save();
const house: House = await House.findOne({ name: 'home' }).populate('rooms.owner').exec() as House;
const house: House = await House.findOne({ name: 'home' }).populate('car.users').exec() as House;
@method({
pre: ['save'],
post: ['remove']
})
public preSavePostRemove() {
}
@model
export class ValidateTest extends TypedModel {
public static EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
@property({
validate: [ValidateTest.validateEmail1, 'Uh oh, "{PATH}" invalid 1.']
})
public email1: string;
@property({
validate: [{
validator: ValidateTest.validateEmail2_1,
message: 'Uh oh, "{PATH}" invalid 2-1.'
}, {
validator: ValidateTest.validateEmail2_2,
message: 'Uh oh, "{PATH}" invalid 2-2.'
}]
})
public email2: string;
@property({
validate: ValidateTest.validateEmail3
})
public email3: string;
@property({
validate: {
isAsync: true,
validator: ValidateTest.validateEmail4,
message: 'Uh oh, "{PATH}" invalid 4.'
}
})
public email4: string;
public static validateEmail1(value) {
// Object is mapped to object
return ValidateTest.EMAIL_REGEX.test(value);
}
public static validateEmail2_1(value) {
// Object is mapped to object
return value.length > 2;
}
public static validateEmail2_2(value) {
// Object is mapped to object
return ValidateTest.EMAIL_REGEX.test(value);
}
public static validateEmail3(value) {
// Object is mapped to object
return ValidateTest.EMAIL_REGEX.test(value);
}
public static validateEmail4(value, callback) {
// Object is mapped to object
// Support mongoose isAsync callback
let result = ValidateTest.EMAIL_REGEX.test(value);
callback(result, 'Overwriting error');
}
@method<ValidateTest>({
validate: ['email2', 'email3'],
message: 'Validating for both email2 and email3'
})
public validateEmail2AndEmail3(value, callback) {
let result = ValidateTest.EMAIL_REGEX.test(value);
callback(result, 'Overwriting error');
}
}
let user = await User.findOne({});
// Created date
let creationDate = user.createdAt;
// Updated date
let updateDate = user.updatedAt;
@model
export class House extends TypedModel implements IHouse {
@property({
hidden: true
})
name: string;
@property({
subdoc: true,
hidden: ['make'],
make: String,
model: String,
color: {
r: Number,
g: Number,
b: Number
},
users: [{ ref: User }]
})
car: ICar;
@property([{
subdoc: { autoIndex: false },
hidden: ['owner'],
name: String,
color: {
hidden: ['r'],
r: Number,
g: Number,
b: Number
},
owner: { refer: User },
windows: [{
hidden: true,
subdoc: true,
installer: { ref: User }
}],
computer: {
hidden: ['users'],
subdoc: true,
users: [{ ref: User }],
system: String,
color: {
r: Number,
g: Number,
b: Number
}
}
}])
rooms: IRoom[];
}
You can overwrite this when you run toJSON or toObject with the following option (default false).
const house: House = await House.findOne({ name: 'home' }).populate('rooms.computer').exec() as House;
const json = house.toJSON({
showHidden: true
})
Licensed under MIT.
Based on the work of @megahertz/mongoose-model