Sequelize Upsert создается вместо обновления - PullRequest
0 голосов
/ 10 января 2020

В соответствии с найденной документацией здесь он гласит следующее:

upsert(values, [options]) -> Promise.<created>

Вставить или обновить одну строку. Обновление будет выполнено, если найдена строка, которая соответствует предоставленным значениям либо по первичному ключу, либо по уникальному ключу. Обратите внимание, что уникальный индекс должен быть определен в вашей модели сиквелиза, а не только в таблице. В противном случае вы можете столкнуться с уникальным нарушением ограничения, потому что sequelize не может определить строку, которая должна быть обновлена.

Так что я ожидаю, что использование уникального ключа должно заменить существующее значение. Однако, когда мой код выполняется вместо обновления существующей записи в базе данных, он добавляет новую. Что я делаю не так?

вот пример моей модели

'use strict'

module.exports = (db, dataTypes) => {
  const titanJob = db.define('titanJob', {
    titanId: {
      type: dataTypes.STRING,
      allowNull: false,
      unique: true
    },
    name: {
      type: dataTypes.STRING,
      allowNull: false
    }
  }, {
    timestamps: true
  })
  return titanJob
}

, а вот пример моей упертости

await asyncForEach(res.data.hits.hits, async es => {
  const src = es._source
  try {
    await titanJob.upsert({
      name: src.name,
      titanId: src.id,
    }, { titanId: src.id })
    logger.debug(`[${file}] upsert successful`)
  } catch (err) {
    logger.warn(`[${file}] failed to save to database`)
    logger.warn(`[${file}] ${err}`)
  }
})

1 Ответ

0 голосов
/ 14 февраля 2020

должно работать. Вот пример, использующий "sequelize": "^5.21.3":

index.ts:

import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../../db';
import assert from 'assert';

class TitanJob extends Model {}
TitanJob.init(
  {
    titanId: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true,
    },
    name: {
      type: DataTypes.STRING,
      allowNull: false,
    },
  },
  { sequelize, modelName: 'titanJob', timestamps: true },
);

(async function test() {
  try {
    await sequelize.sync({ force: true });
    const datas = [
      { titanId: '1', name: 'programmer' },
      { titanId: '2', name: 'teacher' },
    ];
    const jobs = await TitanJob.bulkCreate(datas);
    assert.deepEqual(
      jobs.map((job) => ({ titanId: job.id, name: job.name })),
      datas,
      'Should bulk create programmer and teacher datas',
    );
    const rval = await TitanJob.upsert({ titanId: '1', name: 'driver' }, { returning: true });
    assert.equal(rval[0].titanId, '1', 'Should update the row which titanId is "1"');
  } catch (error) {
    console.log(error);
  } finally {
    await sequelize.close();
  }
})();

Результаты выполнения:

{ POSTGRES_HOST: '127.0.0.1',
  POSTGRES_PORT: '5430',
  POSTGRES_PASSWORD: 'testpass',
  POSTGRES_USER: 'testuser',
  POSTGRES_DB: 'node-sequelize-examples' }
Executing (default): DROP TABLE IF EXISTS "titanJob" CASCADE;
Executing (default): DROP TABLE IF EXISTS "titanJob" CASCADE;
Executing (default): CREATE TABLE IF NOT EXISTS "titanJob" ("id"   SERIAL , "titanId" VARCHAR(255) NOT NULL UNIQUE, "name" VARCHAR(255) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'titanJob' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): INSERT INTO "titanJob" ("id","titanId","name","createdAt","updatedAt") VALUES (DEFAULT,'1','programmer','2020-02-14 08:09:45.506 +00:00','2020-02-14 08:09:45.506 +00:00'),(DEFAULT,'2','teacher','2020-02-14 08:09:45.506 +00:00','2020-02-14 08:09:45.506 +00:00') RETURNING *;
Executing (default): CREATE OR REPLACE FUNCTION pg_temp.sequelize_upsert(OUT created boolean, OUT primary_key text)  AS $func$ BEGIN INSERT INTO "titanJob" ("titanId","name","createdAt","updatedAt") VALUES ('1','driver','2020-02-14 08:09:45.524 +00:00','2020-02-14 08:09:45.524 +00:00') RETURNING "id" INTO primary_key; created := true; EXCEPTION WHEN unique_violation THEN UPDATE "titanJob" SET "titanId"='1',"name"='driver',"updatedAt"='2020-02-14 08:09:45.524 +00:00' WHERE ("id" IS NULL OR "titanId" = '1') RETURNING "id" INTO primary_key; created := false; END; $func$ LANGUAGE plpgsql; SELECT * FROM pg_temp.sequelize_upsert();
Executing (default): SELECT "id", "titanId", "name", "createdAt", "updatedAt" FROM "titanJob" AS "titanJob" WHERE "titanJob"."id" = '1';

Нет подтверждения ошибки. Работает как положено. Проверьте строки данных в базе данных:

node-sequelize-examples=# select * from "titanJob";
 id | titanId |  name   |         createdAt          |         updatedAt
----+---------+---------+----------------------------+----------------------------
  2 | 2       | teacher | 2020-02-14 08:09:45.506+00 | 2020-02-14 08:09:45.506+00
  1 | 1       | driver  | 2020-02-14 08:09:45.506+00 | 2020-02-14 08:09:45.524+00
(2 rows)

исходный код: https://github.com/mrdulin/node-sequelize-examples/tree/master/src/examples/stackoverflow/59686743

...