diff --git a/modules/database/src/adapters/mongoose-adapter/MongooseSchema.ts b/modules/database/src/adapters/mongoose-adapter/MongooseSchema.ts index 96402377e..c6592d97e 100644 --- a/modules/database/src/adapters/mongoose-adapter/MongooseSchema.ts +++ b/modules/database/src/adapters/mongoose-adapter/MongooseSchema.ts @@ -36,8 +36,12 @@ export class MongooseSchema extends SchemaAdapter> { readonly originalSchema: any, readonly adapter: MongooseAdapter, isView: boolean = false, + readonly viewQuery?: Indexable, ) { super(grpcSdk, adapter, isView); + if (viewQuery) { + this.viewQuery = viewQuery; + } if (!isNil(schema.collectionName)) { (schema.modelOptions as _ConduitSchemaOptions).collection = schema.collectionName; // @dirty-type-cast } else { diff --git a/modules/database/src/adapters/mongoose-adapter/index.ts b/modules/database/src/adapters/mongoose-adapter/index.ts index d88c3f58e..68a0c189e 100644 --- a/modules/database/src/adapters/mongoose-adapter/index.ts +++ b/modules/database/src/adapters/mongoose-adapter/index.ts @@ -19,7 +19,7 @@ import { ConduitDatabaseSchema, introspectedSchemaCmsOptionsDefaults, } from '../../interfaces/index.js'; -import { isNil } from 'lodash-es'; +import { isNil, isEqual } from 'lodash-es'; // @ts-ignore import * as parseSchema from 'mongodb-schema'; @@ -75,34 +75,37 @@ export class MongooseAdapter extends DatabaseAdapter { if (!this.models[modelName]) { throw new GrpcError(status.NOT_FOUND, `Model ${modelName} not found`); } - if (this.views[viewName]) { + const existingView = this.views[viewName]; + const isQueryEqual = isEqual(existingView?.viewQuery, query); + if (existingView && isQueryEqual) { return; } + const model = this.models[modelName]; const newSchema: Partial = Object.assign({}, model.schema); //@ts-ignore newSchema.name = viewName; //@ts-ignore newSchema.collectionName = viewName; - try { - const viewModel = new MongooseSchema( - this.grpcSdk, - this.mongoose, - newSchema as ConduitSchema, - model.originalSchema, - this, - true, - ); - this.views[viewName] = viewModel; - await viewModel.model.createCollection({ - viewOn: model.originalSchema.collectionName, - pipeline: JSON.parse(query.mongoQuery), - }); - } catch (e: any) { - if (!e.message.includes('Cannot overwrite')) { - throw e; - } + + if (existingView && !isQueryEqual) { + await this.deleteView(viewName); } + const viewModel = new MongooseSchema( + this.grpcSdk, + this.mongoose, + newSchema as ConduitSchema, + model.originalSchema, + this, + true, + query, + ); + await viewModel.model.createCollection({ + viewOn: model.originalSchema.collectionName, + pipeline: JSON.parse(query.mongoQuery), + }); + this.views[viewName] = viewModel; + const foundView = await this.models['Views'].findOne({ name: viewName }); if (isNil(foundView)) { await this.models['Views'].create({ @@ -131,6 +134,7 @@ export class MongooseAdapter extends DatabaseAdapter { } await this.models['Views'].deleteOne({ name: viewName }); delete this.views[viewName]; + delete this.mongoose.models[viewName]; } async introspectDatabase(): Promise { diff --git a/modules/database/src/adapters/sequelize-adapter/SequelizeSchema.ts b/modules/database/src/adapters/sequelize-adapter/SequelizeSchema.ts index 48814bb7d..1ea1b3e04 100644 --- a/modules/database/src/adapters/sequelize-adapter/SequelizeSchema.ts +++ b/modules/database/src/adapters/sequelize-adapter/SequelizeSchema.ts @@ -59,8 +59,12 @@ export class SequelizeSchema extends SchemaAdapter> { [key: string]: { parentKey: string; childKey: string }; }, readonly isView: boolean = false, + readonly viewQuery?: Indexable, ) { super(grpcSdk, adapter, isView); + if (viewQuery) { + this.viewQuery = viewQuery; + } this.excludedFields = []; this.idField = sqlTypesProcess( sequelize, diff --git a/modules/database/src/adapters/sequelize-adapter/index.ts b/modules/database/src/adapters/sequelize-adapter/index.ts index ec0a77332..caa0f6669 100644 --- a/modules/database/src/adapters/sequelize-adapter/index.ts +++ b/modules/database/src/adapters/sequelize-adapter/index.ts @@ -28,7 +28,7 @@ import { } from '../../interfaces/index.js'; import { sqlSchemaConverter } from './sql-adapter/SqlSchemaConverter.js'; import { pgSchemaConverter } from './postgres-adapter/PgSchemaConverter.js'; -import { isNil } from 'lodash-es'; +import { isEqual, isNil } from 'lodash-es'; const sqlSchemaName = process.env.SQL_SCHEMA ?? 'public'; @@ -51,10 +51,20 @@ export abstract class SequelizeAdapter extends DatabaseAdapter if (!this.models[modelName]) { throw new GrpcError(status.NOT_FOUND, `Model ${modelName} not found`); } + const existingView = this.views[viewName]; + const isQueryEqual = isEqual(existingView?.viewQuery, query); + if (existingView && isQueryEqual) { + return; + } + const model = this.models[modelName]; const newSchema = JSON.parse(JSON.stringify(model.schema)); newSchema.name = viewName; newSchema.collectionName = viewName; + + if (existingView && !isQueryEqual) { + await this.deleteView(viewName); + } const viewModel = new SequelizeSchema( this.grpcSdk, this.sequelize, @@ -64,6 +74,7 @@ export abstract class SequelizeAdapter extends DatabaseAdapter model.extractedRelations, model.objectPaths, true, + query, ); const dialect = this.sequelize.getDialect(); const queryViewName = dialect === 'postgres' ? `"${viewName}"` : viewName;