UNPKG

@xuda.io/xuda-dbs-plugin-xuda

Version:

Xuda Database Socket for Xuda's proprietary structure powered by CouchDB

1,086 lines (950 loc) 31.4 kB
const check_unique = async function (e, docP, table_obj, db) { const validate_unique_index_exist = function () { let unique_exist = false; for (var val of table_obj.tableIndexes) { if (val.data.unique) { unique_exist = true; break; } } return unique_exist; }; if ( validate_unique_index_exist() && (typeof func === 'undefined' || // call from studio !e.full_table_downloaded) ) { const SESSION_ID = Object.keys(SESSION_OBJ)[0]; const ret = await func.common.perform_rpi_request(SESSION_ID, 'dbs_check_unique', {}, { ...e, doc: docP }); return ret || { code: -1, data: {} }; } const local = async function () { var len = docP.udfIndex.length; if (!len) { return { code: -11, data: 'table must have primary index' }; } var found_unique = undefined; var ret = { code: 1, data: 'ok' }; for await (var val of docP.udfIndex) { if (!table_obj.tableIndexes) { ret = { code: -14, data: 'table definition missing for: ' + val.fileId, }; break; } const table_index = find_item_by_key_root(table_obj.tableIndexes, 'id', val.indexId); if (!table_index) { ret = { code: -13, data: 'keys definition missing for Table: ' + val.fileId, }; break; } if (!table_index.data.unique) { continue; } if (!table_index?.data?.keys || _.isEmpty(table_index.data.keys)) { ret = { code: -14, data: 'keys definition missing for index: ' + val.indexId, }; break; } var keysArr = table_index.data.keys; var keyValue = []; for await (const [keySegment, valSegment] of Object.entries(keysArr)) { // run on key segment let _fieldType = find_item_by_key(table_obj.tableFields, 'field_id', valSegment).props.fieldType; let _val = await get_cast_val('check_unique', valSegment, _fieldType, val.keyValue[Number(keySegment)]); keyValue.push(_val); } var key = '["' + val.fileId + '","' + val.indexId + '",[' + keyValue.join(',') + ']]'; try { e.view = 'db_index'; const ret = await query_db(e, db, table_obj); const json = ret.data; // var data; if (json.code > 0) { var data = json.data; if (data?.rows?.length) { for await (var val of data.rows) { if (val.id !== docP._id) { found_unique = true; } } if (found_unique) { throw 'error checking unique:' + key; } } } // throw json.data; return { code: 1, data: 'ok' }; } catch (msg) { console.error(msg); ret = { code: -22, data: msg }; break; } } return ret; }; return await local(); }; const get_index_json = async function (docInP, table_obj) { try { var keysArr = []; var index_json = []; var key_val = []; if (docInP.udfData && table_obj) { // check for udf data array if (table_obj.tableIndexes) { // console.log(udfDicIndexT.rows); for await (var valIndex of table_obj.tableIndexes) { // run on index rows keysArr = valIndex.data.keys; // create array from keys string segment key_val = []; for await (var valSegment of keysArr) { // run on key segment // find keys values if (docInP.udfData.data[valSegment]) { key_val.push(docInP.udfData.data[valSegment]); continue; } // put the value // debugger; const field_obj = find_item_by_key(table_obj.tableFields, 'field_id', valSegment); if (!field_obj) { throw 'field not found in key: ' + valSegment; } let _val = await get_cast_val('get_index_json', field_obj.id, field_obj.props.fieldType, null); key_val.push(_val); } index_json.push({ fileId: docInP.udfData.udffileid, indexId: valIndex.id, keySegments: keysArr, keyValue: key_val, }); } // ); } } return { code: 1, data: index_json }; } catch (msg) { return { code: -1, data: msg }; } }; const find_item_by_key_root = function (arr, key, val) { return _.find(arr, function (e) { return e[key] === val; }); }; const find_item_by_key = function (arr, key, val) { return _.find(arr, function (e) { return e.data[key] === val; }); }; const get_cast_val = async function (source, attributeP, typeP, valP) { const success = function (res) { var msg = `error converting ${attributeP} from ${valP} to ${typeP}`; console.error(source, msg); }; const fail = function (res) { var msg = `type mismatch auto conversion made to ${attributeP} from value ${valP} to ${typeP}`; console.warn(source, msg); }; if (typeof xu_cast === 'undefined') { const xu_cast = await func.common.get_module(Object.keys(SESSION_OBJ)[0], `xuda-get-cast-util-module.mjs`); return xu_cast.cast(typeP, valP, success, fail); } return xu_cast(typeP, valP, success, fail); }; const get_opt = function (e, table_obj) { var limit = 99999; var skip = 0; if (e.limit) { limit = Number(e.limit); } if (e.skip) { skip = JSON.parse(e.skip); } var fields = []; if (e.fields) { for (const [key, val] of Object.entries(typeof e.fields === 'string' ? e.fields.split(',') : e.fields)) { fields.push(val); } } else { for (const [key, val] of Object.entries(table_obj.tableFields)) { fields.push(val.data.field_id); } } var data = {}; var from = e.filter_from ? JSON.parse(e.filter_from) : {}; var to = e.filter_to ? JSON.parse(e.filter_to) : {}; var sort = []; for (const [key, val] of Object.entries(from)) { var field_name = key; sort.push(field_name); if (val === to[key]) { data[field_name] = val; } else { data[field_name] = { $gte: val }; } } for (const [key, val] of Object.entries(to)) { var field_name = key; if (val !== from[key]) { data[field_name]['$lte'] = val; } } var selector = {}; if (from && !_.isEmpty(from)) { selector = data; } var opt = { selector, limit, skip, fields, }; if (sort.length) { opt.sort = sort; } let _sortModel = e?.sortModel || []; if (typeof _sortModel === 'string') { _sortModel = JSON.parse(e.sortModel); } if (_sortModel.length) { opt.sort = _sortModel; if (opt.sort) { for (const [key, val] of Object.entries(opt.sort)) { var field_name = val.field_id; opt.sort[key] = { ['udfData.data.' + field_name]: val.sort_dir, }; } } } // if (e?.sortModel && JSON.parse(e.sortModel).length) { // opt.sort = JSON.parse(e.sortModel); // if (opt.sort) { // for (const [key, val] of Object.entries(opt.sort)) { // var field_name = val.colId; // opt.sort[key] = { // ["udfData.data." + field_name]: val.sort_dir, // }; // } // } // } if (e.total_fields_info) { fields = []; for (const [key, val] of Object.entries(JSON.parse(e.total_fields_info))) { var field_name = val.field_id; fields.push(field_name); } opt.fields = fields; } // fix names for (const [key, val] of Object.entries(opt.fields)) { opt.fields[key] = 'udfData.data.' + val; } if (!e?.sortModel || !JSON.parse(e.sortModel).length) { // added 2021 09 10 if (opt.sort) { for (const [key, val] of Object.entries(opt.sort)) { opt.sort[key] = { ['udfData.data.' + val]: e.sortOrder === 'des' ? 'desc' : 'asc', }; } } } var selector_new = {}; function replaceKeysInQuery(query) { const keys_to_replace = table_obj.tableFields.map((e) => { return e?.data?.field_id; }); // Helper function to recursively process the query object function recursiveReplace(obj) { if (typeof obj === 'object' && obj !== null) { // Create a new object to store the modified query let newObj = Array.isArray(obj) ? [] : {}; // Traverse through the object for (let key in obj) { if (obj.hasOwnProperty(key)) { // If the key is in the keys_to_replace array, replace it let newKey = keys_to_replace.includes(key) ? `udfData.data.${key}` : key; // Recursively process nested objects newObj[newKey] = recursiveReplace(obj[key]); } } return newObj; } else { // If it's not an object or array, return the value as is return obj; } } // Start the recursive replacement return recursiveReplace(query); } function replaceRegexOptions(query) { // Helper function to recursively process the query object function recursiveReplace(obj) { if (typeof obj === 'object' && obj !== null) { // Create a new object to store the modified query let newObj = Array.isArray(obj) ? [] : {}; // Traverse through the object for (let key in obj) { if (obj.hasOwnProperty(key)) { // If the key is in the keys_to_replace array, replace it // let newKey = keys_to_replace.includes(key) // ? `udfData.data.${key}` // : key; if (key === '$regex') { newObj[key] = obj[key]; // RegExp(obj[key], obj.$options); return newObj; } // Recursively process nested objects // if (key !== "$options") { newObj[key] = recursiveReplace(obj[key]); // } } } return newObj; } else { // If it's not an object or array, return the value as is return obj; } } // Start the recursive replacement return recursiveReplace(query); } if (e.dataSourceFilterModelType === 'query' && e.filterModelMongo) { selector_new['$and'] = [replaceRegexOptions(replaceKeysInQuery(JSON.parse(e.filterModelMongo)))]; } if (e.filterModelUserMongo) { if (!selector_new['$and']) { selector_new['$and'] = []; } selector_new['$and'].push(replaceRegexOptions(replaceKeysInQuery(JSON.parse(e.filterModelUserMongo)))); } if (e.dataSourceFilterModelType === 'index') { for (const [key, val] of Object.entries(opt.selector)) { selector_new['udfData.data.' + key] = val; } } opt.selector = selector_new; opt.selector['udfData.udffileid'] = e.table_id; opt.selector['docType'] = 'database'; opt.selector.stat = e.archived ? 4 : 3; // fill selector with sort field if not exist if (opt.sort) { for (let val of opt.sort) { let field_id = Object.keys(val)[0]; if (!opt.selector[field_id]) { opt.selector[field_id] = { $gt: null }; } } } opt.fields.push('_id'); opt.fields.push('_rev'); if (e.selector) { opt.selector = { ...e.selector, ...opt.selector }; } return opt; }; const query_db = async function (e, db, table_obj) { var key = undefined; var opt = get_opt(e, table_obj); const runtime_get_mango_data = async function () { const done = async function (body) { const raw_data = async function () { var rows = []; for (var val of body.docs) { var data = {}; if (e.fields) { for (const [key2, val2] of Object.entries(typeof e.fields === 'string' ? e.fields.split(',') : e.fields)) { if (val.udfData) { data[val2] = val['udfData']['data'][val2]; } else { data[val2] = null; } } } else { for (const [key2, val2] of Object.entries(table_obj.tableFields)) { if (val.udfData) { data[val2.data.field_id] = val['udfData']['data'][val2.data.field_id]; } else { data[val2.data.field_id] = null; } } } rows.push({ id: val._id, value: data, }); } return { code: 1, data: { rows: rows, total_rows: rows.length, opt } }; }; const count_data = async function () { var rows = []; var keys_obj = {}; const table_index = find_item_by_key_root(table_obj.tableIndexes, 'id', e.indexId); if (e.indexId && table_index) { var index_keys = table_index.data.keys; var index_id = e.indexId; for (var val of body.docs) { var data = {}; const _tableFieldsObj = find_item_by_key(table_obj.tableFields, 'field_id', e.field_id); for (const [key2, val2] of Object.entries(table_obj.tableFields)) { data[val2.data.field_id] = val['udfData']['data'][val2.data.field_id]; } var key_arr = []; for (const [key_idx, key_name] of Object.entries(index_keys)) { key_arr.push(data[key_name]); } keys_obj[key_arr]++; } for (const [key_arr, key_count] of Object.entries(keys_obj)) { rows.push({ key: [e.key, index_id, key_arr], value: body.docs.length, }); } } else { // no index rows.push({ key: '', value: body.docs.length }); } return { code: 1, data: { rows: rows, total_rows: rows.length, opt } }; }; const totals = async function () { const median = (arr) => { const mid = Math.floor(arr.length / 2), nums = [...arr].sort((a, b) => a - b); return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2; }; var total_fields_info = JSON.parse(e.total_fields_info); var totals_prop = {}; var totals_obj = {}; var totals_counts = {}; var totals_arr = {}; var totals_sums = {}; var totals_group_obj = {}; for (const [key, val] of Object.entries(total_fields_info)) { totals_counts[val.field_id] = 0; totals_prop[val.field_id] = val; totals_arr[val.field_id] = []; totals_sums[val.field_id] = 0; totals_group_obj[val.field_id] = {}; } for (var row_data of body.docs) { for (const [key, val] of Object.entries(row_data.udfData.data)) { var field_id = key; var value = val; totals_counts[field_id]++; switch (totals_prop[field_id].sum_type) { case 'sum': if (typeof totals_obj[field_id] === 'undefined') { totals_obj[field_id] = value; } else { totals_obj[field_id] += value; } break; case 'average': totals_sums[field_id] += value; totals_obj[field_id] = totals_sums[field_id] / totals_counts[field_id]; break; case 'median': if (!totals_arr[field_id].includes(value)) { totals_arr[field_id].push(value); } totals_obj[field_id] = median(totals_arr[field_id]); break; case 'min': if (typeof totals_obj[field_id] === 'undefined' || value < totals_obj[field_id]) { totals_obj[field_id] = value; } break; case 'max': if (typeof totals_obj[field_id] === 'undefined' || value > totals_obj[field_id]) { totals_obj[field_id] = value; } break; case 'count': if (typeof totals_obj[field_id] === 'undefined') { totals_obj[field_id] = 1; } else { totals_obj[field_id]++; } break; case 'distinct': if (!totals_arr[field_id].includes(value)) { totals_arr[field_id].push(value); } totals_obj[field_id] = totals_arr[field_id].length; break; case 'group': if (!totals_obj[field_id]) { totals_obj[field_id] = {}; } if (typeof totals_obj[field_id][value] === 'undefined') { totals_obj[field_id][value] = 1; //value; } else { totals_obj[field_id][value]++; //+= value; } break; default: totals_obj[field_id] = null; } } } return { code: 1, data: totals_obj }; }; if (e.count) { return count_data(); } if (e.total_fields_info) { return totals(); } return raw_data(); }; // xuda try { try { // console.log("opt", opt); const doc = await db.find(opt); var mango_index_obj; if (doc?.warning?.includes('No matching index found')) { try { const index_name = `index_${e.table_id}_${new Date().valueOf().toString()}`; var index = []; for (const [key, val] of Object.entries(opt.selector)) { index.push(key); } mango_index_obj = { index: { fields: index, }, name: index_name, ddoc: `mango_index_table_${e.table_id}`, }; const result = await db.createIndex(mango_index_obj); // console.log("createIndex", result); throw new Error('creating index in progress'); } catch (err) { return { code: -1, data: err.message }; } } return await done(doc); } catch (err) { if (err.message.includes('Cannot sort on field')) { const index_name = `index_${e.table_id}_${new Date().valueOf().toString()}`; // const _selector = _.cloneDeep(opt.selector); let _sort = _.cloneDeep(opt.sort); mango_index_obj = { index: { fields: [], }, name: index_name, ddoc: `mango_index_table_${e.table_id}`, }; delete _sort['$and']; for (let val of _sort) { let field_id = Object.keys(val)[0]; mango_index_obj.index.fields.push(field_id); } // mango_index_obj = { // index: { // fields: Object.keys( // _.reduce( // _sort, // (ret, val) => { // ret[Object.keys(val)[0]] = val; // return ret; // }, // _selector // ) // ), // }, // name: index_name, // ddoc: `mango_index_table_${e.table_id}`, // }; try { const result = await db.createIndex(mango_index_obj); // const monitor_indexing = async function () { // return new Promise((resolve, reject) => { // db.on("indexing", function (event) { // // called when indexes are updated // console.log("indexes event", event); // // resolve(); // }); // setTimeout(() => { // resolve(); // }, 5000); // }); // }; // await monitor_indexing(); // throw new Error({ code: -88, data: "creating index" }); throw new Error('creating index in progress'); // const doc = await db.find(opt); // return await done(doc); } catch (err) { return { code: -1, data: err.message }; } } else { return { code: -1, data: err.message }; } } } catch (err) { return { code: -1, data: err.message }; } }; const runtime_get_query_data = async function () { var limit = 99999; var skip = 0; if (e.limit) { limit = e.limit; } if (e.skip) { skip = e.skip; } var view_opt = undefined; if (key) { view_opt = { key, include_docs: !e.ids && view !== 'db_index_reduce', limit, skip, }; } else { if (e.reduce) { view_opt = { startkey: e.startkey ? JSON.parse(e.startkey) : '', endkey: e.endkey ? JSON.parse(e.endkey) : '', include_docs: false, reduce: true, group_level: 3, descending: e.desc, }; } else { view_opt = { startkey: e.startkey ? JSON.parse(e.startkey) : '', endkey: e.endkey ? JSON.parse(e.endkey) : '', include_docs: !e.ids, limit, skip, descending: e.desc, }; } } try { const body = await db.query(`xuda/${view}`, view_opt); if (e.reduce) { let rows = []; const table_index = find_item_by_key_root(table_obj.tableIndexes, 'id', e.indexId); for await (var val of body.rows) { const arr = val.key[2]; var obj = {}; for await (const [arr_key, arr_val] of Object.entries(arr)) { let field_id = table_index.data.keys[Number(arr_key)]; const _tableFieldsObj = find_item_by_key(table_obj.tableFields, 'field_id', field_id); let field_value = await get_cast_val('dbs_update', e.field_id, _tableFieldsObj.props.fieldType, arr_val); obj[field_id] = field_value; } obj['REDUCE_VALUE'] = val.value; rows.push({ id: crypto.randomUUID(), value: obj }); } return { code: 1, data: { rows, opt } }; } var rows = []; for await (var val of body.rows) { if (e.ids) { rows.push({ id: val.id, value: val.value._id }); continue; } rows.push({ id: val.id, value: val.doc.udfData, // value: { udfData: val.doc.udfData, _id: val.id }, }); } // const reduce_rows = () => { // const reduce_obj = body.docs.reduce( // (params, data) => { // const fx = (val, idx) => { // const group_by_field = data[params.sort[idx]]; // if (params.sort[idx + 1]) { // val[group_by_field] = val[group_by_field] ?? {}; // val[group_by_field][data[params.sort[idx + 1]]] = 0; // idx++; // fx(val[group_by_field], idx); // return; // } // val[group_by_field] = val[group_by_field] ?? 0; // val[group_by_field] += 1; // }; // fx(params.group, 0); // return params; // }, // { sort: opt.sort, group: {} } // );} return { code: 1, data: { rows: rows, total_rows: rows.length, opt } }; } catch (err) { return { code: -1, data: err.message }; } }; const count_tables = async function () { var opt = { reduce: true, group_level: 1, }; if (e.table_id) { opt.key = e.table_id; } const body = await db.query('xuda/db_table_counts', opt); return { code: 1, data: body }; }; var view; if (e.reduce && JSON.parse(e.reduce)) { view = 'db_index_reduce'; return await runtime_get_query_data(); } if (e.view === 'db_index') { key = e.key; view = e.view; return await runtime_get_query_data(); } if (e.count && !e.filter_from) { // count tables return await count_tables(); } return await runtime_get_mango_data(); }; export const create = async (params, resolve, reject) => { const e = params.e; const db = params.db; const table_obj = params.table_obj; const create_data_obj = async function (table_data) { var doc = {}; for await (const val of table_obj.tableFields) { doc[val.data.field_id] = await get_cast_val('dbs_create', val.data.field_id, val.props.fieldType, table_data[val.data.field_id]); } return doc; }; const get_uuid = function (table_id) { return 'dbs-' + table_id + '_' + crypto.randomUUID(); }; const batch = async function () { var docs = []; try { for await (var val of e.table_data) { var doc = { _id: get_uuid(e.table_id), docType: 'database', user: e.uid, app_id: e.app_id, stat: 3, source: 'dbs', udfData: { udffileid: e.table_id, data: await create_data_obj(val), error: {}, }, ts: Date.now(), date: Date.now(), }; const index_ret = await get_index_json(doc, table_obj); if (index_ret < 0) throw index_ret.data; doc.udfIndex = index_ret.data; const ret = await check_unique(e, doc, table_obj, db); if (ret.code < 0) { throw ret; } docs.push(doc); } try { const body = await db.bulk({ docs: docs }); return resolve(body); } catch (error) { return reject(error); } } catch (msg) { return reject(msg); } }; const single = async function () { var doc = { _id: get_uuid(e.table_id), docType: 'database', user: e.uid, app_id: e.app_id, stat: 3, source: 'dbs', udfData: { udffileid: e.table_id, data: await create_data_obj(e.table_data), error: {}, }, ts: Date.now(), date: Date.now(), }; const index_ret = await get_index_json(doc, table_obj); if (index_ret.code < 0) { return index_ret; } doc.udfIndex = index_ret.data; const ret = await check_unique(e, doc, table_obj, db); if (ret.code < 0) { return reject(ret.data); } try { const body = await db.put(doc); return resolve(body); } catch (error) { return reject(error); } }; if (Array.isArray(e.table_data)) { return await batch(); } return await single(); }; export const read = async (params, resolve, reject) => { const e = params.e; const db = params.db; const table_obj = params.table_obj; const ret = await query_db(e, db, table_obj); if (ret.code < 0) { return reject(ret.data); } return resolve(ret.data); }; export const update = async (params, resolve, reject) => { const e = params.e; const db = params.db; const table_obj = params.table_obj; try { if (e.row_id === 'newRecord') { return this.create(params, resolve, reject); } var doc = await db.get(e.row_id, {}); // let data = doc.udfData.data; var error = undefined; if (!e.field_id && !e.table_data) { error = 'Invalid field_id or table_data object to save'; return reject(error); } // single value save if (e.field_id) { let _tableFieldsObj = find_item_by_key(table_obj.tableFields, 'field_id', e.field_id); if (!_tableFieldsObj || _.isEmpty(_tableFieldsObj)) { return reject('Error: ' + e.field_id + ' - field not found'); } doc.udfData.data[e.field_id] = await get_cast_val('dbs_update', e.field_id, _tableFieldsObj.props.fieldType, e.field_value); } // object value save if (e.table_data) { // data = {}; for await (const [key, val] of Object.entries(e.table_data)) { let _tableFieldsObj = find_item_by_key(table_obj.tableFields, 'field_id', key); if (!_tableFieldsObj || _.isEmpty(_tableFieldsObj)) { reject('Error: ' + key + ' - field not found'); break; } doc.udfData.data[key] = await get_cast_val('dbs_update', key, _tableFieldsObj.props.fieldType, val); } } if (error) { return reject(error); } doc.ts = Date.now(); const index_ret = await get_index_json(doc, table_obj); if (index_ret.code < 0) { return index_ret; } doc.udfIndex = index_ret.data; const ret = await check_unique(e, doc, table_obj, db); if (ret.code < 0) { return ret; } try { const body = await db.put(doc); return resolve(body); } catch (error) { return reject(error); } } catch (error) { return reject(error); } }; export const del = async (params, resolve, reject) => { const e = params.e; const db = params.db; for await (let row_id of e.ids) { try { let doc = await db.get(row_id); if (e.permanent) { await db.put({ _id: doc._id, _rev: doc._rev, _deleted: true, }); } else { doc.stat = 4; doc.ts = Date.now(); await db.put(doc); } } catch (err) { return { code: -110, data: err }; } } resolve(e.ids); }; // export const restore = async (params, resolve, reject) => { // const e = params.e; // const db = params.db; // var docs_to_restore = []; // const data = await db.fetch({ keys: e.ids }); // for await (var val of data.rows) { // var doc = val.doc; // if (doc && doc._id) { // doc.stat = 3; // doc.ts = Date.now(); // docs_to_restore.push(doc); // } // } // try { // await db.bulk({ docs: docs_to_restore }); // resolve(docs_to_restore); // } catch (error) { // return reject(error); // } // }; export const create_design = async (params, resolve, reject) => { const db = params.db; const ddoc = { _id: '_design/xuda', views: { db_index_reduce: { map: 'function(doc) {\r\n if (doc.docType=="database" && doc.stat===3) {\r\n if (doc.udfIndex) {\r\n for (var i = 0; i < doc.udfIndex.length; i++)\r\n {\r\n var val = doc.udfIndex[i]; \r\n emit([val.fileId, val.indexId, val.keyValue],1);\r\n }\r\n }\r\n }\r\n \r\n}', reduce: 'function(keys, values) {\n return sum(values);\n}', }, db_index: { map: 'function(doc) {\r\n if (doc.docType=="database" && doc.stat===3) {\r\n if (doc.udfIndex) {\r\n for (var i = 0; i < doc.udfIndex.length; i++)\r\n {\r\n var val = doc.udfIndex[i]; \r\n emit([val.fileId, val.indexId, val.keyValue],{_id: doc._id});\r\n }\r\n }\r\n }\r\n \r\n}', }, db_table_counts: { map: 'function(doc) {\r\n\r\nif (doc.docType=="database" && doc.stat===3) {\r\n if(doc.udfData && doc.udfData.udffileid) \r\n emit(doc.udfData.udffileid, 1); \r\n }\r\n}', reduce: '_count', }, stat_3_doc_counts: { reduce: '_count', map: 'function (doc) {\n if(doc.stat===3){\n emit(doc.docType, 1);\n }\n}', }, }, }; try { const ret = await db.put(ddoc, { timeout: 99999999999 }); resolve(ret); } catch (err) { return resolve(err); } };