UNPKG

jbd

Version:
925 lines (751 loc) 21.4 kB
/** * ========================================== * Name: Mongo * Author: Buddy-Deus * CreTime: 2014-11-20 * Description: db操作基类 * Log * 2015-06-09 初始化 * 2018-07-26 增加Redis支持 * ========================================== */ jBD.define(function (module, exports, require) { "use strict"; const STATE = { Closed: 0, Closeing: -1, Opened: 1, Opening: 2 }, ERROR = (dtd, code, msg) => { let result = {errorCode: code, errorMsg: msg}; if (dtd) dtd.reject(result); return result; }, CONVER = jBD.Conver, STRING = jBD.String; let API, INFO, DB; INFO = jBD.TSeal.extend({ className: "ConnectInfo", create: function (port) { this.PORT = port || 8080; }, free: function () { }, protocol: "db", host: "", port: 0, uid: "", pwd: "", name: "", opt: null, cite: 0, build: function (name) { return STRING.Format("{protocol}://{act}{host}:{port}" + (name && this.name ? "/{name}" : ""), { protocol: this.protocol, act: this.uid ? this.uid + (this.pwd ? ":" + this.pwd : "") + "@" : "", host: this.host, port: this.port, name: this.name }); }, parse: function (value) { switch (jBD.type(value, true)) { case "string": value = value.split("://"); if (value.length < 2) value.unshift(""); if (value.length < 3) value.push(""); value[2] = value[1].split("/"); value[1] = value[2][0]; value[2] = value[2].length > 1 ? value[2][1] : ""; value[1] = value[1].split("@"); if (value[1].length < 2) value[1].unshift(""); value[1][0] = value[1][0].split(":"); if (value[1][0].length < 2) value[1][0].push(""); value[1][1] = value[1][1].split(":"); if (value[1][1].length < 2) value[1][1].push(this.PORT); value = { protocol: value[0], uid: value[1][0][0], pwd: value[1][0][1], host: value[1][1][0], port: value[1][1][1], name: value[2] }; break; case "object": break; default: value = {}; break; } this.host = (value.host !== "localhost") && jBD.isString(value.host) ? value.host : "127.0.0.1"; this.port = CONVER.toInteger(value.port, this.PORT); this.uid = jBD.isString(value.uid) ? value.uid : ""; this.pwd = jBD.isString(value.pwd) ? value.pwd : ""; if (this.cite < 1) { if (value.name) this.name = value.name; } return this.build(); } }); DB = jBD.TObject.extend({ className: "DB", PORT: 0, state: STATE.Closed, mode: "db", connectString: "", connectInfo: null, create: function (cfg) { this.oper = null; this.connectInfo = new INFO(this.PORT); this.connectString = this.connectInfo.parse(cfg); }, free: function () { let INFO = this.connectInfo; this.Close(true); }, dbConnect: function (dtd, info, name = info, opt = name, done = opt) {}, dbDisconnect: function () {}, dbOpen: async function () { const THIS = this, INFO = THIS.connectInfo, dtd = jBD.Deferred(true), DONE = (dtd, info, db) => { info.cite++; dtd.resolve(db); }; if (THIS.state === STATE.Opened) DONE(dtd, INFO, THIS.oper); else { if (THIS.state === STATE.Closed) { // THIS.oper = null; THIS.connectString = THIS.connectInfo.build(); if (!INFO.name) ERROR(dtd, 200, "连接字符串出错!"); else { try { THIS.oper = await THIS.dbConnect(); THIS.state = STATE.Opened; DONE(dtd, INFO, THIS.oper); } catch (e) { dtd.reject(e); THIS.oper = null; THIS.state = STATE.Closed; } } } } return dtd.promise(); }, dbClose: function (force) { return this; }, Open: async function (info, name, opt) { this.Close(true); if (arguments.length == 1) { name = info; info = null; } const dtd = jBD.Deferred(true), INFO = this.connectInfo; if (info) INFO.parse(info); if (name) INFO.name = name; if (opt) INFO.opt = opt; try { let db = await this.dbOpen(); dtd.resolve(db); } catch (e) { dtd.reject(e); } return dtd.promise(); }, Close: function (force) { const INFO = this.connectInfo, CLOSE = () => { this.state = STATE.Closed; this.oper = null; INFO.cite = 0; // INFO.name = null; }; if (this.state != STATE.Opened || this.oper) CLOSE(); else { if (force) this.dbClose(true, CLOSE); else { if (INFO.cite && --INFO.cite < 1) { this.state = STATE.Closeing; this.dbClose(false, CLOSE); } } } return this; } }); API = function (mode, cfg = mode) { switch (mode) { case "mongo": default: return new API.Mongo(cfg); case "redis": return new API.Redis(cfg); } }; API.STATE = STATE; API.ERROR = ERROR; API.INFO = INFO; API.Config = db => { if (!jBD.isObject(db)) db = {}; db.host = jBD.isString(db.host) ? db.host : "127.0.0.1"; db.port = CONVER.toInteger(db.port, 0); db.max = CONVER.toInteger(db.max, 10); db.uid = jBD.isString(db.uid) ? db.uid : ""; db.pwd = jBD.isString(db.pwd) ? db.pwd : ""; db.name = jBD.isString(db.name) ? db.name : "BD_Server"; if (!jBD.has(db.mode, ["mongo", "redis"])) db.mode = "mongo"; return db; }; API.Mongo = (function (DB, STATE, ERROR) { let Mongo, MONGO; const drop = async (coll) => { let dtd = jBD.Deferred(true); try { await coll.drop(); dtd.resolve(); } catch (e) { ERROR(dtd, 210, e.message || ""); } return dtd.promise(); }, find = async (coll, select, opt) => { let dtd = jBD.Deferred(true); try { let rs = await coll.find(select, opt).toArray(); dtd.resolve(rs); } catch (e) { ERROR(dtd, 211, e.message || ""); } return dtd.promise(); }, findOne = async (coll, select, opt) => { let dtd = jBD.Deferred(true); try { let rs = await coll.findOne(select, opt); dtd.resolve(rs); } catch (e) { ERROR(dtd, 212, e.message || ""); } return dtd.promise(); }, count = async (coll, select) => { let dtd = jBD.Deferred(true); try { let rs = await coll.count(select); dtd.resolve(rs); } catch (e) { ERROR(dtd, 213, e.message || ""); } return dtd.promise(); }, remove = async (coll, select, opt) => { let dtd = jBD.Deferred(true); try { let rs = await coll.remove(select, opt); dtd.resolve(rs); } catch (e) { ERROR(dtd, 214, e.message || ""); } return dtd.promise(); }, insert = async (coll, value, opt) => { let dtd = jBD.Deferred(true); try { let func, rs; if (!coll.insertMany) func = coll.insert; else if (jBD.isArray(value)) func = coll.insertMany; else func = coll.insertOne; rs = await func.call(coll, value, opt); dtd.resolve(rs.ops); } catch (e) { ERROR(dtd, 215, e.message || ""); } return dtd.promise(); }, findAndModify = async (coll, select, sort, value, opt) => { let dtd = jBD.Deferred(true); try { let rs = await coll.findAndModify(select, sort, value, opt); dtd.resolve(rs); } catch (e) { ERROR(dtd, 216, e.message || ""); } return dtd.promise(); }, update = async (coll, select, value, opt) => { let dtd = jBD.Deferred(true); try { let func, rs; if (!coll.updateMany) func = coll.update; else { if (opt.multi) func = coll.updateMany; else func = coll.updateOne; delete opt.multi; } rs = await func.call(coll, select, value, opt); dtd.resolve(rs); } catch (e) { ERROR(dtd, 217, e.message || ""); } return dtd.promise(); }, aggregate = async (coll, opt) => { let dtd = jBD.Deferred(true); try { let rs = coll.aggregate(opt).toArray(); dtd.resolve(rs); } catch (e) { ERROR(dtd, 218, e.message || ""); } return dtd.promise(); }, mapReduce = async (coll, map, red, opt) => { let dtd = jBD.Deferred(true); try { let rs = coll.mapReduce(map, red, opt); dtd.resolve(rs); } catch (e) { ERROR(dtd, 219, e.message || ""); } return dtd.promise(); }; Mongo = DB.extend({ className: "Mongo", PORT: 27017, create: function (cfg) { this.super("create", cfg); if (!MONGO) MONGO = require("mongodb"); this.connectInfo.protocol = "mongodb"; this.connectString = this.connectInfo.build(); }, dbConnect: async function () { const THIS = this, INFO = THIS.connectInfo, dtd = jBD.Deferred(true); THIS.state = STATE.Opening; try { if (THIS.oper) THIS.oper = await THIS.oper.open(); else THIS.oper = await MONGO.MongoClient.connect(THIS.connectString, INFO.opt || {useNewUrlParser: true}); THIS.oper.once("close", () => { if (THIS.state === STATE.Closed) { if (INFO.cite > 0) { THIS.dbConnect(); return; } } this.state = STATE.Closed; }); dtd.resolve(THIS.oper.db(INFO.name)); } catch (e) { ERROR(dtd, 200, e.message || ""); } return dtd.promise(); }, dbClose: function (force, done) { if (this.oper) this.oper.close(force === true); if (jBD.isFunction(done)) done(); }, dbOper: async function (db, name) { const dtd = jBD.Deferred(true); try { let coll = await db.collection(name); dtd.resolve(coll); } catch (e) { ERROR(dtd, 201, e.message || ""); } return dtd.promise(); }, // ============================================================== ObjectID: value => MONGO.ObjectID(value), Code: value => MONGO.Code(value), Time: (value, fmt) => { let dt; value = (value + "").slice(0, 8); if (value.length < 8) dt = new Date(); else dt = new Date(parseInt(value, 16) * 1000); return CONVER.toString(dt, fmt); }, // ============================================================== Find: async function (name, select, opt) { const THIS = this, dtd = jBD.Deferred(true); try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs = await find(coll, select || {}, opt || {}); dtd.resolve(rs); } catch (e) { dtd.reject(e); } return dtd.promise(); }, FindOne: async function (name, select, opt) { const THIS = this, dtd = jBD.Deferred(true); try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs = await findOne(coll, select || {}, opt || {}); dtd.resolve(rs); } catch (e) { dtd.reject(e); } return dtd.promise(); }, Count: async function (name, select) { const THIS = this, dtd = jBD.Deferred(true); try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs = await count(coll, select || {}); dtd.resolve(rs); } catch (e) { dtd.reject(e); } return dtd.promise(); }, Insert: async function (name, value, opt) { const THIS = this, dtd = jBD.Deferred(true), fmtOpt = opt => { opt.multi = opt.multi === true; opt.safe = opt.safe === true; let autoinc = opt.autoinc; if (jBD.isString(autoinc)) autoinc = {field: autoinc, initial: 1}; else if (!jBD.isObject(autoinc)) autoinc = null; else { autoinc = { field: jBD.isString(autoinc.field) ? autoinc.field : "id", initial: jBD.isNumber(autoinc.initial, {int: true}) ? autoinc.initial : 0 }; } return autoinc; }; if (jBD.isArray(value) && value.length < 1) ERROR(dtd, 213, "数据内容错误"); else { try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), autoinc = fmtOpt(opt = opt || {}), rs; if (autoinc) value[autoinc.field] = await THIS.AutoInc(autoinc.field, autoinc.initial, true); rs = await insert(coll, value, opt); dtd.resolve(rs); } catch (e) { dtd.reject(e); } } return dtd.promise(); }, Update: async function (name, select, value, opt) { const THIS = this, dtd = jBD.Deferred(true), fmtOpt = opt => { let r = opt.find; if (opt.upsert) opt.upsert = true; else delete opt.upsert; if (opt.safe) opt.safe = true; else delete opt.safe; if (opt.find) { if (!opt.sort) delete opt.sort; if (opt.new === false) delete opt.new; else opt.new = true; } else { if (opt.multi === false) delete opt.multi; else opt.multi = true; } delete opt.find; return r; }; try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs; if (fmtOpt(opt = opt || {})) { rs = await findAndModify(coll, select || {}, opt.sort || {_id: 1}, value, opt); } else { rs = await update(coll, select || {}, value, opt); rs = rs.result; rs.value = rs.upserted || null; } dtd.resolve(rs); } catch (e) { dtd.reject(e); } return dtd.promise(); }, Remove: async function (name, select) { const THIS = this, dtd = jBD.Deferred(true); try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs = remove(coll, select || {}, {safe: true}); dtd.resolve(rs); } catch (e) { dtd.reject(e); } return dtd.promise(); }, Init: async function (name, empty = true) { const THIS = this, dtd = jBD.Deferred(true); name = jBD.isArray(name) ? name : [name]; empty = empty === true; try { let count = name.length, db = await THIS.dbOpen(), coll; for (let i = 0; i < count; i++) { coll = await THIS.dbOper(db, name[i]); await insert(coll, {__init_temp: 1}, {safe: true}); await remove(coll, empty ? {} : {__init_temp: 1}, {safe: true}); } dtd.resolve(db); } catch (e) { dtd.reject(e); } return dtd.promise(); }, Drop: async function (name) { const THIS = this, dtd = jBD.Deferred(true); name = jBD.isArray(name) ? name : [name]; try { let count = name.length, db = await THIS.dbOpen(), coll; for (let i = 0; i < count; i++) { coll = await THIS.dbOper(db, name[i]); await drop(); } dtd.resolve(db); } catch (e) { dtd.reject(e); } return dtd.promise(); }, Aggregate: async function (name, ...opt) { const THIS = this, dtd = jBD.Deferred(true); if (opt.length < 1) ERROR(dtd, 200, "参数不完整"); else { try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs = await aggregate(coll, opt); dtd.resolve(rs); } catch (e) { dtd.reject(e); } } return dtd.promise(); }, MapReduce: async function (name, mrf, opt) { const THIS = this, dtd = jBD.Deferred(true), chk = obj => jBD.isFunction(obj) || (jBD.isString(obj) && /^function/i.test(obj)) ? obj : null; mrf = jBD.Conver.toArray(mrf, null, null); mrf[0] = chk(mrf[0]); mrf[1] = chk(mrf[1]); mrf[2] = chk(mrf[2]); opt = opt || {}; if (!mrf[0] || !mrf[1]) ERROR(dtd, 200, "参数不完整"); else { try { if (mrf[2]) opt.finalize = mrf[2]; if (!opt.out) opt.out = {inline: 1}; let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs; coll = await mapReduce(coll, mrf[0], mrf[1], opt); if (opt.out.inline === 1) dtd.resolve(coll); else { rs = await find(coll, {}, {}); dtd.resolve(rs); } } catch (e) { dtd.reject(e); } } return dtd.promise() }, AutoInc: async function (field, initial, inc) { const THIS = this, dtd = jBD.Deferred(true); field = jBD.isString(field) ? field : "id"; initial = jBD.isNumber(initial, {int: true}) ? initial : 0; if (initial < 1) initial = 1; try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, "AutoIncrement"), rs, select = {}, value = {}; select[field] = {"$exists": true}; if (inc === true) { value["$inc"] = {}; value["$inc"][field] = 1; rs = await findAndModify(coll, select, {}, value, {safe: true, new: true}); rs = rs.value; } else { rs = await findOne(coll, select, {safe: true}); } if (rs) dtd.resolve(rs); else { select[field] = initial; rs = await insert(coll, select, {safe: true}); dtd.resolve(rs[0][field]); } } catch (e) { dtd.reject(e); } return dtd.promise(); } }); return Mongo; })(DB, STATE, ERROR); API.Redis = (function (DB, STATE, ERROR) { let Redis, REDIS; const get = async (coll, key) => { let dtd = jBD.Deferred(true); try { let rs = await coll.get(key); dtd.resolve(rs); } catch (e) { ERROR(dtd, 211, e.message || ""); } return dtd.promise(); }, set = async (coll, key, value) => { let dtd = jBD.Deferred(true); try { let rs = await coll.set(key, value); dtd.resolve(rs); } catch (e) { ERROR(dtd, 211, e.message || ""); } return dtd.promise(); }; Redis = DB.extend({ className: "Redis", PORT: 6379, create: function (cfg) { this.super("create", cfg); if (!REDIS) REDIS = require("ioredis"); this.connectInfo.protocol = "redis"; this.connectString = this.connectInfo.build(); }, dbConnect: async function () { const THIS = this, INFO = THIS.connectInfo, dtd = jBD.Deferred(true); THIS.state = STATE.Opening; try { if (!THIS.oper) THIS.oper = REDIS.createClient(THIS.connectString); THIS.oper.on("error", err => { INFO.cite = 0; this.state = STATE.Closed; }); if (INFO.pwd) THIS.oper.auth(INFO.pwd); dtd.resolve(THIS.oper); } catch (e) { ERROR(dtd, 200, e.message || ""); } return dtd.promise(); }, dbClose: function (force, done) { const THIS = this; if (THIS.oper) { switch (THIS.state) { case STATE.Opened: case STATE.Opening: THIS.oper.end(true); default: THIS.state = STATE.Closed; break; } } if (jBD.isFunction(done)) done(); }, dbOper: async function (db, name) { const dtd = jBD.Deferred(true); try { // let coll = await db.select(name); dtd.resolve(db); } catch (e) { ERROR(dtd, 201, e.message || ""); } return dtd.promise(); }, // ============================================================== Get: async function (name, key) { const THIS = this, dtd = jBD.Deferred(true); try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs = await get(coll, key); dtd.resolve(rs); } catch (e) { dtd.reject(e); } return dtd.promise(); }, Set: async function (name, key) { const THIS = this, dtd = jBD.Deferred(true); try { let db = await THIS.dbOpen(), coll = await THIS.dbOper(db, name), rs = await set(coll, key); dtd.resolve(rs); } catch (e) { dtd.reject(e); } return dtd.promise(); } }); return Redis; })(DB, STATE, ERROR); return API; }, {module: module, exports: this}, ["String"], "DB");