///
import DI, { DDNames } from '@gongt/ts-stl-library/DI';
import { ObjectId } from 'mongodb';
import { ModelUpdateOptions, Schema, SchemaOptions, } from 'mongoose';
import { inspect, InspectOptions } from 'util';
import { BaseDocument, UpdateResult } from './base.type';
import { ConnectionRegistry, databaseInitComplete, di, registerModel, testDatabaseMuted } from './connect';
import { DataDocument } from './method.type';
import { MongodbOperationImplements } from './mongodb.interface';
export function autoDetect(target: DataModel, propertyKey: 'collectionName'): void {
const modelName = target.constructor.name.replace(/Model$/, '');
Object.assign(target, {
[propertyKey]: modelName,
});
}
export interface DataModelArgs {
connectionName: string;
}
/**
* @singleton : every class name is a singleton, multiple new will return same instance.
*/
export abstract class DataModel extends MongodbOperationImplements {
private readonly _realConnectionName: string;
protected displayTitle: string;
protected readonly schemaOptions: Partial;
constructor({connectionName}: Partial = {}) {
super();
this._realConnectionName = connectionName;
this.displayTitle = `<${this.modelName}@${connectionName}>`;
if (!testDatabaseMuted) {
if (databaseInitComplete) {
this.__init_database();
} else {
registerModel(this.modelName, () => {
if (!testDatabaseMuted) {
this.__init_database();
}
return false;
});
}
}
if (this.log.data.enabled) {
this.wrapDebug('insert');
this.wrapDebug('remove');
}
this.initialization();
this.log.info('prepared: [db=%s] [table=%s]', this.connectionName, this.collectionName);
}
/** @internal */
public toString() {
return `[BaseMongo::DataModel ${this.displayTitle}]`;
}
public async getInstance(idObj: ObjectId|string|DataDocument): Promise> {
if (typeof idObj === 'string' || idObj instanceof ObjectId) {
return await this.getById(idObj);
} else {
return idObj;
}
}
public upsert(conditions: Object, doc: Object, options: ModelUpdateOptions = {}): Promise {
return this.model.update(conditions, doc, {
multi: false,
setDefaultsOnInsert: true,
...options,
upsert: true,
}).exec();
}
/** @internal */
public [inspect.custom](depth: number, options: InspectOptions) {
const padding = ' '.repeat(2 + depth * 2);
return `{
${padding}BaseMongo::DataModel${this.displayTitle} table=${this.realConnectionName} }
`;
}
/** @internal */
private __init_database() {
const modelName = this.modelName;
const connectionName = this.realConnectionName;
const database: ConnectionRegistry = di.get(connectionName, null);
if (!database) {
throw new Error(`can not create ${modelName} instance: no connection is named ${connectionName}.`);
}
this._namespace = database.db.databaseName + '.' + this.collectionName;
if (database.rr.has(modelName)) {
this.model = database.rr.get(modelName);
return;
}
this.log.silly('init');
const schema = new Schema(this.schema, {
...(this.schemaOptions || {}),
collection: this.collectionName,
minimize: false,
typeKey: 'type',
retainKeyOrder: true,
timestamps: {
createdAt: 'createdAt',
updatedAt: 'updatedAt',
},
});
const commonHook = DI.get(DDNames.mongodbSchemaHook, null);
if (commonHook) {
commonHook(schema);
}
this.schemaHook(schema);
this.model = database.model>(modelName, schema);
database.rr.set(modelName, this.model);
}
protected create(): DataDocument {
const item = new this.model;
item._id = new ObjectId;
return item;
}
/** only for overwrite */
protected initialization() {
}
protected insert(content: IDocType) {
const item = new this.model(content);
item._id = new ObjectId();
return item.save();
}
protected remove(doc: any): Promise {
return this.model.remove(doc).exec();
}
/**
* only for overwrite, no content
* @param {"mongoose".Schema} schemaObject
*/
protected schemaHook(schemaObject: Schema) {
}
private _namespace: string;
get namespace() {
if (this._namespace) {
return this._namespace;
}
throw new Error('please wait database to connect, before using any method.');
}
private get realConnectionName() {
const connectionName = this.connectionName || this._realConnectionName;
if (!connectionName) {
throw new Error(`can not create ${this.modelName} instance: no connection name in class declaration, and no arguments.`);
}
return connectionName;
}
}