代码之家  ›  专栏  ›  技术社区  ›  Loki

knex js查询多对多

  •  0
  • Loki  · 技术社区  · 5 年前

    我在node&knex。js

    我正在尝试建立一个包含帖子和;添加功能以向帖子添加多个标签

    我有一个 后模型 具有以下特性:

    id SERIAL PRIMARY KEY NOT NULL,
    name TEXT,
    

    第二,我有 标签模型 用于存储标签的:

    id SERIAL PRIMARY KEY NOT NULL,
     name TEXT
    

    我有一张多对多的桌子: 文章标签 这是指post&标签:

     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'],
      }
    

    这在查询中是可能的,还是我必须手动格式化数据?

    0 回复  |  直到 5 年前
        1
  •  2
  •   F'1    5 年前

    一种方法是使用某种聚合函数。如果您使用的是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' ] }
    
        2
  •  1
  •   slebetman    5 年前

    结果是正确的,因为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);
    

    这不应该太慢,因为你只需要循环一次结果。