123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- /**
- * @class BaseMod 数据模型基类,提供基础服务支持
- */
- const {
- getConfig
- } = require('../../shared')
- //基类
- module.exports = class BaseMod {
- constructor() {
- //配置信息
- this.config = getConfig('config')
- //开启/关闭debug
- this.debug = this.config.debug
- //主键
- this.primaryKey = '_id'
- //单次查询最多返回 500 条数据(阿里云500,腾讯云1000,这里取最小值)
- this.selectMaxLimit = 500
- //数据表前缀
- this.tablePrefix = 'uni-stat'
- //数据表连接符
- this.tableConnectors = '-'
- //数据表名
- this.tableName = ''
- //参数
- this.params = {}
- //数据库连接
- this._dbConnection()
- //redis连接
- this._redisConnection()
- }
- /**
- * 建立uniCloud数据库连接
- */
- _dbConnection() {
- if (!this.db) {
- try {
- this.db = uniCloud.database()
- this.dbCmd = this.db.command
- this.dbAggregate = this.dbCmd.aggregate
- } catch (e) {
- console.error('database connection failed: ' + e)
- throw new Error('database connection failed: ' + e)
- }
- }
- }
- /**
- * 建立uniCloud redis连接
- */
- _redisConnection() {
- if (this.config.redis && !this.redis) {
- try {
- this.redis = uniCloud.redis()
- } catch (e) {
- console.log('redis server connection failed: ' + e)
- }
- }
- }
- /**
- * 获取uni统计配置项
- * @param {String} key
- */
- getConfig(key) {
- return this.config[key]
- }
- /**
- * 获取带前缀的数据表名称
- * @param {String} tab 表名
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- getTableName(tab, useDBPre = true) {
- tab = tab || this.tableName
- const table = (useDBPre && this.tablePrefix && tab.indexOf(this.tablePrefix) !== 0) ? this.tablePrefix + this
- .tableConnectors + tab : tab
- return table
- }
- /**
- * 获取数据集
- * @param {String} tab表名
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- getCollection(tab, useDBPre = true) {
- return this.db.collection(this.getTableName(tab, useDBPre))
- }
- /**
- * 获取reids缓存
- * @param {String} key reids缓存键值
- */
- async getCache(key) {
- if (!this.redis || !key) {
- return false
- }
- let cacheResult = await this.redis.get(key)
- if (this.debug) {
- console.log('get cache result by key:' + key, cacheResult)
- }
- if (cacheResult) {
- try {
- cacheResult = JSON.parse(cacheResult)
- } catch (e) {
- if (this.debug) {
- console.log('json parse error: ' + e)
- }
- }
- }
- return cacheResult
- }
- /**
- * 设置redis缓存
- * @param {String} key 键值
- * @param {String} val 值
- * @param {Number} expireTime 过期时间
- */
- async setCache(key, val, expireTime) {
- if (!this.redis || !key) {
- return false
- }
- if (val instanceof Object) {
- val = JSON.stringify(val)
- }
- if (this.debug) {
- console.log('set cache result by key:' + key, val)
- }
- return await this.redis.set(key, val, 'EX', expireTime || this.config.cachetime)
- }
- /**
- * 清除redis缓存
- * @param {String} key 键值
- */
- async clearCache(key) {
- if (!this.redis || !key) {
- return false
- }
- if (this.debug) {
- console.log('delete cache by key:' + key)
- }
- return await this.redis.del(key)
- }
- /**
- * 通过数据表主键(_id)获取数据
- * @param {String} tab 表名
- * @param {String} id 主键值
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- async getById(tab, id, useDBPre = true) {
- const condition = {}
- condition[this.primaryKey] = id
- const info = await this.getCollection(tab, useDBPre).where(condition).get()
- return (info && info.data.length > 0) ? info.data[0] : []
- }
- /**
- * 插入数据到数据表
- * @param {String} tab 表名
- * @param {Object} params 字段参数
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- async insert(tab, params, useDBPre = true) {
- params = params || this.params
- return await this.getCollection(tab, useDBPre).add(params)
- }
- /**
- * 修改数据表数据
- * @param {String} tab 表名
- * @param {Object} params 字段参数
- * @param {Object} condition 条件
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- async update(tab, params, condition, useDBPre = true) {
- params = params || this.params
- return await this.getCollection(tab).where(condition).update(params)
- }
- /**
- * 删除数据表数据
- * @param {String} tab 表名
- * @param {Object} condition 条件
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- async delete(tab, condition, useDBPre = true) {
- if (!condition) {
- return false
- }
- return await this.getCollection(tab, useDBPre).where(condition).remove()
- }
- /**
- * 批量插入 - 云服务空间对单条mongo语句执行时间有限制,所以批量插入需限制每次执行条数
- * @param {String} tab 表名
- * @param {Object} data 数据集合
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- async batchInsert(tab, data, useDBPre = true) {
- let batchInsertNum = this.getConfig('batchInsertNum') || 3000
- batchInsertNum = Math.min(batchInsertNum, 5000)
- const insertNum = Math.ceil(data.length / batchInsertNum)
- let start;
- let end;
- let fillData;
- let insertRes;
- const res = {
- code: 0,
- msg: 'success',
- data: {
- inserted: 0
- }
- }
- for (let p = 0; p < insertNum; p++) {
- start = p * batchInsertNum
- end = Math.min(start + batchInsertNum, data.length)
- fillData = []
- for (let i = start; i < end; i++) {
- fillData.push(data[i])
- }
- if (fillData.length > 0) {
- insertRes = await this.insert(tab, fillData, useDBPre)
- if (insertRes && insertRes.inserted) {
- res.data.inserted += insertRes.inserted
- }
- }
- }
- return res
- }
- /**
- * 批量删除 - 云服务空间对单条mongo语句执行时间有限制,所以批量删除需限制每次执行条数
- * @param {String} tab 表名
- * @param {Object} condition 条件
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- async batchDelete(tab, condition, useDBPre = true) {
- const batchDeletetNum = 5000;
- let deleteIds;
- let delRes;
- let thisCondition
- const res = {
- code: 0,
- msg: 'success',
- data: {
- deleted: 0
- }
- }
- let run = true
- while (run) {
- const dataRes = await this.getCollection(tab).where(condition).limit(batchDeletetNum).get()
- if (dataRes && dataRes.data.length > 0) {
- deleteIds = []
- for (let i = 0; i < dataRes.data.length; i++) {
- deleteIds.push(dataRes.data[i][this.primaryKey])
- }
- if (deleteIds.length > 0) {
- thisCondition = {}
- thisCondition[this.primaryKey] = {
- $in: deleteIds
- }
- delRes = await this.delete(tab, thisCondition, useDBPre)
- if (delRes && delRes.deleted) {
- res.data.deleted += delRes.deleted
- }
- }
- } else {
- run = false
- }
- }
- return res
- }
- /**
- * 基础查询
- * @param {String} tab 表名
- * @param {Object} params 查询参数 where:where条件,field:返回字段,skip:跳过的文档数,limit:返回的记录数,orderBy:排序,count:返回查询结果的数量
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- async select(tab, params, useDBPre = true) {
- const {
- where,
- field,
- skip,
- limit,
- orderBy,
- count
- } = params
-
- const query = this.getCollection(tab, useDBPre)
-
- //拼接where条件
- if (where) {
- if (where.length > 0) {
- where.forEach(key => {
- query.where(where[key])
- })
- } else {
- query.where(where)
- }
- }
-
- //排序
- if (orderBy) {
- Object.keys(orderBy).forEach(key => {
- query.orderBy(key, orderBy[key])
- })
- }
-
- //指定跳过的文档数
- if (skip) {
- query.skip(skip)
- }
-
- //指定返回的记录数
- if (limit) {
- query.limit(limit)
- }
-
- //指定返回字段
- if (field) {
- query.field(field)
- }
-
- //指定返回查询结果数量
- if (count) {
- return await query.count()
- }
-
- //返回查询结果数据
- return await query.get()
- }
- /**
- * 查询并返回全部数据
- * @param {String} tab 表名
- * @param {Object} condition 条件
- * @param {Object} field 指定查询返回字段
- * @param {Boolean} useDBPre 是否使用数据表前缀
- */
- async selectAll(tab, condition, field = {}, useDBPre = true) {
- const countRes = await this.getCollection(tab, useDBPre).where(condition).count()
- if (countRes && countRes.total > 0) {
- const pageCount = Math.ceil(countRes.total / this.selectMaxLimit)
- let res, returnData
- for (let p = 0; p < pageCount; p++) {
- res = await this.getCollection(tab, useDBPre).where(condition).orderBy(this.primaryKey, 'asc').skip(p *
- this.selectMaxLimit).limit(this.selectMaxLimit).field(field).get()
- if (!returnData) {
- returnData = res
- } else {
- returnData.affectedDocs += res.affectedDocs
- for (const i in res.data) {
- returnData.data.push(res.data[i])
- }
- }
- }
- return returnData
- }
- return {
- affectedDocs: 0,
- data: []
- }
- }
- /**
- * 聚合查询
- * @param {String} tab 表名
- * @param {Object} params 聚合参数
- */
- async aggregate(tab, params) {
- let {
- project,
- match,
- lookup,
- group,
- skip,
- limit,
- sort,
- getAll,
- useDBPre
- } = params
- //useDBPre 是否使用数据表前缀
- useDBPre = (useDBPre !== null && useDBPre !== undefined) ? useDBPre : true
- const query = this.getCollection(tab, useDBPre).aggregate()
-
- //设置返回字段
- if (project) {
- query.project(project)
- }
-
- //设置匹配条件
- if (match) {
- query.match(match)
- }
-
- //数据表关联
- if (lookup) {
- query.lookup(lookup)
- }
-
- //分组
- if (group) {
- if (group.length > 0) {
- for (const gi in group) {
- query.group(group[gi])
- }
- } else {
- query.group(group)
- }
- }
-
- //排序
- if (sort) {
- query.sort(sort)
- }
-
- //分页
- if (skip) {
- query.skip(skip)
- }
- if (limit) {
- query.limit(limit)
- } else if (!getAll) {
- query.limit(this.selectMaxLimit)
- }
-
- //如果未指定全部返回则直接返回查询结果
- if (!getAll) {
- return await query.end()
- }
-
- //若指定了全部返回则分页查询全部结果后再返回
- const resCount = await query.group({
- _id: {},
- aggregate_count: {
- $sum: 1
- }
- }).end()
-
- if (resCount && resCount.data.length > 0 && resCount.data[0].aggregate_count > 0) {
- //分页查询
- const total = resCount.data[0].aggregate_count
- const pageCount = Math.ceil(total / this.selectMaxLimit)
- let res, returnData
- params.limit = this.selectMaxLimit
- params.getAll = false
- //结果合并
- for (let p = 0; p < pageCount; p++) {
- params.skip = p * params.limit
- res = await this.aggregate(tab, params)
- if (!returnData) {
- returnData = res
- } else {
- returnData.affectedDocs += res.affectedDocs
- for (const i in res.data) {
- returnData.data.push(res.data[i])
- }
- }
- }
- return returnData
- } else {
- return {
- affectedDocs: 0,
- data: []
- }
- }
- }
- }
|