-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
upsert with return functionality #1090
Comments
I added await connection.createQueryBuilder()
.insert()
.into(Post)
.values(post2)
.onConflict(`("id") DO NOTHING`)
.execute();
await connection.createQueryBuilder()
.insert()
.into(Post)
.values(post2)
.onConflict(`("id") DO UPDATE SET "title" = :title`)
.setParameter("title", post2.title)
.execute(); For now Will be released in |
* master: fixes #996 fixed failing test added test for #1145 added "ON CONFLICT" cause support for #1090 removed only test added support for indices and listeners in embeddeds version bump added test for #1178 added test for #1210 fixes #1201 removed only test fixes #1118 fixed comments style version bump fixes #1119 and #1205 fixes #1110 # Conflicts: # package.json # src/entity-manager/EntityManager.ts # src/migration/MigrationExecutor.ts # src/query-builder/InsertQueryBuilder.ts # src/repository/BaseEntity.ts # src/subscriber/Broadcaster.ts # test/github-issues/1178/entity/Post.ts # test/github-issues/1178/issue-1178.ts
+1 for actual upsert support |
The above examples show using |
@pleerock @Kononnable any word on support on array of inputs? also, is this only for postgres or does it work for mysql as well? |
can we update row on conflict? |
@landon9720 in postgres you can use the await connection.createQueryBuilder()
.insert()
.into(Post)
.values([post1, post2, ...])
.onConflict(`("id") DO UPDATE SET "title" = excluded.title`)
.execute(); |
What's the status on this issue? |
@mschuttt i haven't been following this project for years, however, i ended up using another library instead which is based on sequelize. it has (user-friendly) upsert functionality and the typescript features are fairly similar if you're used to typeorm |
For those who do not want to finagle with onConflict themselves: |
This comment has been minimized.
This comment has been minimized.
Hi ! Is there any upsert on this issue? |
This comment has been minimized.
This comment has been minimized.
Unfortunately the npm module doesn't work because the code shows up empty. Somebody submitted a PR a couple months ago to fix it danielmhanover/typeorm-upsert#3 which still hasn't been merged in. Seems that the repo isn't being maintained. Here's the upsert() function I ended up writing for PostgreSQL:
https://gist.github.com/JeremyBernier/5683ebc8e83990a4d4e3d0abd1a9549d |
Sorry for the delay on maintaining that package - it is still being maintained but admittedly I went dark over January. Should work on npm now |
@danielmhanover Why not just contribute to typeorm rather than doing your own thing boss? 😊. Would be better if you incorporated your library into typeorm somehow |
I don't think any upsert can work properly due to #2215. If you are upserting with a PK in the object, hoping that the record identified by PK will be updated, it won't actually work. TypeORM omits PKs from value list and the query ends up with insert without PK there, and thereby Example of query generated, where the object did contain an INSERT INTO "user" ("first_name", "last_name", "is_admin")
VALUES ($1, $2, $3)
ON CONFLICT ("id")
DO UPDATE SET
"id" = EXCLUDED."id",
"first_name" = EXCLUDED."first_name",
"last_name" = EXCLUDED."last_name",
"is_admin" = EXCLUDED."is_admin"
RETURNING
* Object I was trying to upsert: { id: 1, firstName: 'baz', lastName: 'bar', isAdmin: false } |
Otherwise, I think I have a pretty clean upsert implementation here: import { EntityManager, QueryFailedError } from './'
interface Type<T> extends Function {
new (...args: any[]): T
}
export interface UpsertOptions<E extends {}, K = keyof E> {
skip?: K[]
pk?: K | K[]
}
function arrayify<T>(maybe: T | T[]) {
return Array.isArray(maybe) ? maybe : [maybe]
}
function objectKeys<T extends object>(obj: T) {
return Object.keys(obj) as Array<keyof typeof obj>
}
export async function upsert<E extends {}>(
entityManager: EntityManager,
Entity: Type<E>,
data: E,
options: UpsertOptions<E> = {},
): Promise<E> {
type K = keyof typeof data
const pks = arrayify(options.pk || ('id' as K))
const skip = options.skip || []
const keys = objectKeys(data).filter((key) => !skip.includes(key))
if (keys.length === 0) {
throw new QueryFailedError('', [], 'Cannot upsert without values specified.')
}
const {
namingStrategy: { columnName },
driver: { escape },
} = entityManager.connection
const col = (key: K) => {
return escape(columnName(key.toString(), undefined, []))
}
const pk = pks.map(col).join(', ')
const set = keys
.map(col)
.map((k) => `${k} = EXCLUDED.${k}`)
.join(', ')
const { generatedMaps } = await entityManager
.getRepository(Entity)
.createQueryBuilder()
.insert()
.values(data)
.onConflict(`(${pk}) DO UPDATE SET ${set}`)
.returning('*')
.execute()
return generatedMaps[0] as E
} |
import { EntityManager, QueryFailedError } from "typeorm";
interface Type<T> extends Function {
new (...args: any[]): T;
}
export interface UpsertOptions<E extends {}, K = keyof E> {
skip?: K[];
pk?: K | K[];
}
function arrayify<T>(maybe: T | T[]) {
return Array.isArray(maybe) ? maybe : [maybe];
}
function objectKeys<T extends object>(obj: T) {
return Object.keys(obj) as Array<keyof typeof obj>;
}
export async function upsert<E extends {}>(
entityManager: EntityManager,
Entity: Type<E>,
data: E | E[], // allow both single entity or multiple entities
options: UpsertOptions<E> = {}
): Promise<E> {
const row = Array.isArray(data) ? data[0] : data; // get a single entity
type K = keyof typeof row;
const pks = arrayify(options.pk || ("id" as K));
const skip = options.skip || [];
const keys = objectKeys(row).filter((key) => !skip.includes(key)); // get key from the single entity
if (keys.length === 0) {
throw new QueryFailedError(
"",
[],
"Cannot upsert without values specified."
);
}
const {
namingStrategy: { columnName },
driver: { escape },
} = entityManager.connection;
const col = (key: K) => {
return escape(columnName(key.toString(), undefined, []));
};
const pk = pks.map(col).join(", ");
const set = keys
.map(col)
.map((k) => `${k} = EXCLUDED.${k}`)
.join(", ");
const { generatedMaps } = await entityManager
.getRepository(Entity)
.createQueryBuilder()
.insert()
.values(data)
.onConflict(`(${pk}) DO UPDATE SET ${set}`)
.returning("*")
.execute();
return generatedMaps[0] as E;
} @moltar Updating a little bit of your code can handle data as an array as well. |
@nowshad-sust that solution is brittle, because it assumes that every item in the array has the same keys. But I do like the idea of adding the array support. |
I would love to share my findings after spending a lot of time looking into this issue. TypeORM documentation really needs a lot of work. I tried the Upsert library, tried the solutions mentioned here but nothing really worked. I was using MySQL and sadly I discovered that await getConnection()
.createQueryBuilder()
.insert()
.into(GroupEntity)
.values(updatedGroups)
.orUpdate({ conflict_target: ['id'], overwrite: ['name', 'parentId', 'web', 'avatar', 'description'] })
.execute(); @moltar it works with arrays as well. |
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
I see the typeorm devs are currently integrating upsert functionality. Any approximate on how long this will be released in your prod version? @nebkat Currently ran into this problem and debating if its worth writing a custom mysql solution or wait it out for your team |
I'm just a contributor, am also waiting for it to get merged. It breaks the API slightly so it might not be accepted for a while. |
Thanks for the quite reply :) |
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
* InsertQueryBuilder.orUpdate() now takes 3 parameters, overwrite columns, column values and conflict condition * Overwrite either specific property paths in array or `true` to overwrite all * Values same as UPDATE query values, replaces overwritten columns * Conflict see onConflict() * InsertQueryBuilder.orIgnore() now takes 2 parameters, ignore and conflict condition * Ignore boolean whether to add IGNORE / DO NOTHING, no longer accepts statement * Conflict see onConflict() * InsertQueryBuilder.onConflict() now accepts either raw expression or property paths array * For Postgres/Sqlite ON CONFLICT ... * EntityManager.upsert(), Repository.upsert(), BaseEntity.upsert() Fixes: typeorm#1090
this worked for me, |
For anyone that stumbles upon this from Google, upsert is directly supported in Which closed: The new method signature is:
So for @ahmadalfy's solution, you'll now need: await getConnection()
.createQueryBuilder()
.insert()
.into(GroupEntity)
.values(updatedGroups)
.orUpdate(
['name', 'parentId', 'web', 'avatar', 'description'],
['id']
)
.execute(); And you can find the current implementation of orUpdate here:
|
What about unique set of colums? I have the following example
This example here gives the following errror: "QueryFailedError: column "dayofweek-pickuppoint" does not exist". So you cant use Unique set of columns? |
Same problem here. I am looking for how to achieve |
it can return id ? |
@LuisOfCourse @CNLHC Did you find a solution/workaround? |
upsert typeorm function not working.... still inserting even i defined the column that should be unique |
is you're column nullable? In Mysql null values can't be used for a constraint |
Does this solution work for
@conoremclaughlin, does this work for postgres?
In my case it is inserting new rows for all the entities, while I am trying to update entities which have PK property set. |
I know I'm too late to catch on this but here is my solution
|
proposing the following functionality (essentially
insert... on conflict.. returning..
):at least in postgres, this is possible in a single operation, but it's not currently possible in a single operation with TypeORM (example not tested, just for showing a current rough solution..):
The text was updated successfully, but these errors were encountered: