云数据库学习

更新时间: 2025-10-29 17:05:37

# DB Schema表介绍

schema第一个作用是对数据库做初始化,第二个作用就是对权限进行校验,B Schema是基于 JSON 格式定义的数据结构的规范。

# 表结构

Schema的一级节点

{
	"bsonType": "object", // 固定节点
	"description": "表的描述",
	"required": [], // 必填字段
	"permission": {
		"read": false, // 前端非admin的读取记录权限控制。默认值是false,即可以不写。可以简单的true/false,也可以写表达式
		"create": false, // 前端非admin的新增记录权限控制。默认值是false,即可以不写。可以简单的true/false,也可以写表达式
		"update": false, // 前端非admin的更新记录权限控制。默认值是false,即可以不写。可以简单的true/false,也可以写表达式
		"delete": false, // 前端非admin的删除记录权限控制。默认值是false,即可以不写。可以简单的true/false,也可以写表达式
		"count": false // 前端非admin的求数权限控制。默认值是true,即可以不写。可以简单的true/false,也可以写表达式
	},
	"properties": { // 表的字段清单
		"_id": { // 字段名称,每个表都会带有_id字段
			"description": "ID,系统自动生成"
			// 这里还有很多字段属性可以设置
		}
	},
	"fieldRules":[
		// 字段之间的约束关系。比如字段开始时间小于字段结束时间。也可以只校验一个字段。支持表达式
	]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# schema2code代码生成系统

写完schema后单击右键,点击schema2code,会自动生成页面,新建的页面会放到pages里

新建的schema如下:

// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": [],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"name":{
			"title":"姓名",  // 为了方便使用schema2code加上title 
			"description": "请输入姓名",
			"bsonType": "string" // 需要加类型,不然生成的页面不知道是什么类型  
		},
		"age":{
			"title":"年龄",
			"description": "请输入年龄",
			"bsonType": "string"
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

点schema2code,直接点确定

会出现一个pages的注册页面,直接点击确定

然后点击合并

现在pages里面就创建好了4个页面

打开list页面看一下,里面有之前创建的一条数据,增删改查都是可以的

# DB schema常用的字段属性

# enum,defaultValue

我们给schema加上性别字段,然后重新生成,注意要重启一下项目才能显示出来




























 
 
 
 
 
 
 
 
 
 
 




// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": [],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"name":{
			"title":"姓名",  // 为了方便使用schema2code加上title 
			"description": "请输入姓名",
			"bsonType": "string" // 需要加类型,不然生成的页面不知道是什么类型  
		},
		"age":{
			"title":"年龄",
			"description": "请输入年龄",
			"bsonType": "int"
		},
		"gender":{
			"title":"性别",
			"bsonType": "int",
            "defaultValue":0,
			"enum":[{
				"text":"保密",
				"value":0
			},{
				"text":"男",
				"value":1
			},{
				"text":"女",
				"value":2
			}]
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# defaultValue/forceDefaultValue

我们新加一个时间选择

"createTime":{
    "title":"时间",
    "bsonType": "timestamp"
}
1
2
3
4

如果想让这个字段记录我们提交时的时间,就需要用到forceDefaultValue

defaultValue和forceDefaultValue都是默认值,即新增一行数据记录时,如果字段内容未提供,则按默认值填充该字段内容。但2者也有区别,如下:

defaultValue没有强制力,是普通的默认值,如果客户端上传一个其他值,则按客户端传的值为准。
forceDefaultValue则是schema强制约定的值,不管客户端传什么都无法修改。只要数据库新增一条记录,字段的值就会是forceDefaultValue。
在实际开发中,forceDefaultValue常用于设置为当前服务器时间、当前登录用户id、客户端ip等。 这些数据都不能通过前端上传,不安全。过去只能在云端写云函数操作。在schema配置后则可以不用写云函数。使用JQL新增数据记录时会自动补齐这些数据。

$env有三个值可以设置:

变量 说明
now 当前服务器时间戳
clientIP 当前客户端IP
uid 当前用户Id,基于uni-id。如果当前用户未登录或登录状态无效会报错
"createTime":{
    "title":"时间",
    "bsonType": "timestamp",
    "forceDefaultValue":{
        "$env": "now"
    }
}
1
2
3
4
5
6
7

这样就可以自动记录提交时的时间戳了

# required必填字段

在schema一级节点的required中,可以以数组的方式填入多个字段名称。比如以下示例将name字段设为必填

{
  "bsonType": "object",
  "required": ["name"],
  "properties": {
    "name": {
      "bsonType": "string",
      "title": "姓名",
      "errorMessage": "{title}不能为空"
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11

# errorMessage错误提示

可以自定义错误提示

"name":{
    "title":"姓名",  // 为了方便使用schema2code加上title 
    "label":"姓名",
    "description": "请输入姓名",
    "minLength": 2,
    "maxLength": 8,
    "bsonType": "string" ,// 需要加类型,不然生成的页面不知道是什么类型  
    "errorMessage": {
        "required": "{label}必填",
        "minLength": "{label}不能小于{minLength}个字符",
        "maxLength": "{label}不能大于{maxLength}个字符"
        }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# maximum/minimum

如果bsonType为数字时,可接受的最大值和最小值

# 上传图片

"avatar":{
    "title":"头像",
    "bsonType": "file"
},
1
2
3
4


可以看到空间存储上有刚刚上传的图片了

但是图片回显有问题,因此我们需要配上跨域配置

(好像是阿里云的问题,我还是无法显示)

# JQL数据库操作说明

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

# 新增

仅允许collection().add()这样的形式

比如在user表里新增一个叫王五的记录:

// 注意,此处的db是通过 uniCloud.databaseForJQL() 得到,而不是 uniCloud.database()
const db = uniCloud.databaseForJQL()
db.collection('user').add({name:"王五"})
1
2
3

废话不多说,我们新增一个页面来实验一下

<template>
	<view>
		demo1
		<button @click="handleAdd">新增</button>
	</view>
</template>

<script setup>
const db = uniCloud.databaseForJQL()  	

const handleAdd = () => {
	db.collection("default").add({
		name:"123",
		age:15,
		gender:0
	}).then(res => {
		console.log(res)
	}).catch((err) => {
		uni.showModal({
			content:err.errMsg
		})
	})	
}

const handelAdd1 = async () => {
    let res = await db.collection("default").add({
		name:"123",
		age:15,
		gender:0
	})
    console.log(res)
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 查询数据

数据查询用get

const getData = async () => {
	let res = await db.collection("default").get()
	if(res.result.errCode == 0) {
		userList.value = res.result.data
	}
}
1
2
3
4
5
6

# 排序orderBy

orderBy允许进行多个字段排序,以逗号分隔。每个字段可以指定 asc(升序)、desc(降序)。默认是升序。

写在前面的排序字段优先级高于后面。

orderBy('quantity asc, create_date desc') //按照quantity字段升序排序,quantity相同时按照create_date降序排序
// asc可以省略,上述代码和以下写法效果一致
orderBy('quantity, create_date desc')

// 注意不要写错成全角逗号  
1
2
3
4
5

# 限制查询记录的条数limit

使用limit方法,可以查询有限条数的数据记录。

比如查询销量top10的书籍,或者查价格最高的一本书。

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

注意

limit默认值是100,即不设置的情况下,默认返回100条数据。limit最大值为1000。

# 查询列表分页

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

// 注意,此处的db是通过 uniCloud.databaseForJQL() 得到,而不是 uniCloud.database()
const db = uniCloud.databaseForJQL()
db.collection('book')
  .skip(20) // 跳过前20条
  .limit(20) // 获取20条
  .get()

// 上述用法对应的分页条件为:每页20条取第2页
1
2
3
4
5
6
7
8

# 字段过滤

查询时可以使用field方法指定返回字段。不使用field方法时会返回所有字段

列出字段名称,多个字段以半角逗号做分隔符。比如

db.collection('book').field("title,author")
1

查询结果会返回_id、title、author3个字段的数据。字符串写法,_id是一定会返回的

还可以用as给字段起别名

const db = uniCloud.databaseForJQL()
db.collection('book')
  .field('title as book_title,author as book_author')
  .get()
1
2
3
4

以上查询返回如下:

{
	"code": "",
	"message": "",
	"data": [{
      "_id": "3",
			"book_author": "罗贯中",
			"book_title": "三国演义"
		}]
}
1
2
3
4
5
6
7
8
9

# 统计数量getcount

统计符合查询条件的记录数,是数据库层面的概念。

在查询的result里,有一个affectedDocs。但affectedDocs和count计数不是一回事。

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

count计数又有2种场景:

  • 单纯统计数量,不查询数据。使用count()方法
db.collection('order').count()
1
  • 查询记录返回详情,同时返回符合查询条件的数量、使用getCount参数
const db = uniCloud.databaseForJQL()
  db.collection('order')
    .get({
      getCount:true
    })
1
2
3
4
5

# 只查一条记录getone

使用JQL的API方式时,可以在get方法内传入参数getOne:true来返回一条数据。

getOne其实等价于上一节的limit(1)。

一般getOne和orderBy搭配。

const db = uniCloud.databaseForJQL()
db.collection('book')
	.get({
		getOne:true
	})
1
2
3
4
5

# where条件查询

jql对查询条件进行了简化,开发者可以使用where('a==1||b==2')来表示字段a等于1或字段b等于2

db.collection("default")
	.where("name == '123'")
1
2

还可以这样,查询出name包含李四和王五的

db.collection("default")
.where("name in ['李四','王五']")
1
2

还可以使用正则查询

const res = await db.collection('goods').where(`${new RegExp(searchVal, 'i')}.test(name)`).get()
1

# doc配合getOne查询单挑记录查询详情

let res = await db.collection('default').doc(id).get({getOne:true})
1

# remove删除数据与控制台数据库回档

db.collection('default').doc(id).remove()
1

注意

如果不加doc(id)查询就删除,会把表里所有的都删除掉的!!!千万不要,不过如果误删了,web控制台里有数据库回档

# 条件查找文档后删除

db.collection("table1").where("a>2").remove()
1

# update更新操作

const db = uniCloud.databaseForJQL()
let collection = db.collection("table1")
let res = await collection.where({_id:'doc-id'})
  .update({
    name: "Hey",
    count: {
      fav: 1
    }
  });
1
2
3
4
5
6
7
8
9

# 联表查询

# init_data.json数据初始化文件

新建一个分类的schema

// classify.schema.json  
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": [],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"name":{
			"title": "分类名称",
			"bsonType": "string"
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

创建一个和表名同名的json文件


表名为classify.schema.json, 那么初始化的文件就叫 classify.init_data.json

// classify.init_data.json  
[{
	"name":"可爱萌宠"
},{
	"name":"明星美女"
}]
1
2
3
4
5
6

然后右键单击初始化云数据库按钮

然后就成功初始化了数据啦

再按刚才的步骤新建一个壁纸schema并初始化

# foreignKey字段外键数据关联关系

先把页面写出来,还是用之前的查单表的方式写一下页面

<template>
	<view class="layout">
		<view class="item" v-for="item in dataList" :key="item._id">
			<view class="left">
				<image :src="item.picurl" mode="widthFix"></image>
			</view>
			<view class="right">
				<view class="desc">{{item.description}}</view>
				<view class="name">{{item.classname}}</view>
			</view>
		</view>
	</view>
</template>

<script setup>
import {ref} from "vue"
const db = uniCloud.database()
const dataList = ref([])
	
const getData = async() => {
	let res = await db.collection("wallpaper").get()
	console.log(res)
	dataList.value = res.result.data
}	

getData()
</script>

<style lang="scss" scoped>
.layout {
	padding: 30rpx;
	.item {
		display: flex;
		.left {
			width: 200rpx;
			image {
				width: 100%;
			}
		}
		.right {
			flex: 1;
			padding-left: 30rpx ;
			.desc {
				font-size: 38rpx;
			}
			.name {
				font-weight: bold;
			}
		}
	}
}
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

我们修改一下wallpaper的schema,加上classid

// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": [],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"picurl":{
			"title": "链接地址",
			"bsonType": "string"
		},
		"description":{
			"title": "壁纸描述",
			"bsonType": "string"
		},
		"classname":{
			"title": "分类",
			"bsonType": "string"
		},
		"classid":{
			"title": "分类ID",
			"bsonType": "string"
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# foreignKey字段外键

一个复杂的业务系统,有很多张数据表。表与表之间,存在的数据关联。foreignKey用于描述数据关联关系。我们给wallpaper表加上一个字段






























 




// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
	"bsonType": "object",
	"required": [],
	"permission": {
		"read": true,
		"create": true,
		"update": true,
		"delete": true
	},
	"properties": {
		"_id": {
			"description": "ID,系统自动生成"
		},
		"picurl":{
			"title": "链接地址",
			"bsonType": "string"
		},
		"description":{
			"title": "壁纸描述",
			"bsonType": "string"
		},
		"classname":{
			"title": "分类",
			"bsonType": "string"
		},
		"classid":{
			"title": "分类ID",
			"bsonType": "string",
			"foreignKey": "classify._id"
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

就加上这样一个字段,我们就可以联表查询了

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

let res = await db.collection(wallTemp, classTemp).get()
console.log(res)
1
2
3
4
5


可以看到classid字段直接是一个数组了

# 临时表和虚拟联表可以使用的方法

临时表可以使用以下方法(需按照下面的顺序调用)

collection
geoNear // 新增于 HBuilderX 3.6.10
where
field
orderBy
skip
limit
getTemp
1
2
3
4
5
6
7
8

虚拟联表可以使用以下方法(需按照下面的顺序调用)

collection
foreignKey
where
field
groupBy
groupField
distinct
orderBy
skip
limit
get
1
2
3
4
5
6
7
8
9
10
11

# arrayElemAt

返回在指定数组下标的元素。

示例集合users包含以下文档:

{"_id":1,"name":"dave123",favorites:["chocolate","cake","butter","apples"]}
{"_id":2,"name":"li",favorites:["apples","pudding","pie"]}
{"_id":3,"name":"ahn",favorites:["pears","pecans","chocolate","cherries"]}
{"_id":4,"name":"ty",favorites:["ice cream"]}  
1
2
3
4

返回favorites数组中的第一个和最后一个元素:

db.collection('users').field('arrayElemAt(favorites, 0) as first, arrayElemAt(favorites, -1) as last').get()  
1

执行结果:

{"_id":1,"first":"chocolate","last":"apples"}
{"_id":2,"first":"apples","last":"pie"}
{"_id":3,"first":"pears","last":"cherries"}
{"_id":4,"first":"ice cream","last":"ice cream"}
1
2
3
4

对于上面的例子可以直接:

let wallTemp = db.collection("wallpaper").getTemp()
let classTemp = db.collection("classify").getTemp()

let res = await db.collection(wallTemp, classTemp)
.field("_id,picurl,description,arrayElemAt(classid.name,0) as classname")
.get()
1
2
3
4
5
6

# 使用uni-file-picker组件实现本地文件上传到云存储

页面使用uni-file-picker

<template>
	<view>
		<uni-file-picker 
			v-model="imageValue" 
			mode="grid"
		/>
	</view>
</template>

<script setup>
import { ref } from 'vue';

const imageValue = ref('')	
</script>

<style lang="scss" scoped>

</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

随便选择一个图片,会发现有请求

打开云存储,发现刚刚选择的图片已经上传到云存储了

# 联表查询

const getData = async() => {
	console.log("获取数据")
	let wallTemp = db.collection('wallpaper').field("description, classid, picurl.url as picurlpath,_id,createTime").orderBy("createTime desc").getTemp()
	let classTemp = db.collection("classify").getTemp()
	let res = await db.collection(wallTemp, classTemp).field("description, picurlpath, createTime, _id, arrayElemAt(classid.name,0) as classname").get()
	listData.value = res.result.data
	
}
1
2
3
4
5
6
7
8