我正在尝试为帖子构建一个包含帖子和标签的应用。对于这些,我有一个post
,tags
和post_tag
表。 tags
具有我之前定义的标签,并且在应用程序的某个位置向前端的用户建议。 post_tag
表格在每行中包含帖子和标记 ID 。
我使用express.js和postgreql以及pg-promise。
据我所知,我需要一个创建后期操作的事务查询。
另外,当用户创建帖子时,我需要一种机制来检测标记是否不在tags
表中,以便我可以动态插入它,并且我有一个每个标记的tag_id
必须在insertion
和post_id
到tag_id
表的post_tag
中使用。否则,我将有一个foreign key error
,因为我需要post_tag
表格的列post_id
和tag_id
来引用posts
和tags
表id
列。
这是我用于此目的的url函数,到目前为止我还没用过:
privateAPIRoutes.post('/ask', function (req, res) {
console.log('/ask req.body: ', req.body);
// write to posts
var post_id = ''
var post_url = ''
db.query(
`
INSERT INTO
posts (title, text, post_url, author_id, post_type)
VALUES
($(title), $(text), $(post_url), $(author_id), $(post_type))
RETURNING id
`,
{
title: req.body.title,
text: req.body.text,
post_url: slug(req.body.title),
author_id: req.user.id,
post_type: 'question'
} // remember req.user contains decoded jwt saved by mw above.
)
.then(post => {
console.log('/ask post: ', post);
post_id = post.id
post_url = post.post_url
// if tag deos not exist create it here
var tags = req.body.tags;
console.log('2nd block tags1', tags);
for (var i = 0; i < tags.length; i++) {
if (tags[i].id == undefined) {
console.log('req.body.tags[i].id == undefined', tags[i].id);
var q1 = db.query("insert into tags (tag) values ($(tag)) returning id", {tag: tags[i].label})
.then(data => {
console.log('2nd block tags2', tags);
tags[i].id = data[0].id
// write to the post_tag
db.tx(t => {
var queries = [];
for (var j = 0; j < tags.length; j++) {
var query = t.query(
`
INSERT INTO
post_tag (post_id, tag_id)
VALUES
($(post_id), $(tag_id))
`,
{
post_id: post_id,
tag_id: tags[j].id
}
)
queries.push(query);
}
return t.batch(queries)
})
.then(data => {
res.json({post_id: post_id, post_url: post_url})
})
.catch(error => {
console.error(error);
})
})
.catch(error => {
console.error(error);
});
}
}
})
.catch(error => {
console.error(error);
})
});
答案 0 :(得分:5)
您遇到的主要问题 - 您无法在任务或事务中使用根级db
对象。尝试在事务内部创建新连接会破坏事务逻辑。在这种情况下,您需要使用t.tx
。但是,在你的情况下,我根本不认为你需要它。
更正后的代码:
privateAPIRoutes.post('/ask', (req, res) => {
console.log('/ask req.body: ', req.body);
db.tx(t => {
return t.one(
`
INSERT INTO
posts (title, text, post_url, author_id, post_type)
VALUES
($(title), $(text), $(post_url), $(author_id), $(post_type))
RETURNING *
`,
{
title: req.body.title,
text: req.body.text,
post_url: slug(req.body.title),
author_id: req.user.id,
post_type: 'question'
} // remember req.user contains decoded jwt saved by mw above.
)
.then(post => {
console.log('/ask second query: post[0]: ', post);
console.log('/ask second query: tags: ', req.body.tags);
console.log('/ask second query: tags[0]: ', req.body.tags[0]);
// the key piece to the answer:
var tagIds = req.body.tags.map(tag => {
return tag.id || t.one("insert into tags(tag) values($1) returning id", tag.label, a=>a.id);
});
return t.batch(tagIds)
.then(ids => {
var queries = ids.map(id => {
return t.one(
`
INSERT INTO post_tag (post_id, tag_id)
VALUES ($(post_id), $(tag_id))
RETURNING post_id, tag_id
`,
{
post_id: post.id,
tag_id: id
}
)
});
return t.batch(queries);
});
});
})
.then(data => {
// data = result from the last query;
console.log('/api/ask', data);
res.json(data);
})
.catch(error => {
// error
});
});
这里的关键是简单地遍历标记id-s,对于未设置的标记,使用插入。然后通过将数组传递到t.batch
。
其他建议:
one
。try
/ catch
。这与如何使用promises相关,而不仅仅是针对此库要更好地了解条件插入,请参阅SELECT->INSERT