提交 e44f75e2 编写于 作者: 雪洛's avatar 雪洛

docs: uniCloud jql

上级 03a6644a
......@@ -363,6 +363,11 @@ const res = await db.collection('goods').where(`${new RegExp(searchVal, 'i')}.te
### 联表查询@lookup
为方便文档描述定义以下两个概念:
临时表:getTemp方法返回的结果,例:`const article = db.collection('article').getTemp() `,此处 article 就是一个临时表
虚拟表:主表与副表联表产生的表,例:`db.collection(article, 'comment').get()`
> JQL于2021年4月28日优化了联表查询策略,详情参考:[联表查询策略调整](https://ask.dcloud.net.cn/article/38966)
`JQL`提供了更简单的联表查询方案。不需要学习join、lookup等复杂方法。
......@@ -372,10 +377,10 @@ const res = await db.collection('goods').where(`${new RegExp(searchVal, 'i')}.te
JQL联表查询有以下两种写法:
```js
// 直接关联多个表为虚拟表再进行查询
// 直接关联多个表为虚拟表再进行查询,旧写法,目前更推荐使用getTemp进行联表查询
const res = await db.collection('order,book').where('_id=="1"').get() // 直接关联order和book之后再过滤
// 使用getTemp先过滤处理获取临时表再联表查询
// 使用getTemp先过滤处理获取临时表再联表查询,推荐用法
const order = db.collection('order').where('_id=="1"').getTemp() // 注意结尾的方法是getTemp,对order表过滤得到临时表
const res = await db.collection(order, 'book').get() // 将获取的order表的临时表和book表进行联表查询
```
......@@ -520,9 +525,10 @@ schema保存后,即使用JQL查询。查询表设为order和book这2个表名
```js
// 客户端联表查询
const db = uniCloud.database()
db.collection('order,book') // 注意collection方法内需要传入所有用到的表名,用逗号分隔,主表需要放在第一位
const order = db.collection('order').field('book_id,quantity').getTemp() // 临时表field方法内需要包含关联字段,否则无法建立关联关系
const book = db.collection('book').field('_id,title,author').getTemp() // 临时表field方法内需要包含关联字段,否则无法建立关联关系
db.collection(order, book) // 注意collection方法内需要传入所有用到的表名,用逗号分隔,主表需要放在第一位
.where('book_id.title == "三国演义"') // 查询order表内书名为“三国演义”的订单
.field('book_id{title,author},quantity') // 这里联表查询book表返回book表内的title、book表内的author、order表内的quantity
.get()
.then(res => {
console.log(res);
......@@ -639,6 +645,145 @@ db.collection('order,book').get()
- 上述示例中如果order表的`book_id`字段是数组形式存放多个book_id,也跟上述写法一致,JQL会自动根据字段类型进行联表查询
- 各个表的_id字段会默认带上,即使没有指定返回
#### 临时表联表查询@lookup-with-temp
> 新增于`HBuilderX 3.2.6`
在此之前JQL联表查询只能直接使用虚拟表,而不能先对主表、副表过滤再生成虚拟表。由于生成虚拟表时需要整个主表和副表进行联表,在数据量大的情况下性能会很差。
使用临时表进行联表查询,可以先对主表或者副表进行过滤,然后在处理后的临时表的基础上生成虚拟表。
仍以上面article、comment两个表为例
获取article_id为'1'的文章及其评论的数据库操作,在直接联表查询和使用临时表联表查询时写法分别如下
```js
// 直接使用虚拟表查询
const res = await db.collection('article,comment')
.where('article_id._value=="1"')
.get()
// 先过滤article表,再获取虚拟表联表获取评论
const article = db.collection('article').where('article_id=="1"').getTemp() // 注意是getTemp不是get
const res = await db.collection(article, 'comment').get()
```
直接使用虚拟表联表查询,在第一步生成虚拟表时会以主表所有数据和副表进行联表查询,如果主表数据量很大,这一步会浪费相当多的时间。先过滤主表则没有这个问题,过滤之后仅有一条数据和副表进行联表查询。
**临时表内可以使用如下方法**
> 方法调用必须严格按照顺序,比如field不能放在where之前
```js
where
field // 关于field的使用限制见下方说明
orderBy
skip
limit
```
```js
const article = db.collection('article').where('article_id=="1"').field('title').getTemp() // 此处过滤article表,仅保留title字段。会导致下一步查询时找不到关联关系而查询失败
const res = await db.collection(article, 'comment').get()
```
**组合出来的虚拟表查询时可以使用的方法**
> 方法调用必须严格按照顺序,比如foreignKey不能放在where之后
```js
foreignKey // foreignKey自 HBuilderX 3.3.7版本支持
where
field // 关于field的使用限制见下方说明
orderBy
skip
limit
```
一般情况下不需要再对虚拟表额外处理,因为数据在临时表内已经进行了过滤排序等操作。以下代码仅供演示,并无实际意义
```js
const article = db.collection('article').getTemp()
const comment = db.collection('comment').getTemp()
const res = await db.collection(article, comment).orderBy('title desc').get() // 按照title倒序排列
```
**field使用限制**
- `HBuilderX 3.3.7`之前 field 内仅可以进行字段过滤,不可对字段重命名、进行运算,`field('name as value')``field('add(score1, score2) as totalScore')`都是不支持的用法
- `HBuilderX 3.3.7`及以上版本支持对字段重命名或运算
- 进行联表查询时仅能使用临时表内已经过滤的字段间的关联关系,例如上面article、comment的查询,如果换成以下写法就无法联表查询
- 不建议在虚拟表内再对副表字段重命名或者运算,如果有此类需求应在临时表内进行,会出现预期之外的结果,**为兼容旧版此用法仅输出警告不会抛出错误**
**权限校验**
要求组成虚拟表的各个临时表都要满足权限限制,即权限校验不会计算组合成虚拟表之后使用的where、field
以下为一个订单表(order)和书籍表(book)的schema示例
```js
// order schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": "doc.uid==auth.uid",
"create": false,
"update": false,
"delete": false
},
"properties": {
"id": { // 订单id
"bsonType": "string"
},
"book_id": { // 书籍id
"bsonType": "string"
},
"uid": { // 用户id
"bsonType": "string"
}
}
}
// book schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": true,
"create": false,
"update": false,
"delete": false
},
"properties": {
"id": { // 书籍id
"bsonType": "string"
},
"name": { // 书籍名称
"bsonType": "string"
}
}
}
```
如果先对主表进行过滤`where('uid==$cloudEnv_uid')`,则能满足权限限制(`order表的"doc.uid==auth.uid"`
```js
const order = db.collection('order')
.where('uid==$cloudEnv_uid') // 先过滤order表内满足条件的部分
.getTemp()
const res = await db.collection(order, 'book').get() // 可以通过权限校验
```
如果不对主表过滤,而是对虚拟表(联表结果)进行过滤,则无法满足权限限制(`order表的"doc.uid==auth.uid"`
```js
const order = db.collection('order').getTemp()
const res = await db.collection(order, 'book').where('uid==$cloudEnv_uid').get() // 对虚拟表过滤,无法通过权限校验
```
#### 设置字段别名@lookup-field-alias
联表查询时也可以在field内对字段进行重命名,写法和简单查询时别名写法类似,`原字段名 as 新字段名`即可。[简单查询时的字段别名](uniCloud/jql.md?id=alias)
......@@ -648,9 +793,11 @@ db.collection('order,book').get()
```js
// 客户端联表查询
const db = uniCloud.database()
db.collection('order,book')
.where('book_id.title == "三国演义"')
.field('book_id{title as book_title,author as book_author},quantity as order_quantity')
const order = db.collection('order').field('book_id,quantity').getTemp()
const book = db.collection('book').field('_id,title as book_title,author as book_author').getTemp()
db.collection(order, book)
.where('book_id.book_title == "三国演义"') // 如果field内对副表字段title进行了重命名,where方法内则需要使用重命名之后的字段名
.get()
.then(res => {
console.log(res);
......@@ -689,56 +836,6 @@ db.collection('order,book')
> 2021年4月28日10点前此方法仅用于兼容JQL联表查询策略调整前后的写法,在此日期后更新的clientDB(上传schema、uni-id均会触发更新)才会有指定foreignKey的功能,关于此次调整请参考:[联表查询策略调整](https://ask.dcloud.net.cn/article/38966)
```js
db.collection('comment,user')
.where('comment_id=="1-1"')
.field('content,sender,receiver.name')
.foreignKey('comment.receiver') // 仅使用comment表内receiver字段下的foreignKey进行主表和副表之间的关联
.get()
```
#### 副表foreignKey联查@st-foreign-key
`2021年4月28日`之前的JQL只支持主表的foreignKey,把副表内容嵌入主表的foreignKey字段下面。不支持处理副本的foreignKey。
`2021年4月28日`调整后,新版支持副表foreignKey联查。副表的数据以数组的方式嵌入到主表中作为一个虚拟表使用。
**关联查询后的数据结构如下:**
> 通过HBuilderX提供的[JQL数据库管理](uniCloud/jql-runner.md)功能方便的查看联表查询时的虚拟表结构
主表某字段foreignKey指向副表时
```js
{
"主表字段名1": "xxx",
"主表字段名2": "xxx",
"主表内foreignKey指向副表的字段名": [{
"副表字段名1": "xxx",
"副表字段名2": "xxx",
}]
}
```
副表某字段foreignKey指向主表时
```js
{
"主表字段名1": "xxx",
"主表字段名2": "xxx",
"副表foreignKey指向的主表字段名": {
"副表1表名": [{ // 一个主表字段可能对应多个副表字段的foreignKey
"副表1字段名1": "xxx",
"副表1字段名2": "xxx",
}],
"副表2表名": [{ // 一个主表字段可能对应多个副表字段的foreignKey
"副表2字段名1": "xxx",
"副表2字段名2": "xxx",
}],
"_value": "主表字段原始值" // 使用副表foreignKey查询时会在关联的主表字段内以_value存储该字段的原始值,新增于HBuilderX 3.1.16-alpha
}
}
```
例:
......@@ -861,11 +958,67 @@ db.collection('comment,user')
}
```
```js
const comment = db.collection('comment').where('comment_id == "1-1"').getTemp()
const user = db.collection('user').field('uid,name').getTemp()
db.collection(comment, user)
.foreignKey('comment.receiver') // 仅使用comment表内receiver字段下的foreignKey进行主表和副表之间的关联
.get()
```
**注意**
- `HBuilderX 3.3.7`及以上版本支持使用getTemp的虚拟表内使用foreignKey方法
#### 副表foreignKey联查@st-foreign-key
`2021年4月28日`之前的JQL只支持主表的foreignKey,把副表内容嵌入主表的foreignKey字段下面。不支持处理副本的foreignKey。
`2021年4月28日`调整后,新版支持副表foreignKey联查。副表的数据以数组的方式嵌入到主表中作为一个虚拟表使用。
**关联查询后的数据结构如下:**
> 通过HBuilderX提供的[JQL数据库管理](uniCloud/jql-runner.md)功能方便的查看联表查询时的虚拟表结构
主表某字段foreignKey指向副表时
```js
{
"主表字段名1": "xxx",
"主表字段名2": "xxx",
"主表内foreignKey指向副表的字段名": [{
"副表字段名1": "xxx",
"副表字段名2": "xxx",
}]
}
```
副表某字段foreignKey指向主表时
```js
{
"主表字段名1": "xxx",
"主表字段名2": "xxx",
"副表foreignKey指向的主表字段名": {
"副表1表名": [{ // 一个主表字段可能对应多个副表字段的foreignKey
"副表1字段名1": "xxx",
"副表1字段名2": "xxx",
}],
"副表2表名": [{ // 一个主表字段可能对应多个副表字段的foreignKey
"副表2字段名1": "xxx",
"副表2字段名2": "xxx",
}],
"_value": "主表字段原始值" // 使用副表foreignKey查询时会在关联的主表字段内以_value存储该字段的原始值,新增于HBuilderX 3.1.16-alpha
}
}
```
以下查询使用comment表的article字段对应的foreignKey进行关联查询
```js
db.collection('article,comment')
.where('article_id._value=="1"')
const article = db.collection('article').where('article_id == "1"').getTemp()
const comment = db.collection('comment').getTemp()
db.collection(article,comment)
.field('content,article_id')
.get()
```
......@@ -899,19 +1052,20 @@ db.collection('article,comment')
```js
// 过滤副表字段
db.collection('article,comment')
.where('article_id._value=="1"')
.field('content,article_id{comment{content}}')
.get()
const article = db.collection('article').where('article_id == "1"').getTemp()
const comment = db.collection('comment').field('article,content').getTemp() // 如果有field方法,field内需包含关联字段以建立关联关系
db.collection('article,comment').get()
// 查询结果如下
[{
"content": "content1",
"article_id": {
"comment": [{ // 使用副表foreignKey联查时此处会自动插入一层副表表名
"article": "1",
"content": "comment1-1"
},
{
"article": "1",
"content": "comment1-2"
}],
"_value": "1"
......@@ -922,171 +1076,30 @@ db.collection('article,comment')
副表内的字段也可以使用`as`进行重命名,例如上述查询中如果希望将副表的content重命名为value可以使用如下写法
> HBuilderX 3.3.7及以上版本支持getTemp内使用as
```js
// 重命名副表字段
db.collection('article,comment')
.where('article_id._value=="1"')
.field('content,article_id{comment{content as value}}')
.get()
const article = db.collection('article').where('article_id == "1"').getTemp()
const comment = db.collection('comment').field('article,content as value').getTemp() // 如果有field方法,field内需包含关联字段以建立关联关系
db.collection(article,comment).get()
// 查询结果如下
[{
"content": "content1",
"article_id": {
"comment": [{ // 使用副本foreignKey联查时此处会自动插入一层副表表名
"article": "1",
"value": "comment1-1"
},
{
"article": "1",
"value": "comment1-2"
}]
}
}]
```
#### 临时表联表查询@lookup-with-temp
> 新增于`HBuilderX 3.2.6`
为方便文档描述定义以下两个概念:
临时表:getTemp方法返回的结果,例:`const article = db.collection('article').getTemp() `,此处 article 就是一个临时表
虚拟表:主表与副表联表产生的表,例:`db.collection(article, 'comment').get()`
在此之前JQL联表查询只能直接使用虚拟表,而不能先对主表、副表过滤再生成虚拟表。由于生成虚拟表时需要整个主表和副表进行联表,在数据量大的情况下性能会很差。
使用临时表进行联表查询,可以先对主表或者副表进行过滤,然后在处理后的临时表的基础上生成虚拟表。
仍以上面article、comment两个表为例
获取article_id为'1'的文章及其评论的数据库操作,在直接联表查询和使用临时表联表查询时写法分别如下
```js
// 直接使用虚拟表查询
const res = await db.collection('article,comment')
.where('article_id._value=="1"')
.get()
// 先过滤article表,再获取虚拟表联表获取评论
const article = db.collection('article').where('article_id=="1"').getTemp() // 注意是getTemp不是get
const res = await db.collection(article, 'comment').get()
```
直接使用虚拟表联表查询,在第一步生成虚拟表时会以主表所有数据和副表进行联表查询,如果主表数据量很大,这一步会浪费相当多的时间。先过滤主表则没有这个问题,过滤之后仅有一条数据和副表进行联表查询。
**临时表内可以使用如下方法**
> 方法调用必须严格按照顺序,比如field不能放在where之前
```js
where
field // 关于field的使用限制见下方说明
orderBy
skip
limit
```
```js
const article = db.collection('article').where('article_id=="1"').field('title').getTemp() // 此处过滤article表,仅保留title字段。会导致下一步查询时找不到关联关系而查询失败
const res = await db.collection(article, 'comment').get()
```
**组合出来的虚拟表查询时可以使用的方法**
> 方法调用必须严格按照顺序,比如foreignKey不能放在where之后
```js
foreignKey // foreignKey自 HBuilderX 3.3.7版本支持
where
field // 关于field的使用限制见下方说明
orderBy
skip
limit
```
一般情况下不需要再对虚拟表额外处理,因为数据在临时表内已经进行了过滤排序等操作。以下代码仅供演示,并无实际意义
```js
const article = db.collection('article').getTemp()
const comment = db.collection('comment').getTemp()
const res = await db.collection(article, comment).orderBy('title desc').get() // 按照title倒序排列
```
**field使用限制**
- `HBuilderX 3.3.7`之前 field 内仅可以进行字段过滤,不可对字段重命名、进行运算,`field('name as value')``field('add(score1, score2) as totalScore')`都是不支持的用法
- `HBuilderX 3.3.7`及以上版本支持对字段重命名或运算
- 进行联表查询时仅能使用临时表内已经过滤的字段间的关联关系,例如上面article、comment的查询,如果换成以下写法就无法联表查询
- 不建议在虚拟表内再对副表字段重命名或者运算,如果有此类需求应在临时表内进行,会出现预期之外的结果,**为兼容旧版此用法仅输出警告不会抛出错误**
**权限校验**
要求组成虚拟表的各个临时表都要满足权限限制,即权限校验不会计算组合成虚拟表之后使用的where、field
以下为一个订单表(order)和书籍表(book)的schema示例
```js
// order schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": "doc.uid==auth.uid",
"create": false,
"update": false,
"delete": false
},
"properties": {
"id": { // 订单id
"bsonType": "string"
},
"book_id": { // 书籍id
"bsonType": "string"
},
"uid": { // 用户id
"bsonType": "string"
}
}
}
// book schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": true,
"create": false,
"update": false,
"delete": false
},
"properties": {
"id": { // 书籍id
"bsonType": "string"
},
"name": { // 书籍名称
"bsonType": "string"
}
}
}
```
如果先对主表进行过滤`where('uid==$cloudEnv_uid')`,则能满足权限限制(`order表的"doc.uid==auth.uid"`
```js
const order = db.collection('order')
.where('uid==$cloudEnv_uid') // 先过滤order表内满足条件的部分
.getTemp()
const res = await db.collection(order, 'book').get() // 可以通过权限校验
```
如果不对主表过滤,而是对虚拟表(联表结果)进行过滤,则无法满足权限限制(`order表的"doc.uid==auth.uid"`
```js
const order = db.collection('order').getTemp()
const res = await db.collection(order, 'book').where('uid==$cloudEnv_uid').get() // 对虚拟表过滤,无法通过权限校验
```
### 查询记录过滤where条件@where
> 代码块`dbget`
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册