【问题标题】:knex js query many to manyknex js查询多对多
【发布时间】:2020-07-30 09:55:51
【问题描述】:

我在使用 node 和 knex.js 时遇到问题

我正在尝试构建一个迷你博客,其中包含帖子和添加功能以添加多个标签来发布

我有一个具有以下属性的 POST 模型

id SERIAL PRIMARY KEY NOT NULL,
name TEXT,

其次,我有 标签模型 用于存储标签:

id SERIAL PRIMARY KEY NOT NULL,
 name TEXT

我有许多表:发布标签,它引用了帖子和标签:

 id SERIAL PRIMARY KEY NOT NULL,
 post_id INTEGER NOT NULL REFERENCES posts ON DELETE CASCADE,
 tag_id INTEGER NOT NULL REFERENCES tags ON DELETE CASCADE

我已经设法插入标签,并创建带有标签的帖子, 但是,当我想获取带有附加到该帖子的标签的帖子数据时,我遇到了麻烦

这里有个问题:

 const data = await knex.select('posts.name as postName', 'tags.name as tagName'
            .from('posts')
            .leftJoin('post_tags', 'posts.id', 'post_tags.post_id')
            .leftJoin('tags', 'tags.id', 'post_tags.tag_id')
            .where('posts.id', id)

以下查询返回此结果:

[
  {
    postName: 'Post 1',
    tagName: 'Youtube',
  },
  {
    postName: 'Post 1',
    tagName: 'Funny',
  }
]

但我希望像这样格式化和返回结果:

  {
    postName: 'Post 1',
    tagName: ['Youtube', 'Funny'],
  }

这甚至可以通过查询还是我必须手动格式化数据?

【问题讨论】:

    标签: node.js postgresql knex.js


    【解决方案1】:

    这样做的一种方法是使用某种聚合函数。如果您使用的是 PostgreSQL:

    const data = await knex.select('posts.name as postName', knex.raw('ARRAY_AGG (tags.name) tags'))
        .from('posts')
        .innerJoin('post_tags', 'posts.id', 'post_tags.post_id')
        .innerJoin('tags', 'tags.id', 'post_tags.tag_id')
        .where('posts.id', id)
        .groupBy("postName")
        .orderBy("postName")
        .first();
    

    ->

    { postName: 'post1', tags: [ 'tag1', 'tag2', 'tag3' ] }
    

    对于 MySQL:

    const data = await knex.select('posts.name as postName', knex.raw('GROUP_CONCAT (tags.name) as tags'))
        .from('posts')
        .innerJoin('post_tags', 'posts.id', 'post_tags.post_id')
        .innerJoin('tags', 'tags.id', 'post_tags.tag_id')
        .where('posts.id', id)
        .groupBy("postName")
        .orderBy("postName")
        .first()
        .then(res => Object.assign(res, { tags: res.tags.split(',')}))
    

    MySQL中没有数组,GROUP_CONCAT只是将所有标签拼接成一个字符串,所以我们需要手动拆分。

    ->

    RowDataPacket { postName: 'post1', tags: [ 'tag1', 'tag2', 'tag3' ] }
    

    【讨论】:

    • 很好的答案,可能会使用这个,thanx 来回答,
    【解决方案2】:

    结果是正确的,因为这就是 SQL 的工作方式 - 它返回数据行。 SQL 没有返回表格以外的任何东西的概念(想想 CSV 数据或 Excel 电子表格)。

    您可以使用 SQL 做一些有趣的事情,将标签转换为您连接在一起的字符串,但这并不是您真正想要的。无论哪种方式,您都需要添加后处理步骤。

    使用您当前的查询,您可以简单地执行以下操作:

    function formatter (result) {
        let set = {};
    
        result.forEach(row => {
            if (set[row.postName] === undefined) {
                set[row.postName] = row;
                set[row.postName].tagName = [set[row.postName].tagName];
            }
            else {
                set[row.postName].tagName.push(row.tagName);
            }
        });
    
        return Object.values(set);
    }
    
    // ...
    
    query.then(formatter);
    

    这应该不会很慢,因为您只循环了一次结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-17
      • 2014-08-24
      • 2011-02-13
      • 1970-01-01
      相关资源
      最近更新 更多