Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API 支持多字段&多值的匹配查询能力 #135

Closed
IMBlues opened this issue Nov 3, 2021 · 6 comments · Fixed by #247 or #261
Closed

API 支持多字段&多值的匹配查询能力 #135

IMBlues opened this issue Nov 3, 2021 · 6 comments · Fixed by #247 or #261
Assignees
Labels
Layer: api Api module related Sign: feature requested New feature request Sign: help wanted Extra attention is needed
Milestone

Comments

@IMBlues
Copy link
Contributor

IMBlues commented Nov 3, 2021

目前我们主要通过 restAPI 来实现数据拉取,例如

聚焦单个字段,查询多个值

/api/v2/profiles/?lookup_field=domain&fuzzy_lookups=aa,bb,cc

聚焦多个字段,查询同一个值

/api/v2/profiles/?wildcard_search=aa&wildcard_search_fields=username,display_name

二者针对不同场景覆盖了大部分数据查询需求,当然也有例外。当前存在需求 #73 ,我们要在 SaaS 中通过 API 针对用户的多个字段添加不同的过滤条件,上述两种查询都无法满足。

由于我们采用的 “轻客户端” 的做法,不太适合在 SaaS 中拼接过于复杂的多次查询,所以我们仍旧需要在 API 层完成这样的查询,以 restAPI 的形式来理解,应该长这个样子

/api/v2/profiles/?username=abcd&domain=xyz&category_id=3

但是存在一些明显的问题:

  • 条件之间的关系?交集?并集?
  • request line 长度限制?

所以虽然我们的模型比较简单,但是不影响引入一些其他的查询思路,例如 graphql ? (社区也有 Django graphql 整合)

如果有其他更好的思路,欢迎留言

@IMBlues IMBlues added Sign: help wanted Extra attention is needed Sign: feature requested New feature request Layer: api Api module related labels Nov 3, 2021
@IMBlues IMBlues changed the title API 支持多字段 vs 多值的匹配查询能力 API 支持多字段&多值的匹配查询能力 Nov 3, 2021
@IMBlues IMBlues added this to the 202111H2 milestone Nov 8, 2021
@Xmandon Xmandon modified the milestones: 202111H2, Y2021M45-46 Nov 8, 2021
@Xmandon Xmandon modified the milestones: Y2021M45-46, Y2021M47-48 Nov 23, 2021
@IMBlues
Copy link
Contributor Author

IMBlues commented Nov 29, 2021

https://emergencyexit.xyz/use-graphql-with-django.html

经过一段时间的调研和尝试,觉得当前还不适合引入 GraphQL,我们仍旧走 REST 协议扩展的路子

@IMBlues
Copy link
Contributor Author

IMBlues commented Nov 30, 2021

原有的 v2 版本 API 有一些既有的协议限制,将新启一个 API 版本 (v3) 处理

和 v2 一样,以 profile 对象为例,适用于所有对象

拉取对象列表

支持直接添加字段过滤列表,条件之间仅支持交集

api/v3/profiles/?username=foo&domain=bar

支持类 Django ORM 查询功能

查询字段列表

# 暂不支持查询内容包含 "," 的情景
api/v3/profiles/?username__in=foo,far

模糊查询

api/v3/profiles/?username__icontains=foo
api/v3/profiles/?username__istartswith=foo
api/v3/profiles/?username__iendswith=foo

限制返回字段

通过 _fields,用法和 v2 类似,只是为了避免和对象自身的字段名冲突,增加了下划线

api/v3/profiles/?username__in=foo&_fields=username,display_name

获取单个对象详情

与 v2 不同的是,路径参数不再是动态可选,而是针对不同对象支持有限的枚举,类似 profile 对象仅支持 idusername(with domain),而 department 对象仅支持 id

api/v3/profiles/{id}/
api/v3/profiles/{username}/

多对多过滤

除了普通字段,需要额外支持多对多字段过滤能力,仅支持通过关联对象的 id 查询

# 过滤绑定了 1,2,3 中任一部门的所有 username 为 foo 的用户
api/v3/profiles/?username=foo&departments=1,2,3

# 过滤绑定了 1,2,3 中任一领导的所有用户
api/v3/profiles/?leaders=1,2,3

这里再举一个 department 对象的例子

# 过滤所有和 1,2,3 三个用户有关联的部门
api/v3/departments/?profiles=1,2,3

排序

和 v2 一致,我们默认以 "-create_time" 排序,请求时可以通过 ordering 参数进行修改

api/v3/profiles/?ordering=id

分页

和 v2 不同,相较于 page & size or limit & offset 这两种写法,使用 cursor 更为现代

{
  "results": [...],
  "next":"http://localhost:8004/api/v3/profiles/?cursor=cD0yMDIxLTAxLTI2KzAzJTNBNTQlM0E0MS4yMDQ4NTclMkIwMCUzQTAw&_fields=username",
  "previous": null
}

返回值

返回值将以 Http Status Code 来标识请求状态。正常返回为 200 OK ,已知的异常返回为 400 Bad Request,同时会有展示详情的 body 内容:

{
  "code": "PERMISSION_DENIED",
  "detail": "request failed, please check api log of bk-user"
}

当出现 >499 的状态值时,说明后端出现了未知的异常。

@Xmandon Xmandon modified the milestones: Y2021M47-48, Y2021M49-50 Dec 6, 2021
@piglei
Copy link

piglei commented Dec 13, 2021

一些想法:

  • 如果复用 Django ORM 的语法来做复杂查询,感觉需要先确定一套所支持的最小集,直接完全动态透风险挺高(其他项目可以复用;ordering 参数也要做同样的事情)
  • “多对多” 的关系查询,感觉和普通查询的区分度不太高

@IMBlues
Copy link
Contributor Author

IMBlues commented Dec 13, 2021

一些想法:

  • 如果复用 Django ORM 的语法来做复杂查询,感觉需要先确定一套所支持的最小集,直接完全动态透风险挺高(其他项目可以复用;ordering 参数也要做同样的事情)
  • “多对多” 的关系查询,感觉和普通查询的区分度不太高
  • ORM 查询在实现上肯定不会是直接透传。第一期也不会直接实现 ORM 复杂查询,后期会根据需求逐步开放,这里只是提前约定。
  • 多对多这里是为了明确指出只支持按照 id 过滤,而不能通过其他 key field 查询。

@Xmandon Xmandon modified the milestones: Y2021M49-50, Y2022M01-02 Jan 5, 2022
@IMBlues IMBlues self-assigned this Jan 5, 2022
This was linked to pull requests Jan 12, 2022
@Xmandon Xmandon modified the milestones: Y2022M01-02, Y2022M07-M08 Feb 14, 2022
@Xmandon Xmandon self-assigned this Feb 14, 2022
@IMBlues IMBlues linked a pull request Feb 21, 2022 that will close this issue
@wklken
Copy link
Collaborator

wklken commented Feb 21, 2022

  • cursor 对于调用方, 需要额外处理, 一页页翻页直到最后; 相当于调用其他系统用的page/page_size, 调用用户管理需要另外写一段逻辑
  • 问题: cursor 返回的next会自动拼接原有的所有查询参数?
  • 问题: cursor 如果调用方需要查回来一样做翻页展示, 是否有 total? 以及 能否设定每页要多少条?

@IMBlues
Copy link
Contributor Author

IMBlues commented Feb 21, 2022

  1. 由于是新起的 API 版本,原有功能不受影响,可以对接入方有更多要求,以保证后期开发更顺滑。同时由于我们的 API 经常会被调用方分页遍历全量拉取,cursor 相较于原有的 offset/page_size 更有优势,可以简单看这篇文章: https://uxdesign.cc/why-facebook-says-cursor-pagination-is-the-greatest-d6b98d86b6c0
  2. cursor 默认就是拼接所有参数的,只不过我这里由于是两级构造,直接返回的完整 URL 是基于 API 模块的,SaaS 无法直接使用,所以这里目前简化成只有 cursor。当然也可以在多加一个处理,判断具体请求的来源返回不同的完整 URL
  3. cursor 有 total,也可以通过 page_size 调整

@Xmandon Xmandon closed this as completed Feb 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Layer: api Api module related Sign: feature requested New feature request Sign: help wanted Extra attention is needed
Projects
None yet
4 participants