- 小白变大神三:高级数据库工具函数
《小白变大神,微信小程序云开发快速入门与成本控制实战》系列文章 第三篇:高级数据库工具函数 前言 在上一篇文章中,我们通过完善 Todolist 的功能,介绍了几个常用数据库操作函数。本篇文章将进一步介绍更多的云数据库工具函数,同时还会提供用于云函数中的版本(云函数中不需要考虑查询权限问题,代码略有不同)。 学习完本篇文章后,云数据库的读写讲解将告一段落,你可以把这三篇文章的代码运用到你的项目中,提高开发效率。 由于这篇文章讲解的函数较多,为了控制文章篇幅,我尽量不在文章中展示代码,建议读者先下载代码库,然后一边阅读文章,一边查看代码。 另外,为了方便读者理解,我为代码库中的函数添加了符合JSDoc规范的注释。 获取代码库 WxMpCloudBooster 建议你在阅读本系列文章时,自己新建一个项目,然后跟着我的步骤在你的电脑上实践。因此,在本篇文章中,你需要先获取 WxMpCloudBooster 库中的代码。 你可以在github代码库:sdjl/WxMpCloudBooster下载,或者使用如下的命令: [代码]# 获取项目 git clone https://github.com/sdjl/WxMpCloudBooster.git # 切换到本篇文章(文章三)对应的代码库 cd WxMpCloudBooster git checkout article3 [代码] 注意:建议 checkout 到 article3,否则你拿到的代码可能和本文中不一致 获取单个文档 [代码]getDoc[代码]函数 [代码]/** * 根据id获取数据 * * @param {string} c - 集合名称。 * @param {string} id - 文档的ID。 * @param {Object} options 包含以下属性的对象: * - {string} only - 仅返回的字段,多个字段用逗号分隔,如:'title, content'。 * - {string} except - 不返回的字段。 * - {boolean} mine - 是否只读取用户自己的数据。 * * @returns {Promise<Object|null>} Promise对象,解析为文档或null。 */ getDoc(c, id, {only = '', except = '', mine = false} = {}) { // ... }, [代码] 此函数用于根据文档的ID获取文档。 注意此函数每调用1次就会消耗1次“调用次数”,如果你有一个列表,建议使用docs函数一次读取多个文档并缓存到本地,以减少调用次数。 上一篇文章我们说到,utils.js中的所有数据库操作函数均会根据当前运行环境自动判断操作测试数据库还是正式数据库。若小程序不在本地运行,则会自动给集合名称添加[代码]p_[代码]前缀,表示访问生产环境的集合表,本文中不再赘述。 关于*My*函数 特别注意,当数据库权限设置为“自定义安全规则”且有“auth.openid == doc._openid”规则时,直接使用[代码]getDoc[代码]函数会拿不到数据,返回的doc总是等于null。 此时需要设置[代码]mine[代码]为[代码]true[代码],或者使用[代码]getMyDoc[代码]函数代替[代码]getDoc[代码]。 utils中的数据库操作函数总是成对出现,如[代码]getDoc[代码]和[代码]getMyDoc[代码],[代码]docs[代码]和[代码]myDocs[代码]等。 当需要读取用户自己的数据时,请使用对应的My函数,否则会有权限问题,本文中不再赘述。 getOne 与[代码]getDoc[代码]不同,当你并没有文档的ID,而是需要根据某个条件获取一个文档时,可以使用[代码]getOne[代码]函数。 [代码]/** * 通过查询条件获取第一个匹配的文档 * * @param {string} c - 集合名称。 * @param {Object} w - 查询条件。 * @param {Object} options 包含以下属性的对象: * - {string} only - 仅返回的字段。 * - {string} except - 不返回的字段。 * - {boolean} mine - 是否只读取用户自己的数据。 * - {Object|string} order_by - 排序规则,与其他函数相同。 * - {boolean} last - 是否根据index字段获取index值最大的文档。 * * @returns {Promise<Object|null>} Promise对象,解析为文档或null。 */ getOne(c, w, {only = '', except = '', mine = false, order_by = {}, last = false} = {}) { // ... }, [代码] [代码]getOne[代码]是支持排序的,例如当你想要获取“最近一个订单数据”时,可以使用[代码]order_by[代码]对下单时间进行排序。[代码]order_by[代码]参数的用法在上一篇文章中已经介绍过,你也可以在代码库中查看注释详解。 假设要获得最新已完成的订单,可以这样使用: [代码]const order = await utils.getOne('order', {status: '已完成'}, {order_by: {finished_time: -1}}) [代码] 这里的[代码]finished_time[代码]表示订单完成时间,[代码]-1[代码]表示降序。 getMyLastOne 获取当前用户的最新一个数据是常见需求,例如用户最近的一个评论,用户最近的一个订单等。 因此在本代码库中,我们引入[代码]index[代码]字段,用于记录文档的创建顺序,然后有些[代码]utils[代码]的函数是基于[代码]index[代码]字段的。 [代码]getMyLastOne[代码]函数是[代码]getOne[代码]的一个封装,用于获取当前用户在指定集合中最新创建的文档: [代码]/** * 获取当前用户在指定集合中最新创建的文档。 * 集合中需有index字段,函数返回的是index值最大的文档。 * * @param {string} c - 集合名称。 * @param {Object} w - 查询条件,默认为空。 * * @returns {Promise<Object|null>} Promise对象,解析为文档或null。 */ getMyLastOne(c, w = {}) { return this.getOne(c, w, {mine: true, last: true}) }, [代码] 如果用户订单表中有[代码]index[代码]字段,你可以这样获取当前用户的最新订单: [代码]const order = await utils.getMyLastOne('order') [代码] 此函数会自动过滤掉其他用户的数据。 getMyUniqueOne 有时候,我们明确的知道某个表中一个用户只能有一个文档,例如在[代码]user_setting[代码]表中。 此时使用[代码]getMyUniqueOne[代码]函数获取用户的唯一数据,在语义上更加清晰: [代码]const us = await utils.getMyUniqueOne('user_setting') [代码] 注意这里[代码]user_setting[代码]表在设计上[代码]_openid[代码]应该是唯一索引。 获取所有文档:[代码]allDocs[代码]函数 上一篇文章中我们介绍了[代码]docs[代码]函数,此函数最多只能读取20个文档(受微信系统限制),但有时候我们需要读取更多的文档,甚至需要读取所有文档。 由于数据库的聚合操作(aggregate)没有单次读取20条的限制,因此我使用[代码]aggregate[代码]实现了[代码]allDocs[代码]函数,用于读取集合中的所有文档。 [代码] /** * 获取某集合中所有文档数据。 * * @param {Object} options - 配置参数,包括: * - {string} c - 集合名称。 * - {Object} match - 匹配条件,默认为空对象。 * - {Object} project - 映射阶段 * - {Object} sort - 排序条件,默认按 _id 升序。 * - {boolean} mine - 是否仅查询用户自己的数据,默认为 false。 * - {number} page_size - 每次查询读取的文档数量,默认为 1000,暂无上限。 * - {boolean} show_loading - 是否显示加载动画,默认为 false。 * - {string} only - 仅包含指定字段。 * - {string} except - 排除指定字段。 * - {number} limit - 限制读取的文档数量。 * @returns {Promise<Array>} 返回一个包含查询结果的数组。 */ allDocs ({c, match = {}, project = {}, sort = {_id: 1}, mine = false, page_size = 1000, show_loading = false, only = '', except = '', limit = null } = {}) { // ... } [代码] 直接调用此函数,会返回集合中的所有数据,如: [代码]const all_orders = await utils.allDocs({c: 'order'}) [代码] 但是,此函数实际上会分多次读取,每次读取[代码]page_size[代码]个文档(默认1000),直到读取完所有文档为止。 如果你实际上有3000个订单,那么此函数会读取3次,就会消耗3次调用次数。 为了减少调用次数,你可以把[代码]page_size[代码]设置得更大一些,如3000,这样读取上面的3000个文档就只需要消耗1次调用次数。 但问题是,系统限制前端每次读取的数据总量不能超过5M,当单次读取超过5M时,就会报错。因此,如果你的单个文档较大,建议适当减小[代码]page_size[代码]的值(这可能会增加调用次数)。 如果你确保表中所有数据加起来不会超过5MB,那么你可以把[代码]page_size[代码]设置得大一些,如999999,这样就能实现仅消耗1次调用次数读取所有文档。 [代码]limit[代码]参数 有些时候你只是想要“较多数据”,而不是“全部数据”,此时你可以使用[代码]limit[代码]参数,如获得500个最新的订单: [代码]const orders = await utils.allDocs({c: 'order', sort: {created: -1}, limit: 500}) [代码] 这里的[代码]limit[代码]参数用于限制读取文档的数量,由于[代码]limit[代码]小于[代码]page_size[代码],因此只会消耗1次调用次数。 再看个例子,假如[代码]page_size[代码]为1000,[代码]limit[代码]为5000,并且集合中文档数量超过5000个,那么以下的代码会读取5次,只返回5000个文档: [代码]// order集合中超过5000个文档 const orders = await utils.allDocs({c: 'order', page_size: 1000, limit: 5000}) // 每次读取1000个文档,直至达到limit的数量 console.log(orders.length) // 5000,消耗5次调用次数 [代码] 单个文档超过1M的读取 无论是[代码]getDoc[代码]、[代码]getOne[代码]还是[代码]docs[代码]函数,它们都是普通查询(相对于聚合查询而言)。 对于普通查询,系统限制单次读取的数据总量不能超过1M(对于docs,就是所有文档加起来不能超过1M)。 那么如果你的文档超过1M怎么办? 如果文档大小在1M-5M之间,你可以使用[代码]allDocs[代码]函数配合[代码]limit[代码]参数,如: [代码]// allDocs内部使用聚合,聚合可以查询5M以内的文档 const doc = await utils.allDocs({c: 'big_doc', limit: 1}) console.log(doc[0]) // 注意allDocs返回的是数组 [代码] 如果文档超过5M,可以配合[代码]only[代码]参数,分多次读取,每次仅读取部分字段,如: [代码]// 读取部分字段 const doc1 = await utils.allDocs({c: 'big_doc', limit: 1, only: 'field1, field2'}) // 读取剩余字段 const doc2 = await utils.allDocs({c: 'big_doc', limit: 1, only: 'field3, field4'}) // 合并字段 const doc = {...doc1[0], ...doc2[0]} [代码] 你可能会问:“我怎么知道我的文档有没有超过1M”,我将在本文末尾讲解如何判断数据库中文档的大小。 [代码]allDocs[代码]函数的其他功能 [代码]allDocs[代码]函数内部使用了聚合查询[代码]aggregate[代码],你可以在官方文档中了解更多关于[代码]aggregate[代码]的用法。 [代码]sort[代码]参数用于排序,使用方法和[代码]docs[代码]中的[代码]order_by[代码]参数相同,但是在使用聚合查询时,我们用[代码]sort[代码]表示排序。 同样的[代码]match[代码]参数与docs中的[代码]w[代码]参数相似,但是在使用聚合时,我们使用[代码]match[代码]命名,以区分普通查询。 [代码]allDocs[代码]函数还支持[代码]project[代码]功能,你可以在官方文档中了解。 当你同时使用[代码]project[代码]和[代码]sort[代码]时,函数内部会先执行[代码]project[代码],然后再执行[代码]sort[代码]。 对了,调用[代码]allDocs[代码]函数时用户可能需要等待几秒钟,此时你可以设置[代码]show_loading[代码]参数为[代码]true[代码],以显示系统默认的loading动画,函数会在数据读取完毕后自动关闭loading动画。 其他更新数据函数 [代码]updateMatch[代码]函数 上篇文章我们介绍了[代码]updateDoc[代码]函数,但[代码]updateDoc[代码]一次只能更新一个文档。 如果你希望仅消耗1次调用次数,就可批量更新多个文档,可以使用[代码]updateMatch[代码]函数。 [代码]/** * 批量更新文档。该操作允许更新超过20条文档,上限未知。 * * @param {string} c - 集合名称。 * @param {Object} w - 匹配被更新文档的条件。 * @param {Object} d - 需要更新的数据,支持点表示法,如:{'a.b.c': 1}。 * @param {Object} [options={mine: false}] * @returns {Promise<Number>} 返回一个 Promise 对象,解析为更新的文档数量。 */ updateMatch(c, w, d, {mine = false} = {}) { // ... } [代码] 若想把所有“未完成”订单的状态都改为“已完成”,可以这样使用: [代码]const count = await utils.updateMatch('order', {status: '未完成'}, {status: '已完成'}) [代码] 有时候,你希望只是删除某个字段,例如删除订单的重量字段[代码]weight[代码],可以这样使用: [代码]const $ = utils.command() const count = await utils.updateMatch('order', {weight: $.exists(true)}, {weight: undefined}) [代码] 上面代码中的第二个参数表示仅修改有[代码]weight[代码]字段的数据,这是可选的。 [代码]updateMatch[代码]有一个限制,即更新时所有匹配的数据设置的值必须是相同的,假如你想给所有订单设置一个[代码]created[代码]字段表示订单创建时间,但每个订单的创建时间是不同的,那么你就不能使用[代码]updateMatch[代码]函数。 微信云数据库的API不支持这种批量更新不同值的操作,但在本文末尾我会提供一种解决方案。 [代码]undefinedToRemove[代码]函数 在[代码]updateMatch[代码]的第三个参数中,我们设置[代码]weight[代码]为[代码]undefined[代码],这样就会删除[代码]weight[代码]字段。 但注意,如果你使用微信默认API,当你设置某字段为[代码]undefined[代码]时,系统会抛出异常,如图所示: [图片] [图片] 但在实际运用中,我们设置一个字段为[代码]undefined[代码]时,我们期望的就是删除这个字段。因此,我在[代码]utils.js[代码]中提供了[代码]undefinedToRemove[代码]函数,并且在[代码]updateMatch[代码]中默认使用了[代码]undefinedToRemove[代码]函数。 [代码]/** * 递归搜索,把对象或数组中所有undefined设置为数据库删除命令。 * @param {Object|Array} obj - 需要处理的对象或数组。 * @returns {Object|Array} 返回处理后的新对象或数组。 */ undefinedToRemove(obj){ // ... } [代码] 传给[代码]undefinedToRemove[代码]函数的参数,可以是一个对象,也可以是一个数组,函数会递归搜索所有值为[代码]undefined[代码]的属性,并替换为数据库删除命令。 假设你想要删除数据库中的[代码]a.b.c[代码]字段,可以这样写: [代码]const doc = {a: {b: {c: undefined}}} const doc2 = utils.undefinedToRemove(doc) // 此时doc2.a.b.c的值为数据库删除命令 // 删除一个文档的属性 utils.updateDoc('todo', 'id123', doc2) // updateDoc需要手动调用undefinedToRemove // 但doc.a.b.c的值仍然为undefined // 删除多个文档的属性 utils.updateMyMatch('todo', w, doc) // updateMyMatch会自动调用undefinedToRemove [代码] [代码]setDoc[代码]函数 [代码]updateDoc[代码]函数只会更新你在第三个参数[代码]d[代码]中所指定的属性,对于你没有明确指定的属性,[代码]updateDoc[代码]不会做任何更改。 但有时候,你希望把整个文档替换成一个新的对象,而不是更新部分字段。这时你可以使用[代码]setDoc[代码]函数。 [代码]/** * 替换指定ID的文档为新的文档 * @param {string} c - 集合名称。 * @param {string} id - 文档的ID。 * @param {Object} d - 新的文档数据。 * @returns {Promise<Object>} 返回一个包含创建和更新状态的Promise对象。 */ setDoc (c, id, d) { // ... } [代码] 如果[代码]setDoc[代码]指定的id不存在,则会创建一个新的文档。 提醒:如果能用[代码]updateDoc[代码],就不要用[代码]setDoc[代码],因为微信限制每次写入或更新的数据不能超过512KB,使用[代码]setDoc[代码]容易超过这个限制。本文末尾处会进一步讨论数据库的API对数据大小的限制。 其他删除数据函数 [代码]removeMatch[代码]函数 同样的,上篇文章中的[代码]removeDoc[代码]只能删除一个文档,若希望仅消耗1次调用次数删除多个文档,可使用[代码]removeMatch[代码]函数。 [代码]/** * 批量删除匹配条件的文档,可以删除超过20条文档(上限未知)。 * @param {string} c - 集合名称。 * @param {Object} w - 匹配被删除的文档的条件。 * @param {Object} [options={mine: false}] - 可选参数,包括权限控制。 * @returns {Promise<number>} 返回一个代表被删除文档数量的Promise对象。 */ removeMatch(c, w, {mine = false} = {}) { // ... } [代码] 例如删除当前用户所有已完成的todo: [代码]const count = await utils.removeMyMatch('todo', {status: '已完成'}) console.log(`删除了 ${count} 个todo`) [代码] [代码]removeAll[代码]函数 开发阶段,你可能需要清空某个表中的所有数据,此时可以使用[代码]removeAll[代码]函数,此函数只需要输入集合名称即可。 [代码]/** * 删除集合中的所有数据。 * @param {string} c - 集合名称。 * @returns {Promise<number>} 返回一个代表被删除文档数量的Promise对象。 */ removeAll(c) { // ... } [代码] 如果你希望向用户提供一个“清空自己的数据”的功能,可以使用[代码]removeMyAll[代码]函数。 其他数据库操作函数 [代码]exists[代码]:根据文档ID或查询条件判断文档是否存在。 [代码]count[代码]: 根据查询条件统计文档数量。 [代码]getMaxFeild[代码]: 获取某个字段的最大值。 [代码]getMinFeild[代码]: 获取某个字段的最小值。 以上函数的用法请查看代码库中的注释。 云函数中的版本 在云函数中操作数据库时,不需要考虑权限问题,代码默认拥有所有操作权限,因此,云函数中不存在*My*函数。 在[代码]WxMpCloudBooster[代码]代码库中,我为云函数提供了一个专门的文件[代码]for_cloud/utils/utils.js[代码]。 你可以把[代码]for_cloud/utils[代码]目录放在云函数的根目录下,复制后目录结构如下: [代码]cloudfunctions/你的云函数名称/utils/utils.js [代码] 然后使用下面的代码导入云函数的[代码]utils[代码]模块: [代码]const sh = require('utils/utils.js') [代码] 以下是[代码]for_cloud/utils/utils.js[代码]文件中的部分函数,这些函数的功能与前端版本一致,不再赘述。 [代码]/* 获取数据库查询指令 */ command() {} /* 获取聚合查询指令 */ aggregate() {} /* 获取指定集合的引用 */ coll() {} /* 返回聚合查询对象 */ agg() {} /* 获取指定集合中的1000条数据(或更多) */ docs() {} /* 获取指定集合中的所有数据 */ allDocs() {} /* 更新指定的文档 */ updateDoc() {} /* 批量更新文档 */ updateMatch() {} /* 替换指定ID的文档为新的文档 */ setDoc() {} /* 删除指定的文档 */ removeDoc() {} /* 批量删除匹配条件的文档 */ removeMatch() {} /* 根据id获取数据 */ getDoc() {} /* 通过查询条件获取第一个匹配的文档 */ getOne() {} /* 向指定集合中添加一个文档 */ addDoc() {} /* 根据集合名和条件判断数据是否存在 */ exists() {} /* 获取集合中满足条件的文档数量 */ count() {} /* 获取指定集合中的最大index值加一 */ getNextIndex() {} /* 获取集合中某字段的最大值 */ getMaxFeild() {} /* 获取集合中某字段的最小值 */ getMinFeild() {} [代码] [代码]addDocList[代码]批量插入函数 微信前端的云数据库API是不支持批量插入数据的,但云函数中可以,因此云函数的[代码]utils.js[代码]中提供了[代码]addDocList[代码]函数,用于批量插入数据: [代码]/** * 批量插入数据到指定的集合中(只有云端可以批量插入) * @param {string} c - 集合名称 * @param {Array} doc_list - 需要插入的文档列表 * @returns {Promise<Object>} 返回一个Promise,其解析结果为一个对象,包含了插入的文档的ID列表和文档数量 * * @example * // 使用示例 * utils.addDocList('coll', [doc1, doc2, ...]) * .then(({ids, len}) => { * console.log(`插入了 ${len} 条数据,ID列表为:${ids}`) * }) */ addDocList(c, doc_list) { // ... } [代码] 假设你有一个[代码]doc_list[代码],里面包含了10000个订单数据,你可以这样插入: [代码]const {ids, len} = await utils.addDocList('order', doc_list) [代码] 在返回的结果中,[代码]ids[代码]是插入的文档的ID列表,[代码]len[代码]是实际插入的文档数量。 你可能好奇,如果通过此函数插入一万条数据,会消耗几次调用次数?幸运的是,[代码]addDocList[代码]无论插入多少条数据,都只消耗1次调用次数。 如果前端有批量插入需求,可以在前端把[代码]doc_list[代码]传给云函数,然后在云函数中调用[代码]addDocList[代码]函数。 如何批量更新不同值? 前面说到,在实际开发中可能会有这么一种需求,即给不同的数据更新不同的值。这分两种情况: 给多个数据的同一个字段更新不同的值,如需要给每一个订单添加一个created字段,表示这个订单的下单时间,但是每个订单的下单时间是不同的。 给多个数据的不同字段更新不同的值,如有些订单需要添加created字段,但有些订单需要添加updated字段,且每个订单的更新值也都不同。 无论是前端还是云函数中,微信云数据库的API都不支持上述两种批量更新操作。如果你要更新一万个数据,就只能调用一万次[代码]updateDoc[代码]函数,这样就会消耗10000次调用次数。 幸运的是,我们可以使用[代码]addDocList[代码]函数,仅需消耗3次调用次数,就可以实现这种需求,步骤如下: 在云函数中新建一个数据库事务(可选步骤)。 读取所有需要更新的数据,并修改数据(消耗1次)。 使用[代码]removeMatch[代码]或[代码]removeAll[代码]函数删除所有需要更新的数据(消耗1次)。 使用[代码]addDocList[代码]函数插入修改后的数据(消耗1次)。 提交数据库事务(可选步骤)。 这样,你就可以实现批量更新不同数据的需求,且仅消耗3次调用次数,同时数据的[代码]_id[代码]字段不会改变。 注意:如果你要更新的数据超过了5M,你就不得不分多次处理,见本文末尾的“云端限制“一节。 数据库事务是否可以节约调用次数? 这里你可能会有一个疑问:“在上面的例子中,我使用了数据库的事务功能,那么我只在第5步发起commit时才会访问数据库,会不会应该只消耗一次数据库调用次数呢(因为只有一次commit嘛)?” 很遗憾,在微信云数据库中,无法通过使用事务减少数据库调用次数,无论你是否使用事务功能,调用次数的计算方式不变。 使用开发者工具导入 对了,除了前面说的使用[代码]addDocList[代码]函数批量插入数据外,你还可以使用开发者工具的导入功能,且不会消耗调用次数。 你可以按照下面这个步骤批量更新数据: 在开发者工具中导出数据; 使用python、js或其他语言修改数据,并输出一个新的json文件; 在开发者工具中导入新的json文件。 不过,这需要你手动操作,若希望程序自动化实现,可使用云数据库的HTTP API(但这可能会消耗调用次数)。 云数据库的限制 前端限制 前端操作云数据库时,有以下限制: get请求每次最多读取20条数据,单次返回数据总大小不能超过1M。 聚合请求最多读取10000(一万)条数据,单次返回数据总大小不能超过5M。 add写入数据操作,单次写入数据不可以超过512KB。 update更新数据操作,单次更新数据不可以超过512KB。 云端限制 get请求默认每次最多读取100条数据,但修改[代码]limit[代码]参数后可以超过100条,上限未知,单次返回数据总大小不能超过50M。 聚合请求读取数据条数的上限未知,单次返回数据总大小不能超过50M。 add写入数据操作,单次写入数据不可以超过5M。 update更新数据操作,单次更新数据不可以超过5M。 [代码]addDocList[代码]函数实际使用了[代码]add[代码]操作,因此[代码]addDocList[代码]函数的单次写入数据不可以超过5M。 注意:以上除“get函数前端20条、云端100条”的限制外,其他限制是我自己测试总结的,微信官方文档中并没有说明,未来微信官方可能会调整这些限制。 如何知道文档的大小? 那么问题来了,我们怎么知道一个文档的大小呢?这里提供一个工具函数: [代码]/** * 返回一个字符串或对象有多少K(仅保留一位小数)。 * * @param {any} obj - 要计算 K 长度的对象或字符串 * @returns {string} 返回计算后的 K 长度,保留一位小数 */ getKLen (obj) { // ... } [代码] 你可以在调用[代码]addDoc[代码]或[代码]updateDoc[代码]之前,先把整个文档传入[代码]getKLen[代码]函数,然后把文档大小保存到某个变量中,如: [代码]const doc = {a: 1, b: 'hello', c: [1, 2, 3]} const doc.size_k = utils.getKLen(doc) // size_k记录了doc有多少k utils.addDoc('coll', doc) [代码] 这样你就可以在微信开发者工具中查看[代码]size_k[代码]的值,如果[代码]size_k[代码]=10,表示这个文档大小为10K左右(注意实际大小会有一定的偏差)。 后期补充内容 聚合版本的[代码]getDoc[代码]等函数 受系统限制,[代码]getDoc[代码]、[代码]getOne[代码]、[代码]getMyDoc[代码]、[代码]getMyOne[代码]这几个函数在读取单个文档时,如果文档大小超过1M,会抛出异常。 但是聚合查询可以超过1M的限制,最高读取5M的文档。那么,是否可以用聚合查询的方式重新实现上面这4个函数呢? 对头,[代码]utils.js[代码]库还提供了[代码]getDocByAgg[代码]、[代码]getOneByAgg[代码]、[代码]getMyDocByAgg[代码]、[代码]getMyOneByAgg[代码]这几个函数,可读取5M以内的单个文档,接口与前面的函数基本一致。 注意:这4个新函数是后期补充的,请使用[代码]git checkout article5[代码]命令切换到第5篇文章对应的代码库查看。 代码库 本系列教程搭配了一个github代码库:sdjl/WxMpCloudBooster,你可以在这里找到文章中的代码: [代码]# 获取项目 git clone https://github.com/sdjl/WxMpCloudBooster.git # 切换到文章三对应的代码库 cd WxMpCloudBooster git checkout article3 [代码] 我每发布一篇文章,就会提交一个 commit,你可以使用 git checkout article + n 来切换到第 n 篇文章对应的代码。 下篇预告 好了,到目前为止我们已经通过三篇文章介绍了云数据库的基本操作,数据库的学习将告一个段落,下一篇文章将开启其他内容的学习。 本文作者刘永辉,安顺果然赞科技有限公司,转载请注明出处。
09-23 - 【已成功】只用云开发,如何发送公众号模板消息?
下发统一消息接口回收后,如果你只有云开发,没有服务器和域名,不妨尝试使用本文的方法来发送公众号模板消息。 前提: 小程序和公众号同主体且已绑定到同一个微信开放平台账号。 思路推理: 小程序用户访问任意云函数都可以拿到小程序openid和unionid,提前将它们保存到云数据库用户集合中。 发送模板消息: 使用云函数A发送模板消息,需要调用公众号发送模板消息的接口,调用接口需要用到公众号的access_token。 获取和保存access_token: 使用云函数B获取access_token,推荐使用Stable Access token接口获取,减少出错率,云函数B可固定IP。因接口有日调用限制且access_token默认2小时过期,获取到access_token之后需要保存到云数据库中。为防止access_token过期,推荐云函数B设置定时触发,每隔1小时执行一次去重新获取access_token保存到云数据库中。 使用最新access_token: 每次发模板消息时从云数据库查询最新的access_token记录,发送模板消息需要用到公众号的openid。 获取公众号openid: 将小程序的云环境共享给公众号,小程序可使用云函数C来获取公众号的openid和unionid。如何操作呢? 1、使用云开发静态网站制作一个授权页面D,在该页面中访问云函数C,使用静默授权方式访问在云函数C即可获取到访问用户的公众号openid和unionid。 2、小程序使用webview来访问页面D,授权成功后在页面显示公众号的二维码,提示用户关注公众号获取通知功能。 将小程序和公众号用户关联: 通过上述引导用户获取到的公众号unionid来查询云数据库中的用户信息,保存公众号的openid到用户信息中。 经历以上安排之后,小程序云数据库的用户集合中,用户信息已经包含了小程序openid、unionid、公众号openid,到发消息的时机就可以发了。
2023-09-24