Knex: upsert ์œ ํ˜• ๊ธฐ๋Šฅ ์ถ”๊ฐ€

์— ๋งŒ๋“  2013๋…„ 08์›” 30์ผ  ยท  54์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: knex/knex

@adamscybot ์ด tgriesser/bookshelf#55์—์„œ ๊ฐ€์ ธ์˜จ ๊ฒƒ - ์ด๊ฒƒ์€ ์ถ”๊ฐ€ํ•˜๊ธฐ์— ์ข‹์€ ๊ธฐ๋Šฅ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

feature request

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@NicolajKN toString()์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๋งŽ์€ ์ข…๋ฅ˜์˜ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๊ณ  DB ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด ๊ฐ’์„ ์ „๋‹ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์ž ์žฌ์ ์ธ SQL ์ฃผ์ž… ๋ณด์•ˆ ๊ตฌ๋ฉ).

๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const query = knex('account').insert(accounts);
const safeQuery = knex.raw('? ON CONFLICT DO NOTHING', [query]);

๋ชจ๋“  54 ๋Œ“๊ธ€

๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ _์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค_!

:+1:

:+1:

CSV์—์„œ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ๋Š”๋ฐ ๋งˆ์ง€๋ง‰ ๊ฐ€์ ธ์˜ค๊ธฐ์—์„œ ์ผ๋ถ€ ๋ ˆ์ฝ”๋“œ๊ฐ€ ๊ฒน์น  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค(์˜ˆ: ๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ€์ ธ์˜จ ๊ฒƒ์€ 1์›” 1์ผ๋ถ€ํ„ฐ 5์›” 31์ผ๊นŒ์ง€, ์ด๋ฒˆ์—๋Š” 5์›” 31์ผ๋ถ€ํ„ฐ 6์›” 18์ผ๊นŒ์ง€ ๊ฐ€์ ธ์˜ด).

๋‹คํ–‰ํžˆ ํƒ€์‚ฌ ์‹œ์Šคํ…œ์€ ์•ˆ์ •์ ์œผ๋กœ ๊ณ ์œ ํ•œ ID๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

์ƒˆ ๋ ˆ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜๊ณ  ์ด์ „ ๋ ˆ์ฝ”๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋‚˜๋Š” ์•„์ง ๊ทธ๊ฒƒ์„ ์‹œ๋„ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

var ids = records.map(function (json) { return json.id })
  ;

Records.forge(ids).fetchAll().then(function () {
  records.forEach(function (record) {
    // now the existing records are loaded in the collection ?
    Object.keys(record).forEach(function (key) {
      Records.forge(record.id).set(key, record[key]);
    });
  });
  Records.invokeThen('save').then(function () {
    console.log('Records have been either inserted or updated');
  });
});

๋˜ํ•œ ๋•Œ๋กœ๋Š” ๋‚ด๊ฐ€ ์ €์žฅํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ํ•ด์‹œ์™€ ๊ฐ™์€ ํ™•์ • id ๊ฐ’์œผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ต์ฒดํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ํ•ญ์ƒ SQL์„ ์ „ํ†ต์ ์ธ SQL๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ข…์ข… ๋ช…ํ™•ํ•œ ๊ด€๊ณ„ ๋งคํ•‘ ๋ฐ ์ธ๋ฑ์Šค์˜ ์ด์ ๊ณผ ํ•จ๊ป˜ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ NoSQL๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

:+1:

์•ˆ๋…•,
์ด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์†Œ์‹์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์•„๋‹ˆ๋ฉด ๋ˆ„๊ตฐ๊ฐ€ mysql์— ๋Œ€ํ•ด ์ด ๊ธฐ๋Šฅ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ฃผ๋Š” ์˜ˆ์ œ๋ฅผ ์ถ”์ฒœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๊ณ ๋งˆ์›Œ

์ง€๊ธˆ์€ raw ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€๋งŒ ๊ณง ์—ฌ๊ธฐ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์—ด์‹ฌํžˆ ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Postgres๋Š” ๋ฐฉ๊ธˆ upsert ์ง€์›์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. +1:

http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=168d5805e4c08bed7b95d351bf097cff7c07dd65

https://news.ycombinator.com/item?id=9509870

๊ตฌ๋ฌธ์€ INSERT ... ON CONFLICT DO UPDATE ์ž…๋‹ˆ๋‹ค.

MySql์—์„œ REPLACE INTO ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ ์ด ๊ธฐ๋Šฅ ์š”์ฒญ์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. REPLACE ๋ฐ INSERT ๋Š” MySql์—์„œ ์ •ํ™•ํžˆ ๋™์ผํ•œ ๊ตฌ๋ฌธ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ON DUPLICATE KEY UPDATE ๋ณด๋‹ค ๊ตฌํ˜„ํ•˜๊ธฐ๊ฐ€ ๋” ์‰ฝ๋‹ค๊ณ  ์ƒ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. REPLACE ๋ฅผ ๊ตฌํ˜„ํ•  ๊ณ„ํš์ด ์žˆ์Šต๋‹ˆ๊นŒ? PR์ด ๊ฐ€์น˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ?

ํŠนํžˆ PostreSQL 9.5์—์„œ ์ด์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

ํ•œ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์งˆ๋ฌธ์€ PostgreSQL ๋ฐ MySQL๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ๋ฐฉ์–ธ์— ๋Œ€ํ•ด ๋™์ผํ•œ upsert ๋ฉ”์„œ๋“œ ์„œ๋ช…์„ ๋…ธ์ถœํ• ์ง€ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค. Sequelize์—์„œ upsert ์˜ ๋ฐ˜ํ™˜ ๊ฐ’๊ณผ ๊ด€๋ จํ•˜์—ฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: https://github.com/sequelize/sequelize/issues/3354.

์ผ๋ถ€ KnexJS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฉ”์„œ๋“œ๋Š” ๋‹ค๋ฅธ ๋ฐฉ์–ธ์˜ ์ปจํ…์ŠคํŠธ์—์„œ ๋ฐ˜ํ™˜ ๊ฐ’๊ณผ ๊ด€๋ จํ•˜์—ฌ ๊ตฌ๋ณ„์ด ์žˆ์Œ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: insert , ์—ฌ๊ธฐ์„œ ์ฒซ ๋ฒˆ์งธ ์‚ฝ์ž…๋œ ID์˜ ๋ฐฐ์—ด์ด Sqlite ๋ฐ MySQL์— ๋Œ€ํ•ด ๋ฐ˜ํ™˜๋˜๋Š” ๋ฐ˜๋ฉด ๋ชจ๋“  ๋ฐฐ์—ด์€ ์‚ฝ์ž…๋œ ID๋Š” PostgreSQL๊ณผ ํ•จ๊ป˜ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค).

์„ค๋ช…์„œ์— ๋”ฐ๋ฅด๋ฉด MySQL์˜ INSERT ... ON DUPLICATE KEY UPDATE ๊ตฌ๋ฌธ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋™์ž‘์ด ์žˆ์Šต๋‹ˆ๋‹ค(http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html).

ON DUPLICATE KEY UPDATE๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ–‰๋‹น ์˜ํ–ฅ์„ ๋ฐ›๋Š” ํ–‰ ๊ฐ’์€ ํ–‰์ด ์ƒˆ ํ–‰์œผ๋กœ ์‚ฝ์ž…๋˜๋ฉด 1, ๊ธฐ์กด ํ–‰์ด ์—…๋ฐ์ดํŠธ๋˜๋ฉด 2, ๊ธฐ์กด ํ–‰์ด ํ˜„์žฌ ๊ฐ’์œผ๋กœ ์„ค์ •๋œ ๊ฒฝ์šฐ 0์ž…๋‹ˆ๋‹ค.

PostgreSQL์—์„œ (http://www.postgresql.org/docs/9.5/static/sql-insert.html):

์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด INSERT ๋ช…๋ น์€ ๋‹ค์Œ ํ˜•์‹์˜ ๋ช…๋ น ํƒœ๊ทธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

INSERT oid count

count๋Š” ์‚ฝ์ž…๋˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธ๋œ ํ–‰์˜ ์ˆ˜์ž…๋‹ˆ๋‹ค. count๊ฐ€ ์ •ํ™•ํžˆ 1์ด๊ณ  ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— OID๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ oid๋Š” ์‚ฝ์ž…๋œ ํ–‰์— ํ• ๋‹น๋œ OID์ž…๋‹ˆ๋‹ค. ๋‹จ์ผ ํ–‰์ด ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š๊ณ  ์‚ฝ์ž…๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด oid๋Š” 0์ž…๋‹ˆ๋‹ค.

INSERT ๋ช…๋ น์— RETURNING ์ ˆ์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ ๊ฒฐ๊ณผ๋Š” ๋ช…๋ น์— ์˜ํ•ด ์‚ฝ์ž…๋˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธ๋œ ํ–‰์— ๋Œ€ํ•ด ๊ณ„์‚ฐ๋œ RETURNING ๋ชฉ๋ก์— ์ •์˜๋œ ์—ด๊ณผ ๊ฐ’์„ ํฌํ•จํ•˜๋Š” SELECT ๋ฌธ์˜ ๊ฒฐ๊ณผ์™€ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ RETURNING ์ ˆ๋กœ ๋ฐ˜ํ™˜๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ๊ฐ?

๋‚˜๋Š” ์‚ฝ์ž…์„ ์œ„ํ•œ "onConflict" ๋ฉ”์†Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด Client_PG๋ฅผ ์›์ˆญ์ด ํŒจ์น˜ํ–ˆ๋‹ค. github oauth ์ž๊ฒฉ ์ฆ๋ช…์„ upsertํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const profile = {
    access_token: "blah blah",
    username: "foobar",
    // ... etc
  }

  const oauth = {
    uid: "13344398",
    provider: "github",
    created_at: new Date(),
    updated_at: new Date(),
    info: profile,
  };

  // todo: add a "timestamp" method

const insert = knex("oauths").insert(oauth).onConflict(["provider", "uid"],{
  info: profile,
  updated_at: new Date(),
});

console.log(insert.toString())

์—ด ์ด๋ฆ„์˜ ๋ฐฐ์—ด์€ ๊ณ ์œ ์„ฑ ์ œ์•ฝ ์กฐ๊ฑด์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

insert into "authentications" ("created_at", "info", "provider", "uid", "updated_at") values ('2016-02-14T14:42:18.342+08:00', '{\"access_token\":\"blah blah\",\"username\":\"foobar\"}', 'github', '13344398', '2016-02-14T14:42:18.342+08:00') on conflict ("provider", "uid")  do update set "info" = '{\"access_token\":\"blah blah\",\"username\":\"foobar\"}', "updated_at" = '2016-02-14T14:42:18.343+08:00'

์›์ˆญ์ด ํŒจ์น˜์— ๋Œ€ํ•œ ์š”์ : https://gist.github.com/hayeah/1c8d642df5cfeabc2a5b ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

์ด๊ฒƒ์€ ๋งค์šฐ ํ•ดํ‚น๋œ ์‹คํ—˜์ž…๋‹ˆ๋‹ค... ๋”ฐ๋ผ์„œ ์›์ˆญ์ด ํŒจ์น˜๋ฅผ ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์— ์ •ํ™•ํžˆ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ์ง€ ๋งˆ์‹ญ์‹œ์˜ค. p

์•Œ๋ ค์ง„ ๋ฌธ์ œ:

  • QueryBuilder์— ์žˆ๋Š” ์›์ˆญ์ด ํŒจ์น˜๋Š” Client_PG๊ฐ€ ๋นŒ๋”๋ฅผ ์ „๋ฌธํ™”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  ๋ฐฉ์–ธ์— ์˜ํ–ฅ์„ ์ค๋‹ˆ๋‹ค.
  • count = count + 1 ์™€ ๊ฐ™์€ ์›์‹œ ์—…๋ฐ์ดํŠธ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ์‚ฝ์ž…๋˜์ง€ ์•Š์œผ๋ฉด onConflict๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ”ผ๋“œ๋ฐฑ?

@hayeah ๋‚˜๋Š” ๋‹น์‹ ์˜ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ข‹์•„ํ•˜๊ณ  Postgres์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹น์‹ ์ด ์ง€์ ํ•œ ๊ฒƒ ์ด์™ธ์˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋ฅผ ๊ฒฝํ—˜์ ์œผ๋กœ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ์ ํŠธ์—์„œ ์›์ˆญ์ด ํŒจ์น˜๋ฅผ ์‹œ๋„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ตฌ๋ฌธ ์ œ์•ˆ: knex('table').upsert(['col1','col2']).insert({...}).update({...}); ์—ฌ๊ธฐ์„œ upsert ๋Š” ์กฐ๊ฑด๋ฌธ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ db์— ๊ตญํ•œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

upsert์˜ ๋‹ค์–‘ํ•œ ๊ตฌํ˜„์— ๋Œ€ํ•œ ์š”์•ฝ์€ https://en.wikipedia.org/wiki/Merge_(SQL )์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ €๋„ ์ด ๋Šฅ๋ ฅ์— ๊ด€์‹ฌ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ ์‚ฌ๋ก€: ์™ธ๋ถ€ ์„œ๋น„์Šค์˜ ๋งŽ์€ ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ์— ์˜์กดํ•˜๋Š” ์‹œ์Šคํ…œ ๊ตฌ์ถ• ๋กœ์ปฌ MySQL db์— ์ €์žฅํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ์ฃผ๊ธฐ์ ์œผ๋กœ ํด๋งํ•ฉ๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์ง€๊ธˆ์€ knex.raw๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ด€์‹ฌ์ด ์žˆ์ง€๋งŒ ๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ๋Š” ์—ด์— ํ•ญ์ƒ '๊ณ ์œ ' ์ œ์•ฝ ์กฐ๊ฑด์ด ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ฏ€๋กœ ์ถฉ๋Œ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ์ฟผ๋ฆฌ์™€ ์ผ์น˜ํ•˜๋Š” ํ•ญ๋ชฉ์„ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์‚ฝ์ž… ์ƒˆ ํ–‰.

@haywirez ๊ณ ์œ  ์ œ์•ฝ ์กฐ๊ฑด์ด ์—†๋Š” ์ด์œ ๊ฐ€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ๊ฒฝ์Ÿ ์กฐ๊ฑด์— ๋…ธ์ถœ๋˜์ง€ ์•Š์„๊นŒ์š”?

@hayeah ๋‚˜๋Š” ์ฃผ์–ด์ง„ ๋‚ ์งœ์— ์—ฐ๊ฒฐ๋œ ๊ฐ’์ด ์žˆ๋Š” ํ•ญ๋ชฉ์„ ์ €์žฅํ•˜๋Š” ์‹œ๊ฐ„ ์ฐฝ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ํŠน์ • ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ผ์น˜ํ•˜๋Š” (์ผ) ํƒ€์ž„์Šคํƒฌํ”„์˜ "๊ฒฐํ•ฉ๋œ ํ‚ค"์™€ ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์˜ PK์— ํ•ด๋‹นํ•˜๋Š” ๋‘ ๊ฐœ์˜ ๋‹ค๋ฅธ ID๊ฐ€ ์žˆ๋Š” ํ•ญ๋ชฉ์„ ์‚ฝ์ž…ํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. 24์‹œ๊ฐ„ ์ด๋‚ด์— ์‚ฝ์ž…ํ•˜๊ฑฐ๋‚˜ ์ตœ์‹  ์นด์šดํŠธ๋กœ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์ข‹์€ ๊ธฐ๋Šฅ์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค!

์—ฌ๊ธฐ์— ๋Œ“๊ธ€์„ ๋‹ฌ์•„์ฃผ์‹  ๋ชจ๋“  ๋ถ„๋“ค ์•ˆ๋…•ํ•˜์„ธ์š”. PRPlease ๋ ˆ์ด๋ธ”์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋Š” PR์„ ํ•˜๊ฒŒ ๋˜์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋จผ์ € ์—ฌ๊ธฐ์—์„œ ์›ํ•˜๋Š” API์— ๋Œ€ํ•œ ํ† ๋ก ์„ ๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

์ถ”์‹ .

^^ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด์™€ ๊ฐ™์€ ๋Œ“๊ธ€์„ ์‚ญ์ œํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. +1์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ์ž‘์€ ์ด๋ชจํ‹ฐ์ฝ˜ ๋ฐ˜์‘์œผ๋กœ ๊ทธ๋ ‡๊ฒŒ ํ•˜์„ธ์š”.

@willfarrell ๋ฐ @hayeah ์˜ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ์—ด ์ œํ•œ ๋ฐฐ์—ด์— ์•ฝ๊ฐ„์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์˜ˆ๊ฐ€ json ์†์„ฑ์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ œ์•ˆ ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ๋ ˆ์ฝ”๋“œ์™€ ์ผ์น˜ํ•˜๋Š” where ๋ฌธ/์ ์ ˆํ•œ "์ฟผ๋ฆฌ"๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š๋Š” ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

์ œ์•ˆ 1

knex('table')
  .where('id', '=', data.id)
  .upsert(data)

์ œ์•ˆ 2

knex('table')
  .upsertQuery(knex => {
    return knex('table')
      .where('id', '=', data.id)
  })
  .upsertUpdate(knex => {
    return knex('table')
      .insert(data)
  })

์ œ์•ˆ 3

knex('table')
  .where('id', '=', data.id)
  .insert(data)
  .upsert() // or .onConflictDoUpdate()

๋‚˜๋Š” 3๊ณผ ๊ฐ™์€ ๊ฒƒ์— ๊ฐ€์žฅ ๋งŽ์ด ๊ธฐ์šธ๊ณ  ์žˆ๋‹ค.

์—ฌ๊ธฐ์— mongodb๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค .

db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

@reggi ๋‚ด ์›์ˆญ์ด ํŒจ์น˜๊ฐ€ where ์™€ ํ˜ธํ™˜๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค ...

@reggi ๋‚˜๋Š” ๋‹น์‹ ์˜ ์š”์ ์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.
@willfarrell ๋ฐ @hayeah ์˜ ์˜ˆ์—์„œ ์ œ์•ˆ๋œ ์ ‘๊ทผ ๋ฐฉ์‹์—์„œ ์–ด๋–ค ๊ธฐ๋Šฅ์ด ๋ˆ„๋ฝ๋˜์—ˆ๋Š”์ง€ ์ž์„ธํžˆ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
์™œ where ์ด ํ•„์š”ํ•ฉ๋‹ˆ๊นŒ?
insert ์ž‘์—…์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

@reggi ์ œ๊ณตํ•œ MongoDB ์˜ˆ์ œ๋Š” "๋จผ์ € UPDATE WHERE ์‹œ๋„ ... ์ฟผ๋ฆฌ์™€ ์ผ์น˜ํ•˜๋Š” ๋ฌธ์„œ๊ฐ€ ์—†์œผ๋ฉด INSERT ์ˆ˜ํ–‰"์„ ์ฝ๋Š” ๋ฐ˜๋ฉด SQL UPSERT๋Š” "INSERT INTO ... ์ด ๊ธฐ๋ณธ ํ‚ค๊ฐ€ ์žˆ๋Š” ํ–‰์ด ์ด๋ฏธ ์žˆ๋Š” ๊ฒฝ์šฐ UPDATING"์ด๋ผ๊ณ  ์ฝ์Šต๋‹ˆ๋‹ค. .
๋”ฐ๋ผ์„œ SQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ตฌํ˜„๋˜๋Š” ๊ฒƒ๊ณผ๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ "์—…์„œํŠธ"์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด API๋ฅผ ์ œ์•ˆํ•  ๊ฒƒ์ด๋‹ค:

knex.createTable('test')
   .bigserial('id')
   .varchar('unique').notNull().unique()
   .varchar('whatever')

knex.table('test').insert(object, { upsert: ['unique'] })

.insert() ํ•จ์ˆ˜๋Š” ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.
๋ฌธ์ž์—ด์ด๋ฉด ์ด์ „ returning ๋งค๊ฐœ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค.
๊ฐ์ฒด์ธ ๊ฒฝ์šฐ options.returning ๋ฐ options.upsert ๊ฐ€ ์žˆ๋Š” options ๋งค๊ฐœ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ options.upsert ๋Š” ๊ณ ์œ  ํ‚ค ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค. ๋ณตํ•ฉ ๊ณ ์œ  ํ‚ค ์ œ์•ฝ ์กฐ๊ฑด์˜ ๊ฒฝ์šฐ).
๊ทธ๋Ÿฐ ๋‹ค์Œ object ์—์„œ ๊ธฐ๋ณธ ํ‚ค์™€ ๋ชจ๋“  options.upsert ํ‚ค๋ฅผ ์ œ์™ธํ•˜๊ณ  ( clone(object) && delete cloned_object.id && delete cloned_object.unique ๋ฅผ ํ†ตํ•ด) ์ œ๊ฑฐ๋œ cloned_object ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” SQL ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. SQL ์ฟผ๋ฆฌ์˜ ๋‘ ๋ฒˆ์งธ ๋ถ€๋ถ„์—์„œ SET ์ ˆ์„ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ(๋ฐ ๊ณ ์œ ) ํ‚ค: ... ON CONFLICT DO UPDATE SET [iterate cloned_object] .

๊ทธ๊ฒƒ์ด ํ˜„์žฌ API์™€ ๋™์งˆ์ ์ธ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ณ  ๋ชจํ˜ธํ•˜์ง€ ์•Š์€ ์†”๋ฃจ์…˜์ด ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@slavafomin @ScionOfBytes API๋„ ์•„์ง ํ•ฉ์˜๋˜์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ๋‹ค์Œ ๋‹จ๊ณ„๊ฐ€ ๋  ๊ฒƒ์ด๊ณ  ๊ทธ๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๊ธฐ๋ฅผ ์ข‹์•„ํ•˜๋Š” ์‚ฌ๋žŒ์€ ๊ทธ๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์†Œ์‹์ด ์—†์Šต๋‹ˆ๋‹ค.

์ถ”์‹ . ์ด ์Šค๋ ˆ๋“œ๊ฐ€ ๋‰ด์Šค ์š”์ฒญ ์ŠคํŒธ ๋ฐ ๊ธฐํƒ€ ๊ด€๋ จ์„ฑ์ด ๋œํ•œ ๋ฉ”์‹œ์ง€๋กœ ์ฑ„์›Œ์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ์ถ”๊ฐ€ ๋‰ด์Šค ์š”์ฒญ์ด ์—†๋Š” ๊ฒฝ์šฐ ๋‰ด์Šค์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์š”์ฒญ์„ ์‚ญ์ œํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

@amir-s ๋™์˜ํ•˜์ง€๋งŒ ์ด ๋ฌธ์ œ์˜ ์ฃผ์ œ๋Š” upsert ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

IMO, ์ง„์งœ ๋ฌธ์ œ๋Š” API๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ upsert๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋“œ๋ฌธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

MySQL(ON DUPLICATE KEY UPDATE) ๋ฐ PostgreSQL 9.5+(ON CONFLICT DO UPDATE)๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ upsert๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

MSSQL๊ณผ Oracle์€ merge ์ ˆ๋กœ ์ด๋ฅผ ์ง€์›ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, knex๊ฐ€ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ ค๋ฉด ์ถฉ๋Œํ•˜๋Š” ์—ด์˜ ์ด๋ฆ„์„ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

-- in this case the conflict column is 'a'
merge into target
using (values (?)) as t(a)
on (t.a = target.a)
when matched then
  update set b = ?
when not matched then
  insert (a, b) values (?, ?);

๊ทธ๋Ÿฌ๋‚˜ SQLite๋Š” ๊ทธ๋ ‡์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. upsert๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๋ ค๋ฉด ๋‘ ๊ฐœ์˜ ์ฟผ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

-- 'a' is the conflict column
insert or ignore into target (a, b) values (?, ?);
update target set b = ?2 where changes() = 0 and a = ?1;

๋˜๋Š” INSERT OR REPLACE , REPLACE ์‚ฌ์šฉ

-- replace will delete the matched row then add a new one with the given data
replace into target (a, b) values (?, ?);

๋ถˆํ–‰ํžˆ๋„ ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ๋ฐ b๋ณด๋‹ค ๋” ๋งŽ์€ ์—ด์ด ์žˆ๋Š” ๊ฒฝ์šฐ ํ•ด๋‹น ๊ฐ’์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋Œ€์ฒด๋ฉ๋‹ˆ๋‹ค.

insert or replace into target (a, b, c) values (?, ?, (select c from target where a = ?1))

CTE๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ์†”๋ฃจ์…˜์€ ์ด stackoverflow ๋‹ต๋ณ€ ์„ ์ฐธ์กฐํ•˜์„ธ์š”.

๋‚˜๋Š” knex ๊ธฐ๋ฐ˜ Postgres upsert๋ฅผ ์ฐพ์•„ ์ด ๋ฌธ์ œ์— ์—ฌ๋Ÿฌ ๋ฒˆ ์™”์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ด๊ฒƒ์„ ํ•„์š”๋กœ ํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‹จ์ผ ๋ฐ ๋ณตํ•ฉ ๊ณ ์œ  ํ‚ค์— ๋Œ€ํ•ด ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.

์„ค์ •

์•„๋ž˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ด๋ธ”์— ๊ณ ์œ  ํ‚ค ์ œ์•ฝ ์กฐ๊ฑด์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ณตํ•ฉ ํ‚ค ์ œ์•ฝ ์กฐ๊ฑด์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

table.unique(['a', 'b'])

ํ•จ์ˆ˜

(ํŽธ์ง‘: ์›์‹œ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋„๋ก ์—…๋ฐ์ดํŠธ๋จ)

const upsert = (params)=> {
  const {table, object, constraint} = params;
  const insert = knex(table).insert(object);
  const update = knex.queryBuilder().update(object);
  return knex.raw(`? ON CONFLICT ${constraint} DO ? returning *`, [insert, update]).get('rows').get(0);
};

์šฉ๋ฒ•

const objToUpsert = {a:1, b:2, c:3}

upsert({
    table: 'test',
    object: objToUpsert,
    constraint: '(a, b)',
})

์ œ์•ฝ ์กฐ๊ฑด์ด ํ•ฉ์„ฑ์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๋‹น์—ฐํžˆ ํ•œ ์ค„์€ constraint: '(a)' ์ž…๋‹ˆ๋‹ค.

์—…๋ฐ์ดํŠธ๋œ ๊ฐœ์ฒด ๋˜๋Š” ์‚ฝ์ž…๋œ ๊ฐœ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋ณตํ•ฉ nullable ์ธ๋ฑ์Šค์— ๋Œ€ํ•œ ์ฐธ๊ณ  ์‚ฌํ•ญ

๋ณตํ•ฉ ์ธ๋ฑ์Šค (a,b) ๊ฐ€ ์žˆ๊ณ  b ๊ฐ€ nullable์ด๋ฉด (1, NULL) ๋ฐ (1, NULL) ๊ฐ’์€ Postgres์—์„œ ์ƒํ˜ธ ๊ณ ์œ ํ•œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค(์ดํ•ดํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์–ด๋Š ํ•˜๋‚˜). ์ด๊ฒƒ์ด ์‚ฌ์šฉ ์‚ฌ๋ก€์ธ ๊ฒฝ์šฐ ๋ถ€๋ถ„ ๊ณ ์œ  ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“  ๋‹ค์Œ upsert ์ „์— null์„ ํ…Œ์ŠคํŠธํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ œ์•ฝ ์กฐ๊ฑด์„ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ถ€๋ถ„ ๊ณ ์œ  ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. CREATE UNIQUE INDEX unique_index_name ON table (a) WHERE b IS NULL . ํ…Œ์ŠคํŠธ์—์„œ b ๊ฐ€ null์ธ ๊ฒƒ์œผ๋กœ ํ™•์ธ๋˜๋ฉด upsert์—์„œ constraint: '(a) WHERE b IS NULL' ์ œ์•ฝ ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. a ๋„ nullable์ธ ๊ฒฝ์šฐ 3๊ฐœ์˜ ๊ณ ์œ  ์ธ๋ฑ์Šค์™€ 4๊ฐœ์˜ if/else ๋ถ„๊ธฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค(๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ ํ™•์‹คํ•˜์ง€ ์•Š์Œ).

๋‹ค์Œ์€ ์ปดํŒŒ์ผ๋œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ž…๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๊ฒƒ์„ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. @elhigu knex().update(object) ์‚ฌ์šฉ์— ๋Œ€ํ•œ ์˜๊ฒฌ์ด ์žˆ์œผ์‹ญ๋‹ˆ๊นŒ? (ํŽธ์ง‘: Nevermind - ๊ฒฝ๊ณ ๋ฅผ ๋ณด์•˜์Šต๋‹ˆ๋‹ค - ์ง€๊ธˆ knex.queryBuilder() ์‚ฌ์šฉ)

@timhuff ๋Š” ๋ฉ‹์ง€๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค. ๋ณ€๊ฒฝํ•ด์•ผ ํ•  ํ•œ ๊ฐ€์ง€๋Š” ๊ฐ’ ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ์›์‹œ์— ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด query.toString() ๊ฐ€ ์ฟผ๋ฆฌ์˜ ๊ฐ ๋ถ€๋ถ„์„ ๋ Œ๋”๋งํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ ๊ฐ€๋Šฅํ•œ ์ข…์†์„ฑ ์ฃผ์ž… ๊ตฌ๋ฉ์ด ์—ด๋ฆฝ๋‹ˆ๋‹ค(queryBuilder.toString()๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฐ”์ธ๋”ฉ๋งŒํผ ๋“œ๋ผ์ด๋ฒ„์— ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ๋งŒํผ ์•ˆ์ „ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค).

@elhigu ์ž ๊น... query.toString() ๋Š” ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ? ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๋Š” ์ˆ˜์ • ์‚ฌํ•ญ์˜ ๋Œ€๋žต์ ์ธ ์˜ˆ๋ฅผ ๋“ค์–ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š”... ์—…๋ฐ์ดํŠธํ•  ์ฝ”๋“œ๊ฐ€ ๋งŽ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Raw bindings ๋ผ๋Š” ๋ ˆ์ด๋ธ”์ด ๋ถ™์€ ๋ฌธ์„œ ๋ถ€๋ถ„์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์˜ˆ์ œ๋ฅผ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. query.toString ๋Š” ์•ˆ์ „ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์„œ ์„น์…˜์— "์•ˆ์ „ํ•˜์ง€ ์•Š์€ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•"๊ณผ ๊ฐ™์€ ๋ ˆ์ด๋ธ”์ด ์žˆ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ธˆ์ง€ ์‚ฌํ•ญ์€ ์†Œ์ˆ˜์— ๋ถˆ๊ณผํ•˜๋ฉฐ ์‚ฌ๋žŒ๋“ค์ด "์ด๋Ÿฐ ์ผ์„ ํ•˜์ง€ ์•Š๋Š” ํ•œ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค"๋ผ๋Š” ๊ฒƒ์„ ์•Œ๋ฉด์„œ ๋„์„œ๊ด€์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ upsert๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. https://gist.github.com/adnanoner/b6c53482243b9d5d5da4e29e109af9bd
๋‹จ์ผ ๋ฐ ์ผ๊ด„ upsert๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. @plurch ์—์„œ ์•ฝ๊ฐ„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ์„ ์€ ํ•ญ์ƒ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :)

๊ฐ€์น˜ ์žˆ๋Š” ์ผ์„ ์œ„ํ•ด ๋‹ค์Œ ํ˜•์‹์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŽธ์ง‘: ์ด๊ฒƒ์„ ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ์„ ์œ„ํ•ด ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. @elhigu ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค

const query = knex( 'account' ).insert( accounts );
const safeQuery = knex.raw( '? ON CONFLICT DO NOTHING', [ query ]);

@NicolajKN toString()์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๋งŽ์€ ์ข…๋ฅ˜์˜ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๊ณ  DB ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด ๊ฐ’์„ ์ „๋‹ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์ž ์žฌ์ ์ธ SQL ์ฃผ์ž… ๋ณด์•ˆ ๊ตฌ๋ฉ).

๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const query = knex('account').insert(accounts);
const safeQuery = knex.raw('? ON CONFLICT DO NOTHING', [query]);

๊ด€๋ จ ์—†๋Š” ๋ฌธ์ œ์— ๋Œ€ํ•œ ํ† ๋ก ์„ ์‚ญ์ œํ–ˆ์Šต๋‹ˆ๋‹ค.

@elhigu ์ž ๊น๋งŒ ๊ทธ ์‚ฝ์ž… ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ ๋œ ์งํ›„์— ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ? ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

@cloutiertyler ๋‹น์‹ ์€ ๋‚˜์—๊ฒŒ ๋ง์„ ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ์•„๋งˆ๋„ ๋‚ด๊ฐ€ @elhigu ์—์„œ ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฟผ๋ฆฌ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. knex('account').insert(accounts) ๋ฌธ์€ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ค์ œ๋กœ ํ˜ธ์ถœ๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์˜ˆ: .then ๋ฅผ ํ†ตํ•ด). ๊ทธ๋Š” ์ด๊ฒƒ์„ query.toString() ํ˜ธ์ถœํ•  knex.raw('? ON CONFLICT DO NOTHING', [query]) ๋กœ ๋ณด๋‚ด๊ณ  ์ฟผ๋ฆฌ ๋ฅผ ์‹คํ–‰๋  SQL ๋ฌธ์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

@timhuff ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค Tim, ๊ทธ๋Ÿฐ ์‹์ด์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ ์•ฝ์†์— ๋Œ€ํ•œ ์ •์ƒ์ ์ธ ํ–‰๋™์€ ์•„๋‹™๋‹ˆ๋‹ค. ์•ฝ์†์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ƒ์„ฑ ์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๋ฌป๋Š” ์ด์œ ๋Š” ์ด upsert๋ฅผ ์‹คํ–‰ํ•˜๋ ค๊ณ  ํ•  ๋•Œ "์—ฐ๊ฒฐ ์ข…๋ฃŒ๋จ"์ด๋ผ๋Š” ์˜ค๋ฅ˜๊ฐ€ ์ž์ฃผ ๋ฐœ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์‚ฝ์ž…๋ฌผ์„ ์ œ๊ฑฐํ•˜๊ณ  ์™„์ „ํžˆ ์›์‹œ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์œผ๋กœ ์ „ํ™˜ํ•˜๋ฉด ์‚ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค. ๊ฒฝ์Ÿ ์กฐ๊ฑด๊ณผ ์ผ์น˜ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.

knex QueryBuilder s๋Š” Promise s๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. knex ์ฟผ๋ฆฌ ์ž‘์„ฑ์„ ์‹œ์ž‘ํ•˜๋ฉด "knexland"์— ๋จธ๋ฌผ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋นŒ๋“œํ•˜๋ ค๋Š” ์ฟผ๋ฆฌ์˜ JSON ์‚ฌ์–‘์„ ๊ตฌ์„ฑํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. .toString ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋นŒ๋“œํ•˜๊ณ  ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ๋“ค ์ค‘ ํ•˜๋‚˜ ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๊นŒ์ง€๋Š” ( bluebird ) Promise๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ช…๋ น๋ฌธ์„ ์ฆ‰์‹œ ์‹คํ–‰ํ•˜๋ ค๋ฉด .return ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์•„, ์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํ˜ผ๋ž€์ด ํ•ด์†Œ๋ฉ๋‹ˆ๋‹ค. ์„ค๋ช…๊ณผ ํฌ์ธํ„ฐ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๋‚ด ๋ฌธ์ œ๋Š” ๋‹ค๋ฅธ ๊ณณ์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ œ์ณ๋‘๊ณ , ์ฆ‰์‹œ ์‹คํ–‰ ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์‚ฌ์‹ค์€ ์ข…์ข… ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋•Œ๋•Œ๋กœ ๋‹น์‹ ์€ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ๊ทธ๊ฒƒ์„ ๊ตฌ์„ฑํ•˜๊ณ  ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

const medicalBuildings = knex.select('building_id').from('buildings').where({type: 'medical'})
const medicalWorkers = knex.select().from('workers').whereIn('building', medicalBuildings)

(๋งค์šฐ ์ธ์œ„์ ์ธ ์˜ˆ์ด์ง€๋งŒ ์‹คํ–‰ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.)

๋‚˜๋Š” ์‹ค์ œ๋กœ ์ฒซ ๋ฒˆ์งธ ๋ฌธ์žฅ์„ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‘ ๋ฒˆ์งธ ๋ฌธ์žฅ์˜ ์ผ๋ถ€์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

๋ชจ๋“  ์ฟผ๋ฆฌ ๋นŒ๋”๊ฐ€ ์ƒ์„ฑ ์‹œ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ ๋นŒ๋” ํŒจํ„ด ์ฟผ๋ฆฌ๋Š” ๋นŒ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ํŠธ๋ฆฌ๊ฑฐ๋ฉ๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์ข…๋ฃŒ์ž ๋ฉ”์„œ๋“œ๊ฐ€ ์—†์œผ๋ฉด ์ „ํ˜€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@elhigu ์ œ ๋ง์€... ํ•ญ์ƒ ๋‹ค์Œ ํ‹ฑ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์€๋ฐ์š”? ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ์ข‹์€ ์ƒ๊ฐ์ด ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ œ์•ˆํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ์ง€๋งŒ ์‹ค์ œ๋กœ ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ์ฟผ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ๋‹ค๋ฅธ ํ‹ฑ์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๊นŒ?

@timhuff ๋‚˜๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋„ค ๊ทธ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™์•„์š”. ์ฟผ๋ฆฌ ์ž‘์„ฑ์„ ์‹œ์ž‘ํ•œ ๋‹ค์Œ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ๊ณ„์† ๋นŒ๋“œํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์•„์ฃผ ํ”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ์ž์ฃผ ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ.

@lukewlms ๋Š” 'execute()'์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ '.then()'์ด๋ผ๊ณ  ํ•˜๋ฉฐ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์•ฝ์†์„ ๋ฐ›๊ณ  ์‹ถ์„ ๋•Œ ํ•ญ์ƒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 'thenable'์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ด๋ฉฐ promise spec์— ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Promise์™€ async/await(Promise.resolve ๋ฐ .then์— ๋Œ€ํ•œ ๊ฑฐ์˜ ์˜๊ด‘์Šค๋Ÿฌ์šด ๋ฐ”๋กœ ๊ฐ€๊ธฐ)๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ค‘์š”ํ•˜๊ณ  ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ  ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ ์•ฑ ์ถฉ๋Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ์ฐพ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ upsert ๊ธฐ๋Šฅ ๊ตฌํ˜„ https://github.com/tgriesser/knex/pull/2197 ์— ๋Œ€ํ•œ ์ด PR์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ๋” ๋‚ซ์Šต๋‹ˆ๋‹ค. API๋Š” ์ด๋ฏธ ์ž‘๋™ ๋ฐฉ์‹์„ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์Šค๋ ˆ๋“œ์—๋Š” ํ•ด๋‹น PR์˜ ์ฃผ์„์—์„œ ์ด๋ฏธ ์–ธ๊ธ‰๋˜์ง€ ์•Š์€ ์œ ์šฉํ•œ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒฝ์šฐ(PR์ด ์ข…๋ฃŒ๋˜๊ณ  ์™„๋ฃŒ๋˜์ง€ ์•Š์Œ) ์ถ”๊ฐ€ API ์„ค๋ช…๊ณผ ํ•จ๊ป˜ ์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ƒˆ ๋ฌธ์ œ๋ฅผ ์—ด โ€‹โ€‹์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@elhigu ์•Œ๋ ค ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๋‚˜๋Š” ๊ทธ ์Šค๋ ˆ๋“œ๋ฅผ ๋ชฐ๋ž๋‹ค. API์— ๋Œ€ํ•œ upsert๊ฐ€ ์ง„ํ–‰ ์ค‘์ด๋ผ๋Š” ์†Œ์‹์„ ๋“ฃ๊ฒŒ ๋˜์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค. 6๊ฐœ์›” ์ „์— 802 ํ…Œ์ŠคํŠธ ์ค‘ 1๊ฐœ์— ์‹คํŒจํ•˜์—ฌ travis-ci๋ฅผ ํ†ต๊ณผํ•˜์ง€ ๋ชปํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด knex API์˜ ์ผ๋ถ€๊ฐ€ ๋˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ ์‹คํŒจํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค 1๊ฐœ์ž…๋‹ˆ๊นŒ?

@timhuff ์ดˆ๊ธฐ ๊ตฌํ˜„๋งŒ ์™„๋ฃŒ๋˜์—ˆ์œผ๋ฏ€๋กœ ์™„์ „ํžˆ ๋‹ค์‹œ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ PR์˜ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ๋Œ€๋ถ€๋ถ„์˜ ๋ฐฉ์–ธ์—์„œ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณตํ†ต API ๋””์ž์ธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๊ธฐ๋Šฅ์€ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ํ•ด๋‹น API๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•  ๋•Œ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์•„๋ฌด๋„ ๊ทธ๋ ‡๊ฒŒ ํ•˜์ง€ ์•Š๊ณ  ์–ธ์  ๊ฐ€ ๋‚˜์—๊ฒŒ ์—ฌ๋ถ„์˜ ์‹œ๊ฐ„์ด ์ƒ๊ธฐ๊ฑฐ๋‚˜ ์ ˆ์‹คํžˆ ํ•„์š”ํ•˜๋‹ค๋ฉด ๋‚ด๊ฐ€ ์ง์ ‘ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ (์—…๋ฐ์ดํŠธ์— ์ฐธ์—ฌํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„) knex๊ฐ€ ์–ป๊ณ ์ž ํ•˜๋Š” ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

@elhigu ์ž‘์„ฑํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„์ด ์ข€ ๋” ์žˆ์œผ๋ฉด ์—ฌ๊ธฐ์—์„œ ์ง„ํ–‰ ์ƒํ™ฉ์„ ์ฝ์–ด๋ด์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ๋„์›€์ด ๋˜๋Š”์ง€ ์•„๋‹ˆ๋ฉด ์ œ๊ฐ€ ๋ฉ์ฒญํ•œ ๋†ˆ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ @timhuff ์˜ ์†”๋ฃจ์…˜์— ๋Œ€ํ•ด ์ฟผ๋ฆฌ ๊ตฌ๋ฌธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ œ์•ฝ ์กฐ๊ฑด์„ ๋”ฐ์˜ดํ‘œ๋กœ ๋ฌถ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

const contraint = '("a", "b")'

๋‚˜๋Š” knex ๊ธฐ๋ฐ˜ Postgres upsert๋ฅผ ์ฐพ์•„ ์ด ๋ฌธ์ œ์— ์—ฌ๋Ÿฌ ๋ฒˆ ์™”์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ด๊ฒƒ์„ ํ•„์š”๋กœ ํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‹จ์ผ ๋ฐ ๋ณตํ•ฉ ๊ณ ์œ  ํ‚ค์— ๋Œ€ํ•ด ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.

์„ค์ •

์•„๋ž˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ด๋ธ”์— ๊ณ ์œ  ํ‚ค ์ œ์•ฝ ์กฐ๊ฑด์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๋ณตํ•ฉ ํ‚ค ์ œ์•ฝ ์กฐ๊ฑด์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

table.unique(['a', 'b'])

ํ•จ์ˆ˜

(ํŽธ์ง‘: ์›์‹œ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋„๋ก ์—…๋ฐ์ดํŠธ๋จ)

const upsert = (params)=> {
  const {table, object, constraint} = params;
  const insert = knex(table).insert(object);
  const update = knex.queryBuilder().update(object);
  return knex.raw(`? ON CONFLICT ${constraint} DO ? returning *`, [insert, update]).get('rows').get(0);
};

์šฉ๋ฒ•

const objToUpsert = {a:1, b:2, c:3}

upsert({
  table: 'test',
  object: objToUpsert,
  constraint: '(a, b)',
})

์ œ์•ฝ ์กฐ๊ฑด์ด ํ•ฉ์„ฑ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋‹น์—ฐํžˆ ํ•œ ์ค„์€ constraint: '(a)' ์ž…๋‹ˆ๋‹ค.

์—…๋ฐ์ดํŠธ๋œ ๊ฐœ์ฒด ๋˜๋Š” ์‚ฝ์ž…๋œ ๊ฐœ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋ณตํ•ฉ nullable ์ธ๋ฑ์Šค์— ๋Œ€ํ•œ ์ฐธ๊ณ  ์‚ฌํ•ญ

๋ณตํ•ฉ ์ธ๋ฑ์Šค (a,b) ๊ฐ€ ์žˆ๊ณ  b ์ด nullable์ด๋ฉด (1, NULL) ๋ฐ (1, NULL) ๊ฐ’์€ Postgres์—์„œ ์ƒํ˜ธ ๊ณ ์œ ํ•œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค(์ดํ•ดํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์–ด๋Š ํ•˜๋‚˜). ์ด๊ฒƒ์ด ์‚ฌ์šฉ ์‚ฌ๋ก€์ธ ๊ฒฝ์šฐ ๋ถ€๋ถ„ ๊ณ ์œ  ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“  ๋‹ค์Œ upsert ์ „์— null์„ ํ…Œ์ŠคํŠธํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ œ์•ฝ ์กฐ๊ฑด์„ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ถ€๋ถ„ ๊ณ ์œ  ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. CREATE UNIQUE INDEX unique_index_name ON table (a) WHERE b IS NULL . ํ…Œ์ŠคํŠธ์—์„œ b ๊ฐ€ null์ธ ๊ฒƒ์œผ๋กœ ํ™•์ธ๋˜๋ฉด upsert์—์„œ constraint: '(a) WHERE b IS NULL' ์ œ์•ฝ ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. a ๋„ nullable์ด๋ฉด 3๊ฐœ์˜ ๊ณ ์œ  ์ธ๋ฑ์Šค์™€ 4๊ฐœ์˜ if/else ๋ถ„๊ธฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค(๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ ํ™•์‹คํ•˜์ง€ ์•Š์Œ).

๋‹ค์Œ์€ ์ปดํŒŒ์ผ๋œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ž…๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๊ฒƒ์„ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. @elhigu ~ knex().update(object) ์‚ฌ์šฉ์— ๋Œ€ํ•œ ์˜๊ฒฌ ?~ (ํŽธ์ง‘: Nevermind - ๊ฒฝ๊ณ ๋ฅผ ๋ณด์•˜์Œ - ์ง€๊ธˆ knex.queryBuilder() ์‚ฌ์šฉ)

์ผ๋ถ€ ๊ด€๋ จ ์—†๋Š” ํ† ๋ก ์„ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค(promise/thenables ์ž‘๋™ ๋ฐฉ์‹์— ๋Œ€ํ•ด).

์ด ์ถ”๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๊นŒ?

์•„๋‹ˆ์š”. https://github.com/knex/knex/issues/3186 ์— ๊ธฐ๋Šฅ ์š”์ฒญ ๋ฐ ์‚ฌ์–‘์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰