JQL概述
JQL,全稱 javascript query language榨呆,是一種js方式操作數(shù)據(jù)庫的規(guī)范。
使用場景
客戶端clientDB,包括js內(nèi)以及unicloud-db組件內(nèi)
HBuilderX JQL數(shù)據(jù)庫管理器
啟用了jql擴(kuò)展的云函數(shù)
不同場景的區(qū)別
- JQL數(shù)據(jù)庫管理器:
○ 不會校驗(yàn)任何權(quán)限暖夭,相當(dāng)于以數(shù)據(jù)庫管理員的身份執(zhí)行
○ 即使是admin不能讀寫的password類型數(shù)據(jù)也可以讀寫
○ 不會觸發(fā)數(shù)據(jù)庫觸發(fā)器
○ 不可以執(zhí)行action云函數(shù) - 客戶端clientDB:
○ 完整的權(quán)限校驗(yàn),執(zhí)行操作的用戶不可以操作自己權(quán)限之外的數(shù)據(jù)
○ admin用戶不可操作password類型的數(shù)據(jù) - 云函數(shù)JQL:
○ 同clientDB撵孤,但是password類型的數(shù)據(jù)可以配置權(quán)限迈着,默認(rèn)權(quán)限是false,可以被admin用戶操作邪码。
○ 可以通過setUser指定當(dāng)前執(zhí)行數(shù)據(jù)庫操作的用戶身份裕菠。
JQL包含的模塊
JQL的限制
○ 會對數(shù)據(jù)庫操作進(jìn)行序列化,除Date類型闭专、RegExp之外的所有不可JSON序列化的參數(shù)類型均不支持(例如:undefined)
○ 為了嚴(yán)格控制權(quán)限奴潘,禁止使用set方法
○ 為了數(shù)據(jù)校驗(yàn)?zāi)車?yán)格限制,更新數(shù)據(jù)庫時不可使用更新操作符db.command.inc等
○ 更新數(shù)據(jù)時鍵值不可使用{'a.b.c': 1}的形式影钉,需要寫成{a:{b:{c:1}}}形式
客戶端調(diào)用
const db = uniCloud.database()
// 使用`jql`查詢list表內(nèi)`name`字段值為`hello-uni-app`的記錄
db.collection('list')
.where('name == "hello-uni-app"')
.get()
.then((res)=>{
// res 為數(shù)據(jù)庫查詢結(jié)果
}).catch((err)=>{
// err.message 錯誤信息
// err.code 錯誤碼
})
JQL方法使用限制
下面這些方法必須嚴(yán)格按照下面的順序進(jìn)行調(diào)用画髓,其他方法需要在這些方法之后調(diào)用(不限制順序)
單表查詢:collection aggregate geoNear doc where field groupBy groupField
聯(lián)表查詢
臨時表:collection geoNear where field orderBy skip limit getTemp
虛擬聯(lián)表:collection foreignKey where field groupBy groupField distinct orderBy skip limit get
新增數(shù)據(jù)記錄
方法:collection.add(data)
其中data類型object|array
data中不需要包括_id字段,數(shù)據(jù)庫會自動維護(hù)該字段平委。
const db = uniCloud.database();
const collection = db.collection('user');
//單條記錄
db.collection('user').add({name:"王五"})
//多條記錄
let res = await collection.add([{
name: '張三'
},{
name: '李四'
},{
name: '王五'
}])
刪除數(shù)據(jù)記錄
通過指定文檔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
})
}
刪除所有的數(shù)據(jù)
const dbCmd = db.command
const db = uniCloud.database();
await db.collection("table1").where({
_id: dbCmd.neq(null)
}).remove()
更新數(shù)據(jù)記錄
通過指定文檔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
}
});
內(nèi)置云端環(huán)境變量
在字符串內(nèi)使用
db.collection('user').where('_id==$cloudEnv_uid').get()
在對象內(nèi)使用
db.collection('user').where({ _id: db.getCloudEnv('$cloudEnv_uid') }).get()
使用正則查詢
const res = await db.collection('goods').where(`${new RegExp(searchVal, 'i')}.test(name)`).get()
聯(lián)表查詢
臨時表:getTemp方法返回的結(jié)果奈虾,例:const article = db.collection('article').getTemp()
,此處 article 就是一個臨時表
虛擬聯(lián)表:主表與副表聯(lián)表產(chǎn)生的表,例:db.collection(article, 'comment').get()
JQL聯(lián)表查詢有以下兩種寫法:
// 直接關(guān)聯(lián)多個表為虛擬聯(lián)表再進(jìn)行查詢肉微,舊寫法匾鸥,目前更推薦使用getTemp進(jìn)行聯(lián)表查詢
const res = await db.collection('order,book').where('_id=="1"').get()// 直接關(guān)聯(lián)order和book之后再過濾
// 使用getTemp先過濾處理獲取臨時表再聯(lián)表查詢,推薦用法
const order = db.collection('order').where('_id=="1"').getTemp() // 注意結(jié)尾的方法是getTemp碉纳,對order表過濾得到臨時表
const res = await db.collection(order, 'book').get() // 將獲取的order表的臨時表和book表進(jìn)行聯(lián)表查詢
表關(guān)聯(lián)示例:定義order表和book表 schema
// order表schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": true
},
"properties": {
"book_id": {
"bsonType": "string",
"foreignKey": "book._id" // 使用foreignKey表示勿负,此字段關(guān)聯(lián)book表的_id。
},
"quantity": {
"bsonType": "int"
}
}
}
// book表schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": true
},
"properties": {
"title": {
"bsonType": "string"
},
"author": {
"bsonType": "string"
}
}
}
定義json數(shù)據(jù)
//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
}
// 客戶端聯(lián)表查詢
const db = uniCloud.database()
const order = db.collection('order').field('book_id,quantity').getTemp() // 臨時表field方法內(nèi)需要包含關(guān)聯(lián)字段劳曹,否則無法建立關(guān)聯(lián)關(guān)系
const book = db.collection('book').field('_id,title,author').getTemp() // 臨時表field方法內(nèi)需要包含關(guān)聯(lián)字段笆环,否則無法建立關(guān)聯(lián)關(guān)系
db.collection(order, book) // 注意collection方法內(nèi)需要傳入所有用到的表名,用逗號分隔厚者,主表需要放在第一位
.where('book_id.title == "三國演義"') // 查詢order表內(nèi)書名為“三國演義”的訂單
.get()
.then(res => {
console.log(res);
}).catch(err => {
console.error(err)
})
查詢結(jié)果如下:
{
"code": "",
"message": "",
"data": [{
"_id": "b8df3bd65f8f0d06018fdc250a5688bb",
"book_id": [{
"_id": "3",
"author": "羅貫中",
"title": "三國演義"
}],
"quantity": 555
}, {
"_id": "b8df3bd65f8f0d06018fdc2315af05ec",
"book_id": [{
"_id": "3",
"author": "羅貫中",
"title": "三國演義"
}],
"quantity": 333
}]
}
存在外鍵時結(jié)果返回的區(qū)別
主表某字段foreignKey指向副表時躁劣,返回結(jié)果格式為:
{
"主表字段名1": "xxx",
"主表字段名2": "xxx",
"主表內(nèi)foreignKey指向副表的字段名": [{
"副表字段名1": "xxx",
"副表字段名2": "xxx",
}]
}
當(dāng)副表某字段foreignKey指向主表時,其返回結(jié)果格式為:
{
"主表字段名1": "xxx",
"主表字段名2": "xxx",
"主表內(nèi)被副表foreignKey指向的字段名": {
"副表1表名": [{ // 一個主表字段可能對應(yīng)多個副表字段的foreignKey
"副表1字段名1": "xxx",
"副表1字段名2": "xxx",
}],
"副表2表名": [{ // 一個主表字段可能對應(yīng)多個副表字段的foreignKey
"副表2字段名1": "xxx",
"副表2字段名2": "xxx",
}],
"_value": "主表字段原始值" // 使用副表foreignKey查詢時會在關(guān)聯(lián)的主表字段內(nèi)以_value存儲該字段的原始值库菲,新增于HBuilderX 3.1.16
}
}
可簡單理解為:
主表foreignKey指向副表時账忘,查詢結(jié)果:關(guān)聯(lián)字段只包含副表查詢結(jié)果,不含表名
副表foreignKey指向主表時熙宇,查詢結(jié)果:關(guān)聯(lián)字段中會包含副表的表名和查詢結(jié)果
臨時聯(lián)表查詢
使用臨時表進(jìn)行聯(lián)表查詢鳖擒,可以先對主表或者副表進(jìn)行過濾,然后在處理后的臨時表的基礎(chǔ)上生成虛擬聯(lián)表烫止。
// 先過濾article表蒋荚,再獲取虛擬聯(lián)表聯(lián)表獲取評論
const article = db.collection('article').where('article_id=="1"').getTemp() // 注意是getTemp不是get
const res = await db.collection(article, 'comment').get()
設(shè)置表字段別名
語法:原字段名 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內(nèi)對副表字段title進(jìn)行了重命名,where方法內(nèi)則需要使用重命名之后的字段名
.get()
.then(res => {
console.log(res);
}).catch(err => {
console.error(err)
})
查詢結(jié)果如下
{
"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 簡單查詢
這里的test方法比較強(qiáng)大馆蠕,格式為:正則規(guī)則.test(fieldname)期升。
具體到這個正則 /abc/.test(content),類似于sql中的content like '%abc%'互躬,即查詢所有字段content包含abc的數(shù)據(jù)記錄播赁。
注意
? 不支持非操作
? 編寫查詢條件時,除test外吼渡,均為運(yùn)算符左側(cè)為數(shù)據(jù)庫字段容为,右側(cè)為常量
簡單查詢條件內(nèi)要求二元運(yùn)算符兩側(cè)不可均為數(shù)據(jù)庫內(nèi)的字段
上述寫法的查詢語句可以在權(quán)限校驗(yàn)階段與schema內(nèi)配置的permission進(jìn)行一次對比校驗(yàn),如果校驗(yàn)通過則不會再查庫進(jìn)行權(quán)限校驗(yàn)寺酪。
復(fù)雜查詢條件
復(fù)雜查詢內(nèi)可以使用數(shù)據(jù)庫運(yùn)算方法
示例:數(shù)據(jù)表test內(nèi)有以下數(shù)據(jù)
{
"_id": "1",
"name": "n1",
"chinese": 60, // 語文
"math": 60 // 數(shù)學(xué)
}
{
"_id": "2",
"name": "n2",
"chinese": 60,
"math": 70
}
{
"_id": "3",
"name": "n3",
"chinese": 100,
"math": 90
}
//篩選語文數(shù)學(xué)總分大于150的數(shù)據(jù)
const db = uniCloud.database()
const res = await db.collection('test')
.where('add(chinese,math) > 150')
.get()
注意:復(fù)雜查詢條件不可以使用正則查詢
查詢列表分頁
可以通過skip+limit來進(jìn)行分頁查詢
const db = uniCloud.database()
db.collection('book')
.where('status == "onsale"')
.skip(20) // 跳過前20條
.limit(20) // 獲取20條
.get()
// 上述用法對應(yīng)的分頁條件為:每頁20條取第2頁
字段過濾field
// 聯(lián)表查詢
db.collection('order,book') // 注意collection方法內(nèi)需要傳入所有用到的表名坎背,用逗號分隔,主表需要放在第一位
.field('book_id{title,author},quantity') // 這里聯(lián)表查詢book表返回book表內(nèi)的title寄雀、book表內(nèi)的author得滤、order表內(nèi)的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')
// 這里聯(lián)表查詢book表返回book表內(nèi)的title膜蛔、book表內(nèi)的author、order表內(nèi)的quantity脖阵,
//并將title重命名為book_title皂股,author重命名為book_author,quantity重命名為order_quantity
.orderBy('order_quantity desc') // 按照order_quantity降序排列
排序orderBy
orderBy允許進(jìn)行多個字段排序命黔,以逗號分隔呜呐。每個字段可以指定 asc(升序)、desc(降序)悍募。默認(rèn)是升序蘑辑。
寫在前面的排序字段優(yōu)先級高于后面。
orderBy('quantity asc, create_date desc') //按照quantity字段升序排序坠宴,quantity相同時按照create_date降序排序
限制查詢記錄的條數(shù)limit
使用limit方法洋魂,可以查詢有限條數(shù)的數(shù)據(jù)記錄。
limit默認(rèn)值是100喜鼓,即不設(shè)置的情況下副砍,默認(rèn)返回100條數(shù)據(jù)。
limit有最大值庄岖,騰訊云限制為最大1000條豁翎,阿里云限制為最大500條。
// 這以上面的book表數(shù)據(jù)為例隅忿,查價格最高的一本書
db.collection('book')
.orderBy('price desc')
.limit(1)
.get()
只查一條記錄getone
// 這以上面的book表數(shù)據(jù)為例
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
統(tǒng)計(jì)數(shù)量getcount
affectedDocs表示從服務(wù)器返回給前端的數(shù)據(jù)條數(shù)。默認(rèn)100條背桐,可通過limit方法調(diào)整刘陶。
count則是指符合查詢條件的記錄總數(shù),至于這些記錄是否返回給前端牢撼,和count無關(guān)匙隔。
count計(jì)數(shù)又有2種場景:
單純統(tǒng)計(jì)數(shù)量,不返回數(shù)據(jù)明細(xì)
使用count()
方法熏版,如db.collection('order').count()
可以繼續(xù)加where等條件進(jìn)行數(shù)據(jù)記錄過濾纷责。
查詢記錄返回詳情,同時返回符合查詢條件的數(shù)量撼短、使用getCount參數(shù)
// 這以上面的order表數(shù)據(jù)為例
const db = uniCloud.database()
db.collection('order')
.get({
getCount:true
})
.then(res => {
console.log(res);
}).catch(err => {
console.error(err)
})
如果使用uniCloud-db組件再膳,則在組件的屬性上增加一個 getcount
數(shù)據(jù)去重distinct
通過.distinct()方法,對數(shù)據(jù)查詢結(jié)果中重復(fù)的記錄進(jìn)行去重曲横。
const res = await db.collection('score')
.field('grade,class')
.distinct() // 注意distinct方法沒有參數(shù)
.get()
查詢返回結(jié)果
{data:[{grade:"1",class:"A"},{grade:"1",class:"B"},{grade:"2",class:"A"}]}
注意:distinct指對返回結(jié)果中完全相同的記錄進(jìn)行去重喂柒,重復(fù)的記錄只保留一條不瓶。因?yàn)開id字段是必然不同的,所以使用distinct時必須同時指定field灾杰,且field中不可存在_id字段蚊丐。