uniCloud JQL语法(一)

JQL概述

JQL,全称 javascript query language,是一种js方式操作数据库的规范。

使用场景
客户端clientDB,包括js内以及unicloud-db组件内
HBuilderX JQL数据库管理器
启用了jql扩展的云函数

不同场景的区别

  • JQL数据库管理器:
    ○ 不会校验任何权限,相当于以数据库管理员的身份执行
    ○ 即使是admin不能读写的password类型数据也可以读写
    ○ 不会触发数据库触发器
    ○ 不可以执行action云函数
  • 客户端clientDB:
    ○ 完整的权限校验,执行操作的用户不可以操作自己权限之外的数据
    ○ admin用户不可操作password类型的数据
  • 云函数JQL:
    ○ 同clientDB,但是password类型的数据可以配置权限,默认权限是false,可以被admin用户操作。
    ○ 可以通过setUser指定当前执行数据库操作的用户身份。

JQL包含的模块

image.png

JQL的限制
○ 会对数据库操作进行序列化,除Date类型、RegExp之外的所有不可JSON序列化的参数类型均不支持(例如:undefined)
○ 为了严格控制权限,禁止使用set方法
○ 为了数据校验能严格限制,更新数据库时不可使用更新操作符db.command.inc等
○ 更新数据时键值不可使用{'a.b.c': 1}的形式,需要写成{a:{b:{c:1}}}形式

客户端调用

const db = uniCloud.database()

// 使用`jql`查询list表内`name`字段值为`hello-uni-app`的记录
db.collection('list')
  .where('name == "hello-uni-app"')
  .get()
  .then((res)=>{
    // res 为数据库查询结果
  }).catch((err)=>{
    // err.message 错误信息
    // err.code 错误码
  })

JQL方法使用限制

下面这些方法必须严格按照下面的顺序进行调用,其他方法需要在这些方法之后调用(不限制顺序)
单表查询collection aggregate geoNear doc where field groupBy groupField
联表查询
临时表:collection geoNear where field orderBy skip limit getTemp
虚拟联表:collection foreignKey where field groupBy groupField distinct orderBy skip limit get

新增数据记录

方法:collection.add(data) 其中data类型object|array
data中不需要包括_id字段,数据库会自动维护该字段。

const db = uniCloud.database();
const collection = db.collection('user');
//单条记录
db.collection('user').add({name:"王五"})
//多条记录
let res = await collection.add([{
  name: '张三'
},{
  name: '李四'
},{
  name: '王五'
}])

删除数据记录

通过指定文档id删除:collection.doc(_id).remove()
比如:

const db = uniCloud.database();
await db.collection("table1").doc("5f79fdb337d16d0001899566").remove()

条件查询删除
collection.where().remove()

// 删除字段a的值大于2的文档
try {
    await db.collection("table1").where("a>2").remove()
} catch (e) {
    uni.showModal({
        title: '提示',
        content: e.message
    })
}

删除所有的数据

const dbCmd = db.command
const db = uniCloud.database();
await db.collection("table1").where({
  _id: dbCmd.neq(null)
}).remove()

更新数据记录

通过指定文档id更新:collection.doc(_id).update(Object data)
条件查询更新

const db = uniCloud.database();
let collection = db.collection("table1")
let res = await collection.where({_id:'doc-id'})
  .update({
    name: "Hey",
    count: {
      fav: 1
    }
  });

内置云端环境变量

image.png

在字符串内使用
db.collection('user').where('_id==$cloudEnv_uid').get()
在对象内使用
db.collection('user').where({ _id: db.getCloudEnv('$cloudEnv_uid') }).get()
使用正则查询
const res = await db.collection('goods').where(`${new RegExp(searchVal, 'i')}.test(name)`).get()

联表查询

临时表:getTemp方法返回的结果,例:const article = db.collection('article').getTemp(),此处 article 就是一个临时表
虚拟联表:主表与副表联表产生的表,例:db.collection(article, 'comment').get()

JQL联表查询有以下两种写法:

// 直接关联多个表为虚拟联表再进行查询,旧写法,目前更推荐使用getTemp进行联表查询
const res = await db.collection('order,book').where('_id=="1"').get()// 直接关联order和book之后再过滤

// 使用getTemp先过滤处理获取临时表再联表查询,推荐用法
const order = db.collection('order').where('_id=="1"').getTemp() // 注意结尾的方法是getTemp,对order表过滤得到临时表
const res = await db.collection(order, 'book').get() // 将获取的order表的临时表和book表进行联表查询

表关联示例:定义order表和book表 schema

// order表schema
{
  "bsonType": "object",
  "required": [],
  "permission": {
    "read": true
  },
  "properties": {
    "book_id": {
      "bsonType": "string",
      "foreignKey": "book._id" // 使用foreignKey表示,此字段关联book表的_id。
    },
    "quantity": {
      "bsonType": "int"
    }
  }
}

// book表schema
{
  "bsonType": "object",
  "required": [],
  "permission": {
    "read": true
  },
  "properties": {
    "title": {
      "bsonType": "string"
    },
    "author": {
      "bsonType": "string"
    }
  }
}

定义json数据

//book表
{
  "_id": "1",
  "title": "西游记",
  "author": "吴承恩"
}
{
  "_id": "2",
  "title": "水浒传",
  "author": "施耐庵"
}
{
  "_id": "3",
  "title": "三国演义",
  "author": "罗贯中"
}
{
  "_id": "4",
  "title": "红楼梦",
  "author": "曹雪芹"
}

//order表
{
  "book_id": "1",
  "quantity": 111
}
{
  "book_id": "2",
  "quantity": 222
}
{
  "book_id": "3",
  "quantity": 333
}
{
  "book_id": "4",
  "quantity": 444
}
{
  "book_id": "3",
  "quantity": 555
}
// 客户端联表查询
const db = uniCloud.database()
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表内书名为“三国演义”的订单
  .get()
  .then(res => {
    console.log(res);
  }).catch(err => {
    console.error(err)
  })

查询结果如下:

{
    "code": "",
    "message": "",
    "data": [{
        "_id": "b8df3bd65f8f0d06018fdc250a5688bb",
        "book_id": [{
            "_id": "3",
            "author": "罗贯中",
            "title": "三国演义"
        }],
        "quantity": 555
    }, {
        "_id": "b8df3bd65f8f0d06018fdc2315af05ec",
        "book_id": [{
            "_id": "3",
            "author": "罗贯中",
            "title": "三国演义"
        }],
        "quantity": 333
    }]
}

存在外键时结果返回的区别

主表某字段foreignKey指向副表时,返回结果格式为:

{
"主表字段名1": "xxx",
"主表字段名2": "xxx",
"主表内foreignKey指向副表的字段名": [{
  "副表字段名1": "xxx",
  "副表字段名2": "xxx",
}]
}

当副表某字段foreignKey指向主表时,其返回结果格式为:

{
  "主表字段名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
  }
}

可简单理解为:
主表foreignKey指向副表时,查询结果:关联字段只包含副表查询结果,不含表名
副表foreignKey指向主表时,查询结果:关联字段中会包含副表的表名和查询结果

临时联表查询
使用临时表进行联表查询,可以先对主表或者副表进行过滤,然后在处理后的临时表的基础上生成虚拟联表。

// 先过滤article表,再获取虚拟联表联表获取评论
const article = db.collection('article').where('article_id=="1"').getTemp() // 注意是getTemp不是get
const res = await db.collection(article, 'comment').get()

设置表字段别名
语法:原字段名 as 新字段名

const db = uniCloud.database()
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);
  }).catch(err => {
    console.error(err)
  })

查询结果如下

{
    "code": "",
    "message": "",
    "data": [{
        "_id": "b8df3bd65f8f0d06018fdc250a5688bb",
        "book_id": [{
            "book_author": "罗贯中",
            "book_title": "三国演义"
        }],
        "order_quantity": 555
    }, {
        "_id": "b8df3bd65f8f0d06018fdc2315af05ec",
        "book_id": [{
            "book_author": "罗贯中",
            "book_title": "三国演义"
        }],
        "order_quantity": 333
    }]
}

where 简单查询

image.png

这里的test方法比较强大,格式为:正则规则.test(fieldname)。
具体到这个正则 /abc/.test(content),类似于sql中的content like '%abc%',即查询所有字段content包含abc的数据记录。
注意
• 不支持非操作
• 编写查询条件时,除test外,均为运算符左侧为数据库字段,右侧为常量
简单查询条件内要求二元运算符两侧不可均为数据库内的字段
上述写法的查询语句可以在权限校验阶段与schema内配置的permission进行一次对比校验,如果校验通过则不会再查库进行权限校验。

复杂查询条件
复杂查询内可以使用数据库运算方法
示例:数据表test内有以下数据

{
  "_id": "1",
  "name": "n1",
  "chinese": 60, // 语文
  "math": 60 // 数学
}
{
  "_id": "2",
  "name": "n2",
  "chinese": 60,
  "math": 70
}
{
  "_id": "3",
  "name": "n3",
  "chinese": 100,
  "math": 90
}
//筛选语文数学总分大于150的数据
const db = uniCloud.database()
const res = await db.collection('test')
.where('add(chinese,math) > 150')
.get()

注意:复杂查询条件不可以使用正则查询

查询列表分页
可以通过skip+limit来进行分页查询

const db = uniCloud.database()
db.collection('book')
  .where('status == "onsale"')
  .skip(20) // 跳过前20条
  .limit(20) // 获取20条
  .get()
  
// 上述用法对应的分页条件为:每页20条取第2页

字段过滤field

// 联表查询
db.collection('order,book') // 注意collection方法内需要传入所有用到的表名,用逗号分隔,主表需要放在第一位
  .field('book_id{title,author},quantity') // 这里联表查询book表返回book表内的title、book表内的author、order表内的quantity
  .get()
//或者
db.collection('order,book')
  .where('book_id.title == "三国演义"')
  .field('book_id.title,book_id.author,quantity as order_quantity') // book_id.title、book_id.author为副表字段,使用别名时效果和上一个示例不同,请见下方说明
  .orderBy('order_quantity desc') // 按照order_quantity降序排列
  .get()

字段别名的话,还是用as 别名,和之前用法一致
比如

db.collection('order,book')
  .where('book_id.title == "三国演义"')
  .field('book_id{title as book_title,author as book_author},quantity as order_quantity')
 // 这里联表查询book表返回book表内的title、book表内的author、order表内的quantity,
 //并将title重命名为book_title,author重命名为book_author,quantity重命名为order_quantity
  .orderBy('order_quantity desc') // 按照order_quantity降序排列

排序orderBy
orderBy允许进行多个字段排序,以逗号分隔。每个字段可以指定 asc(升序)、desc(降序)。默认是升序。
写在前面的排序字段优先级高于后面。
orderBy('quantity asc, create_date desc') //按照quantity字段升序排序,quantity相同时按照create_date降序排序

限制查询记录的条数limit
使用limit方法,可以查询有限条数的数据记录。
limit默认值是100,即不设置的情况下,默认返回100条数据。
limit有最大值,腾讯云限制为最大1000条,阿里云限制为最大500条。

// 这以上面的book表数据为例,查价格最高的一本书
  db.collection('book')
    .orderBy('price desc')
    .limit(1)
    .get()

只查一条记录getone

// 这以上面的book表数据为例
const db = uniCloud.database()
  db.collection('book')
    .where({
      title: '西游记'
    })
    .get({
      getOne:true
    })
    .then(res => {
      console.log(res);
    }).catch(err => {
      console.error(err)
    })

如果使用uniCloud-db组件,则在组件的属性上增加一个 getone

统计数量getcount
affectedDocs表示从服务器返回给前端的数据条数。默认100条,可通过limit方法调整。
count则是指符合查询条件的记录总数,至于这些记录是否返回给前端,和count无关。

count计数又有2种场景:
单纯统计数量,不返回数据明细
使用count()方法,如db.collection('order').count()
可以继续加where等条件进行数据记录过滤。

查询记录返回详情,同时返回符合查询条件的数量、使用getCount参数

// 这以上面的order表数据为例
const db = uniCloud.database()
  db.collection('order')
    .get({
      getCount:true
    })
    .then(res => {
      console.log(res);
    }).catch(err => {
      console.error(err)
    })

如果使用uniCloud-db组件,则在组件的属性上增加一个 getcount

数据去重distinct
通过.distinct()方法,对数据查询结果中重复的记录进行去重。

const res = await db.collection('score')
.field('grade,class')
.distinct() // 注意distinct方法没有参数
.get()

查询返回结果
{data:[{grade:"1",class:"A"},{grade:"1",class:"B"},{grade:"2",class:"A"}]}
注意:distinct指对返回结果中完全相同的记录进行去重,重复的记录只保留一条。因为_id字段是必然不同的,所以使用distinct时必须同时指定field,且field中不可存在_id字段。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容