UNPKG

@warlock.js/cascade

Version:

ORM for managing databases

216 lines (215 loc) 7.38 kB
import {areEqual}from'@mongez/reinforcements';import {getDatabaseConfig}from'../config.js';class ModelSync { model; columns; embedMethod; /** * What do do when model is deleted */ whenDelete = getDatabaseConfig("model")?.cascadeOnDelete || "unset"; /** * Embed on create */ embedOnCreate = ""; /** * Sync mode */ syncMode = "single"; /** * Query runner using `when` method */ queryRunner; /** * Define when should the synced model starts to update when any of the given columns is updated in the original model */ _updateWhenChange; /** * Constructor */ constructor(model, columns, embedMethod = "embedData") { this.model = model; this.columns = columns; this.embedMethod = embedMethod; // } /** * Define when should the synced model starts to update when any of the given columns is updated in the original model */ updateWhenChange(columns) { this._updateWhenChange = Array.isArray(columns) ? columns : [columns]; return this; } /** * Set query runner */ where(queryRunner) { this.queryRunner = queryRunner; return this; } /** * Unset on delete */ unsetOnDelete() { this.whenDelete = "unset"; return this; } /** * Ignore on delete */ ignoreOnDelete() { this.whenDelete = "ignore"; return this; } /** * Remove all matched documents on delete to be fully deleted */ removeOnDelete() { this.whenDelete = "remove"; return this; } /** * Embed on create to injected model one the original model is created */ embedOnCreateFrom(column) { this.embedOnCreate = column; return this; } /** * Mark as many sync */ syncMany() { this.syncMode = "many"; return this; } /** * Start syncing the model */ async sync(model, saveMode, oldModel) { if (saveMode === "update") { return this.syncUpdate(model, oldModel); } if (!this.embedOnCreate) return; const columns = Array.isArray(this.columns) ? this.columns : [this.columns]; const syncedModel = await this.model.first({ id: model.get(this.embedOnCreate + ".id"), }); if (!syncedModel) return; try { const modelData = typeof model[this.embedMethod] !== "undefined" ? model[this.embedMethod] : model.data; for (const column of columns) { if (this.syncMode === "single") { syncedModel.set(column, modelData); } else { syncedModel.associate(column, modelData); } } await syncedModel.save(); } catch (error) { console.log("Error in Sync", error.message); console.log(model); throw error; } } /** * Sync update */ async syncUpdate(model, oldModel) { if (this._updateWhenChange && oldModel) { // now check if any of the columns has changed // if all of them are the same, then we don't need to update if (this._updateWhenChange.every(column => areEqual(model.get(column), oldModel.get(column)))) { return; } } const columns = Array.isArray(this.columns) ? this.columns : [this.columns]; const whereOptions = {}; for (const column of columns) { whereOptions[column + ".id"] = model.id; } const query = this.model.aggregate().orWhere(whereOptions); if (this.queryRunner) { this.queryRunner(query); } const models = await query.get(); try { const modelData = typeof model[this.embedMethod] !== "undefined" ? model[this.embedMethod] : model.embeddedData; for (const updatingModel of models) { for (const column of columns) { if (this.syncMode === "single") { updatingModel.set(column, modelData); } else { if (column.includes(".")) { // if column includes dot, then it's a nested column // so we need to get the top document key as it should be an array. const [topKey, nestedKey] = column.split("."); const documentsList = updatingModel.get(topKey) || []; // as we're updating, so there should be at least one document if (documentsList?.length === 0) continue; // now we need to find the document that has the same id as the model we're updating const documentIndex = documentsList.findIndex((document) => document[nestedKey]?.id === model.get("id")); // if document is not found, then we don't need to update if (documentIndex === -1) continue; // now we need to update the document documentsList[documentIndex][nestedKey] = modelData; // and finally set the updated documents list updatingModel.set(topKey, documentsList); } else { // otherwise, it is a direct column update so we can just set it updatingModel.reassociate(column, modelData); } } } // disable casting so we can save the data as it is await updatingModel.save(undefined, { cast: false, }); } } catch (error) { console.log("Error in Sync", error.message); console.log(model); throw error; } } /** * Sync model destruction */ async syncDestruction(model) { const columns = Array.isArray(this.columns) ? this.columns : [this.columns]; const query = {}; for (const column of columns) { query[column + ".id"] = model.get("id"); } const models = await this.model .aggregate() .orWhere(query) .get(); for (const currentModel of models) { if (this.whenDelete === "unset") { for (const column of columns) { if (this.syncMode === "single") { currentModel.unset(column); } else { currentModel.disassociate(column, model); } } await currentModel.save(); } else if (this.whenDelete === "remove") { await currentModel.destroy(); } } } }export{ModelSync};//# sourceMappingURL=ModelSync.js.map