美文网首页go
【Go Web开发】过滤查询列表

【Go Web开发】过滤查询列表

作者: Go语言由浅入深 | 来源:发表于2022-02-18 23:16 被阅读0次

本节我们将开始使用查询字符串参数,以便用户可以搜索具有特定标题或类型的电影。

具体来说,我们将构建一个简化的过滤器,允许客户端根据电影标题不区分大小写和一个或多个电影类型的精确匹配进行搜索。例如:

// 查询所有movies
/v1/movies

// 查询title内容和'balck panther'匹配的电影
/v1/movies?title=black+panther

// 查询title为moana,电影类型genres包含“animation“和“adventure”。
/v1/movies?title=moana&genres=animation,adventure

上面查询参数中的“+“号,在URL编码中指的是空格符。你也可以使用%20代替。两种方式在URL中都表示空格。

SQL动态过滤

构建动态过滤最困难的部分是创建查询数据库的SQL语句—我们需要在标题和类型上都使用过滤器,或者只在其中一个上使用过滤器。

为了解决这个问题,一种选择是在运行时动态构建SQL查询……为连接或插入到WHERE子句中的每个过滤器提供必要的过滤条件。但是这种方法会使您的代码变得混乱和难以理解,特别是对于需要支持大量过滤器选项的大型查询。

本书中我们选择另一种方法,使用固定的SQL查询:

SELECT id, create_at, title, year, runtime, genres, version
FROM movies
WHERE (LOWER(title) = LOWER($1) OR  $1 = '')
AND (genres @> $2 OR $2 = '{}')
ORDER BY id

这个SQL查询的设计使得每个过滤器的行为都像是“可选的”。例如,条件(LOWER(title) = LOWER($1) OR $1 = '') 占位符$1和数据库中title在不区分大小写情况下匹配或者为空就返回记录。因此,当搜索的电影标题是空字符串“”时,这个过滤条件实际上将被“跳过”。

同样(genres @> $2 OR $2 = '{}')筛选条件,@>是包含的意思。条件返回true,如果占位符$2中的值都包含在数据库genres字段,或者占位符为空也返回true。

你应该记得,在本系列文章的前面,我们设置了listMoviesHandler,使用空字符串“”和空切片作为标题和类型过滤器参数的默认值:

input.Title = app.readString(qs, "title", "") 
input.Genres = app.readCSV(qs, "genres", []string{})

把这些结合在一起,意味着,如果用户没有提供title查询字符串参数,$1占位符的值是空字符串""。SQL查询的过滤条件将返回true,类似跳过过滤操作。genres参数也是如此。

PostgreSQL对数组字段提供了很多操作符,包括&&重叠操作,@<操作符以及array_length()函数。完整列表可以查看这里

下面我们回到internal/data/movies.go文件,更新GetAll()方法,使用这个查询语句:

File:internal/data/movies.go


package data

...

func (m MovieModel)GetAll(title string, genres []string, filters Filters) ([]*Movie, error)  {
    query := `
        SELECT id, create_at, title, year, runtime, genres, version
        FROM movies
        WHERE (LOWER(title) = LOWER($1) OR $1 = '')
        AND (genres @> $2 OR $2 = '{}')
        ORDER BY id`
    //创建3s超时上下文实例
    ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
    defer cancel()
    //使用QueryContext()来执行查询
    rows, err := m.DB.QueryContext(ctx, query, title, pq.Array(genres))
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var movies []*Movie
    for rows.Next(){
        var movie Movie
        err := rows.Scan(
            &movie.ID,
            &movie.CreateAt,
            &movie.Title,
            &movie.Year,
            &movie.Runtime,
            pq.Array(&movie.Genres),
            &movie.Version,
            )
        if err != nil {
            return nil, err
        }
        movies = append(movies, &movie)
    }
    if err = rows.Err(); err != nil {
        return nil, err
    }
    return movies, nil
}

现在让我们重新启动应用程序,并使用之前给出的示例进行测试。如果你一直跟随操作,如下所示:

$ curl "localhost:4000/v1/movies?title=black+panther"
{
        "movies": [
                {
                        "id": 2,
                        "title": "Black Panther",
                        "year": 2018,
                        "runtime": "134 mins",
                        "genres": [
                                "sci-fi",
                                "action",
                                "adventure"
                        ],
                        "Version": 2
                }
        ]
}
$ curl "localhost:4000/v1/movies?genres=adventure"
{
        "movies": [
                {
                        "id": 1,
                        "title": "Moana",
                        "year": 2016,
                        "runtime": "107 mins",
                        "genres": [
                                "animation",
                                "adventure"
                        ],
                        "Version": 1
                },
                {
                        "id": 2,
                        "title": "Black Panther",
                        "year": 2018,
                        "runtime": "134 mins",
                        "genres": [
                                "sci-fi",
                                "action",
                                "adventure"
                        ],
                        "Version": 2
                }
        ]
}
$  curl "localhost:4000/v1/movies?title=moana&genres=animation,adventure"
{
        "movies": [
                {
                        "id": 1,
                        "title": "Moana",
                        "year": 2016,
                        "runtime": "107 mins",
                        "genres": [
                                "animation",
                                "adventure"
                        ],
                        "Version": 1
                }
        ]
}

您还可以发送不匹配任何记录的过滤器请求。在这种情况下,你应该在响应中得到一个空的JSON数组,如下所示:

$  curl "localhost:4000/v1/movies?genres=western"
{
        "movies": null
}

进展很顺利。我们的API接口现在返回经过适当过滤的电影记录,并且我们有一个模式,可以很容易地扩展该模式以便在未来使用其他过滤规则(例如关于电影year或runtime过滤器)。

相关文章

  • 【Go Web开发】过滤查询列表

    本节我们将开始使用查询字符串参数,以便用户可以搜索具有特定标题或类型的电影。 具体来说,我们将构建一个简化的过滤器...

  • 【Go Web开发】查询列表排序

    上一篇文章[https://www.jianshu.com/p/33fc8db3d091]我们介绍了根据关键字搜索...

  • 【Go Web开发】查询数据列表

    接着上一篇文章[https://www.jianshu.com/p/3e1b1558ec56]的内容,实现GET ...

  • Go Web编程.epub

    【下载地址】 《Go Web编程》介绍如何用Go语言进行Web应用的开发,将Go语言的特性与Web开发实战组合到一...

  • 【Go Web开发】查询结果分页

    如果您的接口返回一个包含数百或数千条记录的列表,那么出于性能或可用性的考虑,您可能需要在接口上实现分页—以便在单个...

  • go web开发学习之路

    基础:go 原生web开发1、go web的工作原理2、搭建一个简单的go web站点3、go web中载入静态文...

  • 2018-08-23

    Java Web开发技术应用——过滤器 什么是过滤器:过滤源 ——> 过滤规则 ——> ...

  • go学习计划 2018-07-13

    go语言入门及实战 go web开发 go源码及项目阅读

  • Go race 数据竞争检测

    WEB服务 main.go goods.go 增加 列表 go 启动增加参数 race,检测数据竞争 执行imag...

  • 开发自己的区块链基础功能篇

    准备工作: 安装go开发环境 用go搭建web服务 go语言基础 安装go开发环境 到https://golang...

网友评论

    本文标题:【Go Web开发】过滤查询列表

    本文链接:https://www.haomeiwen.com/subject/zywslrtx.html