* 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: UpsertManyOptions<Entity, Fields> = {},
)
| 1389 | * 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. |
| 1390 | */ |
| 1391 | async upsertMany<Entity extends object, Fields extends string = any>( |
| 1392 | entityNameOrEntity: EntityName<Entity> | Entity[], |
| 1393 | data?: (EntityData<Entity> | NoInfer<Entity>)[], |
| 1394 | options: UpsertManyOptions<Entity, Fields> = {}, |
| 1395 | ): Promise<Entity[]> { |
| 1396 | if (options.disableIdentityMap ?? this.config.get('disableIdentityMap')) { |
| 1397 | const em = this.getContext(false); |
| 1398 | const fork = em.fork({ keepTransactionContext: true }); |
| 1399 | const ret = await fork.upsertMany(entityNameOrEntity, data, { ...options, disableIdentityMap: false }); |
| 1400 | fork.clear(); |
| 1401 | |
| 1402 | return ret; |
| 1403 | } |
| 1404 | |
| 1405 | const em = this.getContext(false); |
| 1406 | em.prepareOptions(options); |
| 1407 | |
| 1408 | let entityName: EntityName<Entity>; |
| 1409 | let propIndex: number | false; |
| 1410 | |
| 1411 | if (data === undefined) { |
| 1412 | entityName = (entityNameOrEntity as Entity[])[0].constructor; |
| 1413 | data = entityNameOrEntity as Entity[]; |
| 1414 | } else { |
| 1415 | entityName = entityNameOrEntity as EntityName<Entity>; |
| 1416 | } |
| 1417 | |
| 1418 | const batchSize = options.batchSize ?? this.config.get('batchSize'); |
| 1419 | |
| 1420 | if (data.length > batchSize) { |
| 1421 | const ret: Entity[] = []; |
| 1422 | |
| 1423 | for (let i = 0; i < data.length; i += batchSize) { |
| 1424 | const chunk = data.slice(i, i + batchSize); |
| 1425 | ret.push(...(await this.upsertMany(entityName, chunk, options))); |
| 1426 | } |
| 1427 | |
| 1428 | return ret; |
| 1429 | } |
| 1430 | |
| 1431 | const meta = this.metadata.get<Entity>(entityName); |
| 1432 | const convertCustomTypes = !Utils.isEntity(data[0]); |
| 1433 | const allData: EntityData<Entity>[] = []; |
| 1434 | const allWhere: FilterQuery<Entity>[] = []; |
| 1435 | const entities = new Map<Entity, EntityData<Entity>>(); |
| 1436 | const entitiesByData = new Map<EntityData<Entity>, Entity>(); |
| 1437 | const entitiesByAllDataIdx = new Map<number, Entity>(); |
| 1438 | |
| 1439 | for (let i = 0; i < data.length; i++) { |
| 1440 | let row = data[i]; |
| 1441 | let where: FilterQuery<Entity>; |
| 1442 | |
| 1443 | if (Utils.isEntity(row)) { |
| 1444 | const entity = row as Entity; |
| 1445 | |
| 1446 | if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) { |
| 1447 | em.#entityFactory.mergeData(meta, entity, row, { initialized: true }); |
| 1448 | entities.set(entity, row); |