githubEdit

Ent API: upsert*()

The upsert*() call is a mix of INSERT and UPDATE operation, based on an Ent unique key.

Ent.upsert(vc, { field: "...", ... }): string

This call tries to update an existing row in the database (i.e. a row with the same unique key defined in Ent schema). In case there is no such row yet, it inserts the new one.

Returns ID of the updated (or inserted) row.

You can rely on the behavior of autoInsert and autoUpdate fields: they work the same way as in regular insert*() and update*() calls.

Upsert can't work if some triggers are defined for the Ent, because we don't know Ent ID in advance (whether the upsert succeeds or skips on duplication).

Also, upsert() will refuse to run if there are Inverses defined on some Ent fields (same reason: Inverses operations run in a different microshard strictly before the main Ent operation, and they must know the row's ID in advance).

Ent.upsertReturning(vc, { ... }): Ent

This call is very similar to upsert(), but in the end, it loads the updated (or inserted) Ent back from the datbase using loadX().

Since upsert() is meant to always succeed (except when there is a transport error, or when some database constraint check unrelated to the main Ent's unique key fails), there are no "X" and "Nullable" variations of this method.

Batching

Multiple upsert*() calls running in parallel are batched by Ent Framework:

await Promise.all([
  EntTopic.upsert(vc, { 
    slug: "s1",
    creator_id: "123",
    subject: "test1",
  }),
  EntTopic.upsert(vc, {
    slug: "s2",
    creator_id: "456",
    subject: "test2",
  }),
]);

The batched query will look like this:

It is complicated! In fact, the query runs UPDATE-INSERT-UPDATE sequence, to ensure that it doesn't call id_gen() in case the row already exists in the database (to not exhaust the sequence).

Last updated