与pg-promise的嵌套事务

我正在使用NodeJS,PostgreSQL和令人惊叹的pg-promise库。 就我而言,我想执行三个主要的查询:

  1. 在表格的“推文”中插入一条推文。
  2. 如果tweet中有hashtags,将它们插入到另一个表“hashtags”
  3. 他们将tweet和hashtag链接到第三个表“hashtagmap”(多对多关系表)

以下是请求正文(JSON)的示例:

{ "id":"12344444", "created_at":"1999-01-08 04:05:06 -8:00", "userid":"@postman", "tweet":"This is the first test from postman!", "coordinates":"", "favorite_count":"0", "retweet_count":"2", "hashtags":{ "0":{ "name":"test", "relevancetraffic":"f", "relevancedisaster":"f" }, "1":{ "name":"postman", "relevancetraffic":"f", "relevancedisaster":"f" }, "2":{ "name":"bestApp", "relevancetraffic":"f", "relevancedisaster":"f" } } 

上面的所有字段都应该包含在表格“推文”之外,而标签又应该包含在表格“标签”中。

下面是我使用的代码,基于NodeJS模块中来自pg-promise文档的嵌套事务。 我想我需要嵌套事务,因为我需要知道tweet_idhashtag_id为了链接他们在hashtagmap表中。

 // Columns var tweetCols = ['id','created_at','userid','tweet','coordinates','favorite_count','retweet_count']; var hashtagCols = ['name','relevancetraffic','relevancedisaster']; //pgp Column Sets var cs_tweets = new pgp.helpers.ColumnSet(tweetCols, {table: 'tweets'}); var cs_hashtags = new pgp.helpers.ColumnSet(hashtagCols, {table:'hashtags'}); return{ // Transactions add: body => rep.tx(t => { return t.one(pgp.helpers.insert(body,cs_tweets)+" ON CONFLICT(id) DO UPDATE SET coordinates = "+body.coordinates+" RETURNING id") .then(tweet => { var queries = []; for(var i = 0; i < body.hashtags.length; i++){ queries.push( t.tx(t1 => { return t1.one(pgp.helpers.insert(body.hashtags[i],cs_hashtags) + "ON CONFLICT(name) DO UPDATE SET fool ='f' RETURNING id") .then(hash =>{ t1.tx(t2 =>{ return t2.none("INSERT INTO hashtagmap(tweetid,hashtagid) VALUES("+tweet.id+","+hash.id+") ON CONFLICT DO NOTHING"); }); }); })); } return t.batch(queries); }); }) } 

问题是这个代码我能够成功地插入推文,但没有任何反应。 我不能插入标签,也不能将标签链接到推文。

对不起,但我是新来的编码,所以我想我不明白如何正确地从交易中返回,以及如何执行这个简单的任务。 希望您能够帮助我。

先谢谢你。

吉恩

Jean Phelippe自己的回答得到改善:

 // Columns var tweetCols = ['id', 'created_at', 'userid', 'tweet', 'coordinates', 'favorite_count', 'retweet_count']; var hashtagCols = ['name', 'relevancetraffic', 'relevancedisaster']; //pgp Column Sets var cs_tweets = new pgp.helpers.ColumnSet(tweetCols, {table: 'tweets'}); var cs_hashtags = new pgp.helpers.ColumnSet(hashtagCols, {table: 'hashtags'}); return { /* Tweets */ // Add a new tweet and update the corresponding hash tags add: body => db.tx(t => { return t.one(pgp.helpers.insert(body, cs_tweets) + ' ON CONFLICT(id) DO UPDATE SET coordinates = ' + body.coordinates + ' RETURNING id') .then(tweet => { var queries = Object.keys(body.hashtags).map((_, idx) => { return t.one(pgp.helpers.insert(body.hashtags[i], cs_hashtags) + 'ON CONFLICT(name) DO UPDATE SET fool = $1 RETURNING id', 'f') .then(hash => { return t.none('INSERT INTO hashtagmap(tweetid, hashtagid) VALUES($1, $2) ON CONFLICT DO NOTHING', [+tweet.id, +hash.id]); }); }); return t.batch(queries); }); }) .then(data => { // transaction was committed; // data = [null, null,...] as per t.none('INSERT INTO hashtagmap... }) .catch(error => { // transaction rolled back }) }, 

笔记:

  • 正如我之前的笔记所说,你必须把所有的查询都链接起来,否则你最终会得到宽松的承诺
  • 远离嵌套事务,除非你明白他们是如何在PostgreSQL中工作的( 阅读这个 ,特别是Limitations部分)。
  • 避免手动查询格式化,这是不安全的,总是依靠库的查询格式。
  • 除非你在其他地方传递事务的结果,否则你至less应该提供.catch处理程序。

PS对于像+tweet.id这样的语法,它和parseInt(tweet.id)是一样的,只是更短一些,以防这些是string;)

对于那些将面临类似问题的人,我会公布答案。

首先,我的错误:

  1. 在for循环中: body.hashtag.length不存在,因为我正在处理一个对象(这里非常基本的错误)。 改为Object.keys(body.hashtags).length
  2. 为什么使用这么多的交易? 接下来的答案是: 与pg-promise相互依存的交易我删除了额外的交易。 目前还不清楚如何打开一个事务,并在同一事务中将一个查询的结果用于另一个查询。

这是最后的代码:

  // Columns var tweetCols = ['id','created_at','userid','tweet','coordinates','favorite_count','retweet_count']; var hashtagCols = ['name','relevancetraffic','relevancedisaster']; //pgp Column Sets var cs_tweets = new pgp.helpers.ColumnSet(tweetCols, {table: 'tweets'}); var cs_hashtags = new pgp.helpers.ColumnSet(hashtagCols, {table:'hashtags'}); return { /* Tweets */ // Add a new tweet and update the corresponding hashtags add: body => rep.tx(t => { return t.one(pgp.helpers.insert(body,cs_tweets)+" ON CONFLICT(id) DO UPDATE SET coordinates = "+body.coordinates+" RETURNING id") .then(tweet => { var queries = []; for(var i = 0; i < Object.keys(body.hashtags).length; i++){ queries.push( t.one(pgp.helpers.insert(body.hashtags[i],cs_hashtags) + "ON CONFLICT(name) DO UPDATE SET fool ='f' RETURNING id") .then(hash =>{ t.none("INSERT INTO hashtagmap(tweetid,hashtagid) VALUES("+tweet.id+","+hash.id+") ON CONFLICT DO NOTHING"); }) ); } return t.batch(queries); }); }),