- 云开发数据库入门讲解
任何一个大型的应用程序和服务,都必须会使用到高性能的数据存储解决方案,用来准确(ACID,原子性 Atomicity、一致性 Consistency、隔离性 Isolation、持久性 Durability,可以拓展了解一下)、快速、可靠地存储和检索用户的账户信息、商品以及商品交易信息、产品数据、资讯文章等等等等,而云开发就自带高性能、高可用、高拓展性且安全的数据库。 云数据库的基础知识在操作数据库时,我们要对数据库database、集合collection、记录doc以及字段field要有一定的了解,首先要记住这些对应的英文单词,当你要操作某个记录doc的字段内容时,就像投送快递一样,要先搞清楚它到底在哪个数据库、在哪个集合、在哪个记录里,一级一级的去找。操作数据库通常都是对数据库、集合、记录、字段进行增、删、改、查,当你清楚了这些,操作数据库就不会迷糊了。 云数据库与 Excel、MySQL 的对应理解我们可以结合 Excel 以及 MySQL(之前没有接触过 MySQL 也没有关系,只看与 Excel 的对应就行)来理解云开发的数据库。 [图片] 集合的创建与数据类型我们现在来创建一个 books 的集合(相当于创建一张 Excel 表),用来存放图书馆里面书籍的信息,比如这样一本书: [图片] 打开云开发控制台的数据库标签,新建集合 books,然后选择该集合,给 books 里添加记录(类似于填写 Excel 含字段的第一行和其中一行关于书的信息记录),依次添加字段: 字段名:title,类型:string,值: JavaScript 权威指南(第 6 版)字段名:author,类型:string,值:弗兰纳根(David Flanagan)字段名:isbn,类型:string,值:9787111376613字段名:publishInfo,类型:object然后我们再在 publishInfo 的下面(二级)添加字段 press,类型为 string,值为:机械工业出版社;year,类型为 number,值为:2012 另外,如需批量添加数据库集合中的记录时,推荐使用云开发数据库自带的“高级操作”,高级操作支持数据库的增删改查以及聚合等操作,可有效提高数据库管理效率。 [图片] 在控制台数据库管理页中可以编写和执行数据库脚本,这些脚本的语法会跟我们之后会学习的 SDK 数据库语法一样,可以作为我们日常“调试”数据库的一种基础方式,可理解为云开发控制台的 console。 技术文档:数据库高级操作 云数据库的导入和导出 除了在集合中添加数据库记录,云开发数据库还支持 json 和 csv 文件的导入和导出。 [图片] json 文件的导入 打开云开发控制台,在数据库里新建一个集合,即可选择导入本地的 json 文件。 注:导入时会有冲突模式选择,看下面的介绍,推荐大家使用 upsert: Insert:总是插入新记录 Upsert:如果记录存在则更新,否则插入新记录 导入后,发现数据库自动给每一条数据(记录)都加了唯一的标识 _id 。 csv 文件的导入 在云开发控制台里新建一个数据库集合,点击“导入”,选中本地的 csv 文件即可导入到集合当中。 本教程中以某年的中国部分城市经济数据为例:创建一个名为“china”的集合,并导入 china.csv 文件,这里我们也准备了一份线上版本供使用。 此处创建的集合,以及导入的数据,在下方章节的学习中会使用到。 数据库的导出 [图片] 点击 云开发控制台-数据库-导出,填写需要导出的字段,即可选择导出 json 和 csv 文件,其中导出 csv 文件时必须填写字段,字段间需以逗号隔开。 数据库的权限控制与安全规则在数据库创建之后,我们需要在云开发控制台-数据库-集合的权限设置标签对数据库进行权限设置。数据库的权限分为小程序端和服务端(云函数、云开发控制台)。服务端拥有读写所有数据的读写权限,所以这里的权限设置只是在设置小程序端的用户对数据库的操作权限。权限控制分简易权限控制和自定义权限(也就是安全规则),建议开发者用安全规则取代简易的权限控制。 技术文档:权限控制 要使用自定义权限(也就是安全规则)来全面取代简易的权限控制,我们需要了解 4 个简易的权限控制所表示的意思,以及安全规则应该如何一一取代它们,也就是我们在配置集合的权限时,不再选择简易的权限控制,而是统一选择自定义权限,填写与之对应的 json 规则即可。 安全规则可以让更加灵活而又明确地自定义前端数据库读写权限的能力,通过配置安全规则,开发者可以精细化的控制集合中所有记录的读read、写write权限。其中write权限还可以细分为create新建、update更新、delete删除等权限,还支持比较、逻辑运算符进行更加精细化的权限配置。 所有用户可读,仅创建者可读写:比如用户发的帖子、评论、文章,这里的创建者是指小程序端的用户,也就是存储 UGC(用户产生内容)的集合要设置为这个权限; { "read": true, "write": "doc._openid == auth.openid" } 仅创建者可读写:比如私密相册,用户的个人信息、订单,也就是只能用户自己读与写,其他人不可读写的数据集合; { "read": "doc._openid == auth.openid", "write": "doc._openid == auth.openid" } 所有人可读:比如资讯文章、商品信息、产品数据等你想让所有人可以看到,但是不能修改的内容; { "read": true, "write": false } 所有用户不可读写:如后台用的不暴露的数据,只能你自己看到和修改的数据; { "read": false, "write": false } 小程序端 API 拥有严格的调用权限控制,比如在小程序端 A 用户是不能修改 B 用户的数据的,没有这样的权限,在小程序端只能修改非敏感且只是针对单个用户的数据;对于有更高安全要求的数据,我们可以在云函数内通过服务端 API 来进行操作。 如果数据库集合里的数据是通过导入的方式获取的,这个集合的权限默认为“仅创建者可读写”,这个权限在服务端(云函数)可以调用,但是在小程序端可能会返回空数组哦,所以一定要记得根据情况修改权限。 小程序端与云函数的服务端无论是在权限方面、API的写法上(有时看起来一样,但是写法不一样),还是在异步处理上(比如服务端不再使用success、fail、complete回调,而是返回Promise对象),都存在非常多的差异,这一点要分清楚。 一窥数据查询的全貌 查询集合 collection 里的记录 查询集合 collection 里的记录是云开发数据库操作最重要的知识,在上文中我们已经将中国城市经济数据 china.csv 的数据导入到了集合 china 之中,并介绍了如何设置集合权限,接下来我们就以此为例来讲解数据库的查询。 [图片] 在中国城市经济数据线上 excel 版以及云开发控制台 china 集合里,我们可以看到中国 332 个城市的名称 city、省份 province、市区面积 city_area、建成区面积 builtup_area、户籍人口 reg_pop、常住人口 resident_pop、GDP 的数据。 查询中国 GDP 在 3000 亿元以上的前 10 个城市,并要求不显示_id 字段,显示城市名、所在省份以及 GDP,并按照 GDP 大小降序排列。 使用开发者工具新建一个 chinadata 页面,然后再在 index.js 的 onLoad 生命周期函数里输入以下代码。操作集合里的数据涉及的知识点非常繁杂,下面的案例相对比较完整,便于大家有一个整体性的理解: const db = wx.cloud.database() //获取数据库的引用 const _ = db.command //获取数据库查询及更新指令 db.collection("china") //获取集合china的引用 .where({ //查询的条件指令where gdp: _.gt(3000) //查询筛选条件,gt表示字段需大于指定值。 }) .field({ //显示哪些字段 _id:false, //默认显示_id,这个隐藏 city: true, province: true, gdp:true }) .orderBy('gdp', 'desc') //排序方式,降序排列 .skip(0) //跳过多少个记录(常用于分页),0表示这里不跳过 .limit(10) //限制显示多少条记录,这里为10 .get() //获取根据查询条件筛选后的集合数据 .then(res => { console.log(res.data) }) .catch(err => { console.error(err) }) 大家可以留意一下数据查询的链式写法, wx.cloud.database().collection('数据库名').where().get().then().catch(),前半部分是数据查询时对对象的引用和方法的调用;后半部分是Promise对象的方法,Promise对象是get的返回值。写的时候为了让结构更加清晰,我们做了换行处理,写在同一行也是可以的。 构建查询条件的 5 个方法在上面的案例中,就包含了构建查询条件的五个方法: Collection.where()、 Collection.field()、 Collection.orderBy()、 Collection.skip()、 Collection.limit(),这五个方法是可以单独拆开使用的,比如只使用 where 或只使用 field、limit,也可以从这 5 个中抽几个组合在一起使用,还可以一次查询里写多个相同的方法,比如 orderBy、where 可以写多次相同的。 不过值得注意的是这5个方法顺序不同查询的结果有时也会有所不同(比如orderBy多次打乱顺序的情况下),查询性能也会有所不同。通常skip最好放在后面,不要让skip略过大量数据。skip().limit()和limit().skip()效果是等价的。构建查询条件的5个方法是基于集合引用Collection的,就拿where来说,不能写成 wx.cloud.database().where(),也不能是 wx.cloud.database().collection("china").doc.where(),只能是 wx.cloud.database().collection("china").where(),也就是只能用于查询集合collection里的记录。 指令查询条件 where,注意在后面我们会介绍的 command 查询指令比如筛选字段大于/小于/不等于某个值的比较指令,同时满足多个筛选条件的逻辑指令等,以及模糊查询的正则都是写在 where 内;技术文档:Collection.where 指定返回哪些字段 field,查询时只需要传入 true|false(或 1|-1)就可以返回或不返回哪些字段,在上面的案例里我们就只返回 city、province、gdp 三个字段的值:技术文档:Collection.field 数据排序 orderBy,排序的语法如下,里面为排序的条件,这里的字段名可不受 field 的限制(不在 field 内,没有显示,但是还是会起作用): orderBy('字段名', '排序方式')。排序方式只支持 desc 降序、asc 升序这两种方式,如果字段里面的值时数字就按照大小,如果是字母就按照先后顺序,不支持中文的排序方式。排序支持按多个字段排序,多次调用 orderBy 即可,多字段排序时的顺序会按照 orderBy 调用顺序先后对多个字段排序。如果需要对嵌套字段排序,可以使用点表示法,比如上面的 books 根据出版年份 year 从旧到新排序,可以写为 orderBy('publishInfo.year', 'asc')。技术文档:Collection.orderBy 分页显示 skip,skip 常与 limit 一起用于分页,比如商品列表一页只显示 20 个商品,第 1 页显示整个数据的 0~20 个,那么第 2 页我们用 skip(20)可以跳过第一页的 20 条数据,第 3 页则跳过 40 个数据,第 N 页则是 skip((n-1)*20)个数据:技术文档:Collection.skip 限制数量上限的 limit,集合数据查询的数量上限 limit 在小程序端最大数量为 20,在服务端为 100,比如 limit(30)在小程序端还是只会显示 20 条数据,更多数据则需要我们结合分页 skip 与 javascript 进行编程处理。技术文档:Collection.limit 小程序查询数据显示的结果虽然有数量限制,比如服务端为 100 个,但是排序仍然是基于整个集合的数据进行排序的,并不是只针对这 100 个数据。 匹配查询传入的对象的每个 构成一个筛选条件,有多个 则表示需同时满足这些条件,是 与的关系,如果需要 或关系,可使用 command.or 技术文档:command.or 查询指令 Command指令用于查询时,都会写在 where 内,主要对字段的值进行比较和逻辑的筛选判断。数据库 API 提供了大于、小于等多种查询指令,这些指令都暴露在 db.command 对象上。 指令Command可以分为查询指令和更新指令,这两者的用法有很大的区别,查询指令用于db.collection的where条件筛选,而更新指令则是用于db.collection.doc的update请求的字段的更新里,这两者的区别在后面我们也会反复提及。 比较操作符和逻辑操作符下面我们把查询指令的比较操作符和逻辑操作符整理成了一张表格,并附上相应的技术文档,方便大家对它们有一个清晰而整体的认识。 查询指令之比较 [图片]查询指令的写法指令 command 是基于 database 数据库引用的,我们以大于 gt 在小程序端(以大于 3000 为例)的完整写法为例: wx.cloud.database().command.gt(3000) 为了简便,通常我们会把 wx.cloud.database()会赋值给一个变量,如 db, db.command 又会赋值给 _,使用时最终被简化为 _.gt(3000)。通过一层一层的声明变量并赋值,大大简化了指令的写法,大家可以在其他指令都沿用这种写法。 用法丰富的等于指令 Command.eq相比于其他的比较指令等于 eq 和不等于 neq 操作符的用法非常丰富,它可以进行数值比较,我们查询某个字段比如 GDP 等于某个数值如 17502.8 亿的城市: .where({ gdp: _.eq(17502.8), }) 它还可以进行字符串的匹配,比如我们查询某个字段比如 city 完整匹配一个字符串如深圳: .where({ city: _.eq("深圳"), }) 注意:在查询时,gdp: _.eq(17502.8)的效果等同于gdp:17502.8,而city: _.eq(“深圳”)等同于city:”深圳”,虽然两种方式查询的结果都是一致的,但是它们的原理不同,前者用的是等于指令,后者用的是传递对象值。 eq 还可以用于字段的值是数组以及对象的情况,在后面的章节我们会再来介绍。 字段内的逻辑指令查询广东省内、GDP 在 3000 亿以上且在 1 万亿以下的城市。在广东省内也就是让字段 province 的值等于”广东”,而 GDP 的要求则是 GDP 这个字段同时满足大于 3000 亿且小于 1 万亿,这时就需要用到 and(条件与,也就是且的意思): .where({ province:_.eq("广东"), gdp:_.gt(3000).and(_.lt(10000)) }) 跨字段的逻辑指令上面的案例中 where 内的两个条件, province:.eq("广东")和 gdp:.gt(3000).and(_.lt(10000))带有跨字段的条件与 and(也就是且)的关系,那如何实现跨字段的条件或 or 呢? 查询中国 GDP 在 3000 亿元以上且常住人口在 500 万以上或建城区面积在 300 平方公里以上的前 20 个大城市。这里常住人口和建成区面积只需要满足其中一个条件即可,这就涉及到条件或 or(注意下面代码的格式写法): .where( { gdp: _.gt(3000), resident_pop:_.gt(500), }, _.or([{ builtup_area: _.gt(300)} ]), ) 注意上面三个条件, gdp: .gt(3000)和 resident_pop:.gt(500)是逻辑与,而与 builtup*area: *.gt(300)}的关系是逻辑或。 _.or([{条件一 },{条件二 }])内是一个数组,条件一与条件二又构成逻辑与的关系。 正则查询 db.RegExp正则表达式能够灵活有效匹配字符串,可以用来检查一个串里是否含有某种子串,比如“CloudBase 技术训练营”里是否含有”技术”这个词。云数据库正则查询支持 UTF-8 的格式,可以进行中英文的模糊查询。正则查询也是写在 where 字段的条件筛选里。 技术文档:Database.RegExp 字段字符串的模糊查询我们可以用正则查询来查询某个字段,比如 city 城市名称内,包含某个字符串比如”州”的城市: .where({ city: db.RegExp({ regexp: '州', options: 'i', }) }) 注意这里的 city 是字段,db.RegExp()里的 regexp 是正则表达式,而 options 是 flag,i 是 flag 的值表示不区分字母的大小写。当然我们也可以直接在 where 内用 JavaScript 的原生写法或调用 RegExp 对象的构造函数。比如上面的案例也可以写成: //JavaScript原生正则写法 .where({ city:/州/i }) //JavaScript调用RegExp对象的构造函数写法 .where({ city: new db.RegExp({ regexp: "州", options: 'i', }) }) 数据库查询的正则表达式也支持模板字符串,比如我们可以先声明 const cityname=”州”,然后用模板字符串包住 cityname 变量: city: db.RegExp({ regexp:`${cityname}`, options: 'i', }) 简单的正则表达式入门正则表达式的用法是非常繁杂的,关于正则表达式的知识可以去 MDN 以及掘金上面搜索了解更多细节。 技术文档:正则表达式、掘金正则搜索列表 值得注意的是,在数据库查询时应尽可能避免过度使用正则表达式来做复杂的匹配,尤其是用户访问触发较多的场景,通常情况下数据查询的响应时间(无论是小程序端还是云函数端)最好要低于500ms。 在小程序端新增记录和统计记录在前面我们已经介绍了集合数据请求的查询方法 get,除了 get 查询外,请求的方法还有 add 新增,remove 删除、update 改写/更新、count 统计以及 watch 监听,这些方法都是基于数据库集合的引用 Collection 的,接下来我们再来介绍如何基于 Collection 新增记录和统计记录的数量。 基于数据库集合的引用Collection所查询到的记录都是多条记录,也就是说我们可以对N条记录进行增、删、改、查等操作,不过目前还不支持在小程序端进行多条记录的update和remove,只能在云函数端进行这样的操作。 统计记录 Collection.count 统计集合记录数或统计查询语句对应的结果记录数。小程序端与云函数端的表现会有如下差异:小程序端:注意与集合权限设置有关,一个用户仅能统计其有读权限的记录数云函数端:因属于管理端,因此可以统计集合的所有记录数。 技术文档:Collection.count() const db = wx.cloud.database() const _ = db.command db.collection("china") .where({ gdp: _.gt(3000) }) .count().then(res => { console.log(res.total) }) field、orderBy、skip、limit 对 count 是无效的,只有 where 才会影响 count 的结果,count 只会返回记录数,不会返回查询到的数据。 新增记录 Collection.add在前面我们将知乎日报的数据导入到了 zhihu_daily 的集合里,接下来,我们就来给 zhihu_daily 新增记录。 技术文档:Collection.add 使用开发者工具新建一个 zhihudaily 的页面,然后在 zhihudaily.wxml 里输入以下代码,新建一个绑定了事件处理函数为 addDaily 的 button 按钮: 新增日报数据 然后再在 zhihudaily.js 里输入以下代码,在事件处理函数 addDaily 里调用 Collection.add,往集合 zhihu_daily 里添加一条记录,如果传入的记录对象没有 _id 字段,则由后台自动生成 _id;若指定了 _id,则不能与已有记录冲突。 addDaily(){ db.collection('zhihu_daily').add({ data: { _id:"daily9718005", title: "元素,生生不息的宇宙诸子", images: [ "https://pic4.zhimg.com/v2-3c5d866701650615f50ff4016b2f521b.jpg" ], id: 9718005, url: "https://daily.zhihu.com/story/9718005", image: "https://pic2.zhimg.com/v2-c6a33965175cf81a1b6e2d0af633490d.jpg", share_url: "http://daily.zhihu.com/story/9718005", body:"谨以此文,纪念元素周期表发布 150 周年。\r\n地球,世界,和生活在这里的芸芸众生从何而来,这是每个人都曾有意无意思考过的问题。\r\n科幻小说家道格拉斯·亚当斯给了一个无厘头的答案,42;宗教也给出了诸神创世的虚构场景;\r\n最为恢弘的画面,则是由科学给出的,另一个意义上的生死轮回,一场属于元素的生死轮回。" } }) .then(res => { console.log(res) }) .catch(console.error) } 点击新增日报数据的 button,会看到控制台打印的 res 对象里包含新增记录的_id 为我们自己设置的 daily9718005。打开云开发控制台的数据库标签,打开集合 zhihu_daily,翻到最后一页,就能看到我们新增的记录啦。 _openid 与集合权限注意和导入的数据不同的是,在小程序端新增记录,都会自动添加一个_openid 的字段,它的值等于用户 openid,_openid 的值是不允许修改的。当我们把集合的权限改为仅创建者可读写,或所有人可读,仅创建者可读写,在小程序端查询或更新记录时,会自动添加一个条件, .where({ _openid:"当前用户的openid" }) 所以这就是为什么尽管集合里面有数据,但是由于有了这个条件,只要记录里没有_openid 或 openid 不匹配就查询不到记录。 集合请求方法注意事项 get、update、count、remove、add等都是请求,在小程序端可以有callback和promise两种写法,但是在云函数端只能用promise,不能用callback。为了方便,建议大家统一使用promise的写法,也就是then、catch。 get、update、count、remove、add请求不能在一个数据库引用里同时存在。比如不能又是get(),又是count()的,不能这么写: db.collection('china').where({ _openid: 'xxx', }).get().count().add() 云函数端操作集合内记录云函数端调用数据库在云开发能力章节我们已经介绍过如何在云函数端调用数据库,这里也是一样。新建一个云函数 chinadata,然后在 exports.main = async (event, context) => {}输入以下代码,注意是 const db = cloud.database(),wx. cloud.database(),云函数端的数据库引用和小程序端有所不同: const db = cloud.database() const _ = db.command return await db.collection("china") .where({ gdp: _.gt(3000) }) .field({ _id: false, city: true, province: true, gdp: true }) .orderBy('gdp', 'desc') .skip(0) .limit(10) .get() try/catch async错误处理 当 async 函数中只要一个 await 出现 reject 状态,则后面的 await 都不会被执行。如果有多个 await 则可以将其都放在 try/catch 中。 然后右键 chinadata 云函数根目录选择在终端中打开,输入 npm install,之后上传并部署所有文件。 在前面我们了解到,调用云函数可以使用本地调试、云端测试,我们还可以在小程序端调用云函数,将云函数的数据返回到小程序端来。使用开发者工具在 chinadata.wxml 里输入以下代码,也就是我们通用点击按钮触发事件处理函数: 调用chinadata云函数 再在事件处理函数里调用云函数,在 chinadata.js 里输入 getChinaData 事件处理函数来调用 chinadata 云函数: getChinaData() { wx.cloud.callFunction({ name: 'chinadata', success: res => { console.log("云函数返回的数据",res.result.data) }, fail: err => { console.error('云函数调用失败:', err) } }) }, 在模拟器里点击调用 chinadata 云函数的 button 按钮,就能在控制台里看到云函数返回的查询到的结果,大家可以通过 setData 的方式将查询的结果渲染到小程序页面,这里就不介绍啦。 删除多条数据记录基于数据库集合的引用 Collection,我们可以先匹配 where 语句查询到相关条件的多条记录,再来调用 Collection.remove()来进行删除。五个查询方法,skip 和 limit 不支持,field、orderBy 没有意义,只有 where 条件可以用来筛选记录。数据一旦删除就不能再找回了。 技术文档:Collection.remove() 我们可以把之前建好的 chinadata 云函数 exports.main = async (event, context) => {}里的代码修改为如下,即删除省份 province 为广东的所有数据: return await db.collection('china') .where({ province:"广东" }) .remove() 在模拟器里点击调用 chinadata 云函数的 button 按钮,就能在控制台里看到云函数返回的对象,其中包含 stats: {removed: 22},即删除了 22 条数据。 更新多条记录 Collection.update 我们可以把之前建好的 chinadata 云函数 exports.main = async (event, context) => {}里的代码修改为如下,也就是先查询省份 province 为湖北的记录,给这个记录更新一个字段英文省份名 pro-en: return await db.collection('china') .where({ province:"湖北" }) .update({ data: { "pro-en": "Hubei" }, }) 这里要注意的是,pro-en 这个字段之前是没有的,通过 Collection.update 不只是起到更新的作用,还可以批量新增字段并赋值,也就是 update 时记录里有相同字段就更新,没有就新增; "pro-en": "Hubei",直接使用 pro-en 会报错,用双引号效果等价。 如果你想给导入的数据添加_openid字段,只用云函数是没法实现的,因为云函数没有用户的登录态。我们需要先在小程序端调用云函数比如login返回openid,再将openid的值再传给chinadata云函数,才能给记录添加openid。 操作单个记录 doc 的字段值在前面我们已经了解了基于集合引用 Collection 构建查询条件的 5 个方法,以及一些请求方法,接下来我们来讲一下基于集合记录引用 Document 的四个请求方法:获取单个记录数据 Document.get()、删除单个记录 Document.remove()、更新单个记录 Document.update()、替换更新单个记录 Document.set()。和基于 Collection 不一样的是,前者的增删改查是可以批量多条的,而基于 Document 则是操作单条记录。 查询集合collection里的记录常用于获取文章、资讯、商品、产品等等的列表;而查询单个记录doc的字段值则常用于这些列表里的详情内容。如果你在开发中需要增删改查某个记录的字段值,为了方便让程序可以根据_id找到对应的记录,建议在创建记录的时候_id用程序有规则的生成。 查询单个记录 doc 的字段值集合里的每条记录都有一个 _id 字段用以唯一标志一条记录,_id 的数据格式可以是 number 数字,也可以是 string 字符串。这个_id 是可以自定义的,当导入记录或写入记录没有自定义时系统会自动生成一个非常长的字符串。查询记录 doc 的字段 field 值就是基于_id 的。 技术文档:获取单个记录数据 Document.get() 比如我们查询其中知乎日报的一篇文章(也就是其中一条记录)的数据,使用开发者工具 zhihudaily 页面的 zhihudaily.js 的 onLoad 生命周期函数里输入以下代码(db 不要重复声明): db.collection('zhihu_daily').doc("daily9718006") .get() .then(res => { console.log('单个记录的值',res.data) }) .catch(err => { console.error(err) }) }, 如果集合的数据是导入的,那_id 是自动生成的,自动生成的_id 是字符串 string,所以 doc 内使用了单引号(双引号也是可以的哦),如果你自定义的_id 是 number 类型,比如自定义的_id 为 20191125,查询时为 doc(20191125)即可,这只是基础知识啦。 删除单条记录技术文档:删除单个记录Document.remove() removeDaily(){ db.collection('zhihu_daily').doc("daily9718006") .remove() .then(console.log) .catch(console.error) } 更新单条记录技术文档:更新单个记录 Document.update() updateDaily(){ db.collection('zhihu_daily').doc("daily9718006") .update({ data:{ title: "【知乎日报】元素,生生不息的宇宙诸子", } }) }, 替换更新记录技术文档:替换更新单个记录 Document.set() setDaily(){ db.collection('zhihu_daily').doc("daily9718006") .set({ data: { "title": "为什么狗会如此亲近人类?", "images": [ "https://pic4.zhimg.com/v2-4cab2fbf4fe9d487910a6f2c54ab3ed3.jpg" ], "id": 9717547, "url": "https://daily.zhihu.com/story/9717547", "image": "https://pic4.zhimg.com/v2-60f220ee6c5bf035d0eaf2dd4736342b.jpg", "share_url": "http://daily.zhihu.com/story/9717547", "body": "让狗从凶猛的野兽变成忠实的爱宠,涉及了宏观与微观上的两层故事:我们如何在宏观上驯养了它们,以及这些驯养在生理层面究竟意味着什么。\r\ncontent-image\" src=\"http://pic1.zhimg.com/70/v2-4147c4b02bf97e95d8a9f00727d4c184_b.jpg\" alt=\"\">\r\n狗是灰狼(Canis lupus)被人类驯养后形成的亚种,至少可以追溯到 1 万多年以前,是人类成功驯化的第一种动物。在这漫长的岁月里,人类的定向选择强烈改变了这个驯化亚种的基因频率,使它呈现出极高的多样性,尤其体现在生理形态上。" } }) }
2021-09-10 - 企业微信没有任何违规却被永久封禁,该如何解决?
从企业微信刚推出第一个版本起,就一直在关注企业微信的动态。 上个月感觉企业微信各方面的功能已趋于成熟,于是开始使用。 最初企业里只创建立了一个账号,然后用这个账户创建了一个互通群,设置了一条欢迎语:欢迎 @XX 进群 然后将二维码发到了个人朋友圈,大半天的时间,八十多人进群了,然后我的企业微信账号突然就被封禁了。询问客服,得到的回答是:核实到账号存在异常,被限制使用互通发消息功能,具体解封时间以及原因不便透露。 再继续问,就都是不便透露。 本以为这只是系统的误判,或者是微信好友进群的速度太快导致的,猜测三五天或者个把星期就会自动解封的。 于是重新创建了两个账号,使用时更加克制了,没有主动或被动加过一个微信好友,仍然只是建了两个群,微信好友进群的速度也很慢。 并且设置了比较严格的群规和提醒。 [图片] [图片] 然后,没等到第一个账号的解封,反而第二个和第三个账号,陆续都被封禁了,显示:账号存在异常。 再次询问客服,得到的回答依然是:核实到账号存在异常,被限制使用互通发消息功能,具体解封时间以及原因不便透露。 [图片] 再三沟通之后,客服终于答应继续进行核实。 [图片] 本以为通过进一步的核实,会发现这是系统误判,然后能顺利解封的。却没想到等来了这个结果: [图片] 然而,事实是,我们的账号没有发过任何广告,仔细研究了“使用规范”,没有发现任何违规的地方,不知道是不是竞争对手恶意投诉的。 现在企业里创建的账号也统统没有了互通功能,之前创建的三四个群,也没办法设置个人微信号为管理员,更不能将群转让给个人微信号来管理。企业微信作为群主已经成了个摆设,现在只能用个人微信号来艰难的维护群内秩序,要不然就只能放弃之前辛苦建立起来的客户群。 [图片] [图片] 实在是搞不懂,自己这么克制的在使用企业微信,对群成员的管理也非常苛刻,设置了严格的群规来约束群成员。但最终却落得个“永久封禁”,之前努力建起来的几个客户群也前功尽弃了。早知道当初就直接用个人微信建群的,现在这局面太尴尬了,希望竞争对手不要看到这个帖子,怕被他们笑话死。 最后,真诚的希望企业微信在进行永久封禁的时候能给出具体违规的截图,或者至少告知一下是哪个群里的哪个人在哪个时间发布了哪条广告或不良信息导致了我们被永久封禁。这样不明不白的就被永久封禁了,对我们影响实在太大了。如果可以,希望企业微信再次详查一下,在此谢过。(企业ID:ww1be42cb336d98795)
2020-08-21