* Creates or updates the entity, based on whether it is already present in the database. * This method performs an `insert on conflict merge` query ensuring the database is in sync, returning a managed * entity instance. The method accepts either `entityName` together with the entity `data`, o
(
entityNameOrEntity: EntityName<Entity> | Entity,
data?: EntityData<Entity> | NoInfer<Entity>,
options: UpsertOptions<Entity, Fields> = {},
)
| 1216 | * If the entity is already present in current context, there won't be any queries - instead, the entity data will be assigned and an explicit `flush` will be required for those changes to be persisted. |
| 1217 | */ |
| 1218 | async upsert<Entity extends object, Fields extends string = any>( |
| 1219 | entityNameOrEntity: EntityName<Entity> | Entity, |
| 1220 | data?: EntityData<Entity> | NoInfer<Entity>, |
| 1221 | options: UpsertOptions<Entity, Fields> = {}, |
| 1222 | ): Promise<Entity> { |
| 1223 | if (options.disableIdentityMap ?? this.config.get('disableIdentityMap')) { |
| 1224 | const em = this.getContext(false); |
| 1225 | const fork = em.fork({ keepTransactionContext: true }); |
| 1226 | const ret = await fork.upsert(entityNameOrEntity, data, { ...options, disableIdentityMap: false }); |
| 1227 | fork.clear(); |
| 1228 | |
| 1229 | return ret; |
| 1230 | } |
| 1231 | |
| 1232 | const em = this.getContext(false); |
| 1233 | em.prepareOptions(options); |
| 1234 | |
| 1235 | let entityName: EntityName<Entity>; |
| 1236 | let where: FilterQuery<Entity>; |
| 1237 | let entity: Entity | null = null; |
| 1238 | |
| 1239 | if (data === undefined) { |
| 1240 | entityName = (entityNameOrEntity as Dictionary).constructor; |
| 1241 | data = entityNameOrEntity as Entity; |
| 1242 | } else { |
| 1243 | entityName = entityNameOrEntity as EntityName<Entity>; |
| 1244 | } |
| 1245 | |
| 1246 | const meta = this.metadata.get<Entity>(entityName); |
| 1247 | const convertCustomTypes = !Utils.isEntity(data); |
| 1248 | |
| 1249 | if (Utils.isEntity(data)) { |
| 1250 | entity = data as Entity; |
| 1251 | |
| 1252 | if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) { |
| 1253 | em.#entityFactory.mergeData(meta, entity, data, { initialized: true }); |
| 1254 | return entity; |
| 1255 | } |
| 1256 | |
| 1257 | where = helper(entity).getPrimaryKey() as FilterQuery<Entity>; |
| 1258 | em.#entityFactory.assignDefaultValues(entity, meta); |
| 1259 | data = em.#comparator.prepareEntity(entity); |
| 1260 | } else { |
| 1261 | data = Utils.copy(QueryHelper.processParams(data)); |
| 1262 | where = Utils.extractPK(data, meta) as FilterQuery<Entity>; |
| 1263 | |
| 1264 | if (where && !this.config.get('upsertManaged')) { |
| 1265 | const exists = em.#unitOfWork.getById<Entity>(entityName, where as Primary<Entity>, options.schema); |
| 1266 | |
| 1267 | if (exists) { |
| 1268 | return em.assign(exists, data as any) as any; |
| 1269 | } |
| 1270 | } |
| 1271 | |
| 1272 | em.#entityFactory.assignDefaultValues(data as Entity, meta, true); |
| 1273 | |
| 1274 | for (const key of Object.keys(data!)) { |
| 1275 | const prop = meta.properties[key as EntityKey<Entity>]; |