diff --git a/src/adapters/PouchDBAdapter.js b/src/adapters/PouchDBAdapter.js index 535b02c..49f73e6 100644 --- a/src/adapters/PouchDBAdapter.js +++ b/src/adapters/PouchDBAdapter.js @@ -1,4 +1,3 @@ -// Requires pouchdb and pouchdb-find plugin 6.2.0 angular.module('angular-hook-orm') .factory('PouchDBWrapper', ['JH', PouchDBWrapper]) .factory('PouchDBAdapter', ['JH', PouchDBAdapter]); @@ -10,85 +9,75 @@ function PouchDBWrapper(JH) { var _c; var _d; var _e; - var _cfg; - var ndxs = []; - (function () { - _cfg = JH.acquire('HookConfig', true); - _c = _cfg.connections; - _e = _cfg.entities; - _d = _cfg.database; - })(); - var _checkIndexes = function _checkIndexes () { - DB.indexes().then(function(r){ - r.indexes.map(function(i){ - ndxs.push(i.name); - }); - if (ndxs.indexOf('JTs') == -1) { - _index( [_e.table], 'JTs'); - } - }); - }; - DB.debugging = function debugging() { - let debug = _cfg.db_debug; - if (debug) { - PouchDB.debug.enable(debug); - } else { - PouchDB.debug.disable(); - } - }; - DB.connect = function connect() { - if (!_d) return; - _db = new PouchDB(_d); - if (_db) { - if (ndxs.length == 0) { - _checkIndexes(); - } - } - }; - DB.getDB = function getDB () { - if (!_db) return false; + DB.getDB = function () { return _db; }; DB.indexes = function () { - if (!_db) return false; return _db.getIndexes(); }; - DB.createIndex = function (i) { - if (!_db) return false; - return _db.createIndex({index:i}); - }; - var _index = function _index(f, n, d = null, t = 'json') { + DB.index = function (f, n, d = null, t = 'json') { if (JH.empty(f)) return false; let i = {fields: f}; if (n) if (!d) {d = n;} i.name = n; i.ddoc = d; if (t) i.type = t; - return DB.createIndex(i); + return _db.createIndex({index:i}); }; DB.status = function () { - if (!_db) return false; return _db.info(); }; + (function () { + let config = JH.acquire('HookConfig'); + _c = config.connections; + _e = config.entities; + _d = config.database; + let debug = config.db_debug; + if (debug) { + PouchDB.debug.enable(debug); + } else { + PouchDB.debug.disable(); + } + _db = new PouchDB(_d); + if (_db) { + _db.compact(); + var existing = []; + DB.indexes().then(function(r){ + console.log(r); + r.indexes.map(function(i){ + if(i.ddoc != null){ + existing.push(i.name); + } + // console.log(i); + // _db.deleteIndex(i); + }); + if (existing.indexOf('Ts') == -1) { + DB.index([_e.table], 'Ts'); + } + if (existing.indexOf('Hs') == -1) { + DB.index([_e.hooks], 'Hs'); + } + // if (existing.indexOf('THs') == -1) { + // DB.index([_e.table, _e.hooks], 'THs'); + // } + }); + } + })(); DB.backup = function () { - if (!_db || !_c) return false; let bDB = _c.backupDB; - if (!bDB) return false; + if (!_c || !bDB) return false; return _db.replicate.to(bDB.path, bDB.options); }; DB.restore = function () { - if (!_db || !_c) return false; let bDB = _c.backupDB; - if (!bDB) return false; + if (!_c || !bDB) return false; return _db.replicate.from(bDB.path, bDB.options); }; DB.sync = function () { - if (!_db || !_c) return false; let sDB = _c.backupDB; - if (!sDB) return false; + if (!_c || !sDB) return false; return _db.sync(sDB.path, sDB.options); }; DB.drop = function () { - if (!_db || !_c) return false; if (_c.backup_on_destroy) { return DB.backup().then(function() { return _db.destroy(); @@ -98,7 +87,6 @@ function PouchDBWrapper(JH) { } }; DB.empty = function () { - if (!_db) return false; let s = {}; s[_e.table] = {$gt: null}; return _db.find({selector: s}).then(function(r){ @@ -110,9 +98,15 @@ function PouchDBWrapper(JH) { }); }; DB.close = function () { - if (!_db) return false; return _db.close(); }; + DB.viewCleanup = function () { + return _db.viewCleanup().then(function (result) { + console.log(result); + }).catch(function (err) { + console.log(err); + }); + }; } return PouchDBWrapper; } @@ -120,33 +114,94 @@ function PouchDBWrapper(JH) { function PouchDBAdapter(JH) { function PouchDBAdapter() { var pa = this; - var DBW; + var DBP; var _db; var _c; var _e; (function() { - DBW = JH.acquire('PouchDBWrapper'); - DBW.connect(); - _db = DBW.getDB(); - _c = JH.acquire('HookConfig', true); + DBP = JH.acquire('PouchDBWrapper'); + _db = DBP.getDB(); + _c = JH.acquire('HookConfig'); _e = _c.entities; console.log('started PouchDB Adapter'); })(); - pa.getWrapper = function getWrapper() { - return DBW; - }; + + // // To Be Continued! + + // pa.selectAll = function (table) { + // // let s = {}; + // // s[_e.table] = {$eq: table}; + // // var tk = _e.table; + // // var id = _e.key; + + // var myMapFunction = function (doc, emit) { + // // console.log(tk); + // // emit(doc._id); + // emit(doc[_e.key]); + // } + // return _db.query(myMapFunction, { + // startkey : table +':', + // // endkey : table +':\ufff0', + // // stale: 'update_after', + // // descending: true, + // include_docs : true + // }).then(function(r) { + // var docs = []; + // r.rows.map(function(i) { + // docs.push(i.doc); + // }) + // console.log(r); + // console.log(docs); + // return JH.promise(docs); + // }) + // }; + pa.selectAll = function (table) { let s = {}; s[_e.table] = {$eq: table}; return _db.find({ - selector:s + selector:s, + // use_index:'_design/Ts' }).then(function(r) { return JH.promise(r.docs); }); }; - pa.findOne = function(i){ - return _db.get(i); + pa.findOne = function(i, o = null){ + return _db.get(i, o); + }; + pa.children = function(id, table = null) { + let s = {}; + s[_e.parents] = {$gt: [], $elemMatch: {$eq : id}}; + if (table != null) { + s[_e.table] = table; + } + return _db.find({selector: s}); + }; + pa.join = function join (name) { + var hkn = function hkn () { + return name; + }; + var mf = function map (doc, emit) { + let join = hkn(); + let hooks = JSON.parse(doc[_e.hooks]); + if (hooks[join] != undefined) { + let rel = doc[_e.relations]; + let r = {}; + r[_e.key] = rel[join]; + emit(r); + } + }; + return _db.query(mf, {include_docs : true}).then(function(r) { + let objects = []; + let key= hkn(); + r.rows.map(function(a){ + objects.push(a.doc); + }); + let results = []; + results[key] = JH.extractIfOne(objects); + return JH.promise(results); + }); }; pa.findThese = function(ids) { let s = {}; @@ -161,11 +216,18 @@ function PouchDBAdapter(JH) { pa.update = function (e) { return _db.put(e); }; - pa.remove = function (e) { - return _db.remove(e); + pa.remove = function (e,o = null) { + return _db.remove(e, o); }; pa.bulk = function (b) { - return _db.bulkDocs(b); + return _db.bulkDocs(b).then(function(){ + return _db.compact(); + }).catch(function (err) { + console.log(err); + }); + }; + pa.truncate = function () { + return DBP.empty(); }; } return PouchDBAdapter; diff --git a/src/angular-hook-orm.js b/src/angular-hook-orm.js index aa87a4a..b3fee55 100644 --- a/src/angular-hook-orm.js +++ b/src/angular-hook-orm.js @@ -1,32 +1,23 @@ angular.module('angular-hook-orm', []) .factory('JH', ['$injector', '$q', JH]) -.factory('EntitiesManager', ['JH', EntitiesManager]) -.factory('DatabaseManager', ['JH', DatabaseManager]); +.factory('EntitiesManager', ['JH', EntitiesManager]); function JH($injector, $q){ function JH() { var h = this; - var _d = {}; h.v = []; - h.acquire = function acquire(factoryName, hold = false) { + h.acquire = function acquire(factoryName) { let obj; let ff; - if (hold && _d[factoryName] != undefined){ - obj = _d[factoryName]; - } else { - try { - ff = $injector.get(factoryName); - } catch (e) { - throw new Error('No "' + factoryName + '" class available. Cause: ' + "\n\n" + e); - } - try { - obj = $injector.instantiate(ff); - if (hold) { - _d[factoryName] = obj; - } - } catch (e) { - throw new Error('Could not instantiate "'+ factoryName + '" class. ' + "\n\n" + e); - } + try { + ff = $injector.get(factoryName); + } catch (e) { + throw new Error('No "' + factoryName + '" class available. Cause: ' + "\n\n" + e); + } + try { + obj = $injector.instantiate(ff); + } catch (e) { + throw new Error('Could not instantiate "'+ factoryName + '" class. ' + "\n\n" + e); } return obj; }; @@ -50,6 +41,14 @@ function JH($injector, $q){ h.vct[d2>>16&0xff] + h.vct[d2>>24&0xff] + h.vct[d3&0xff] + h.vct[d3>>8&0xff] + h.vct[d3>>16&0xff] +h.vct[d3>>24&0xff]; }; + h.findIndex = function findIndex(array, id) { + var low = 0, high = array.length, mid; + while (low < high) { + mid = (low + high) >>> 1; + array[mid] != undefined ? array[mid]._id < id ? low = mid + 1 : high = mid :high = mid ; + } + return low; + }; h.empty = function empty(obj) { let k = Object.keys(obj).length; let s = JSON.stringify(obj); @@ -78,53 +77,17 @@ function JH($injector, $q){ h.extractIfOne = function extractIfOne(arr) { return arr.length === 1 ? arr[0] : arr; }; - } - return new JH; -} - -function DatabaseManager(JH) { - function DatabaseManager() { - var dbm = this; - var DBW; - var _c; - (function () { - console.log('started Database Manager'); - _c = JH.acquire('HookConfig', true); - let DBA = JH.acquire(_c.adapter, true); - DBW = DBA.getWrapper(); - })(); - dbm.connect = function connect() { - return DBW.connect(); - }; - dbm.status = function status() { - return DBW.status(); - }; - dbm.backup = function backup() { - return DBW.backup(); - }; - dbm.restore = function restore() { - return DBW.restore(); - }; - dbm.sync = function sync() { - return DBW.sync(); - }; - dbm.drop = function drop() { - return DBW.drop(); - }; - dbm.empty = function empty() { - return DBW.empty(); - }; - dbm.createIndex = function createIndex(i) { - return DBW.createIndex(i); - }; - dbm.indexes = function indexes() { - return DBW.indexes(); - }; - dbm.close = function close() { - return DBW.close(); + h.needsParsing = function needsParsing(json) { + if (typeof json == 'object'){ + return false; + } + if (typeof json == 'array'){ + return false; + } + return true; }; } - return new DatabaseManager; + return new JH; } function EntitiesManager(JH) { @@ -138,73 +101,65 @@ function EntitiesManager(JH) { var _e; var _q = []; (function () { - console.log('started Entity Manager'); - _c = JH.acquire('HookConfig', true); - DBA = JH.acquire(_c.adapter, true); + _c = JH.acquire('HookConfig'); + DBA = JH.acquire(_c.adapter); _e = _c.entities; + console.log('started Entity Manager'); })(); - /* Queueing System Methods */ - var ready = function ready() { - let defer = JH.defer(); - var int = setInterval(function () { - if (_q.length === 0) { - clearInterval(int); - defer.resolve(true); - } - }, 100); - return defer.promise; - }; - var que = function que(start) { - let defer = JH.defer(); - var int = setInterval(function () { - if (_q[0] === start) { - clearInterval(int); - defer.resolve(true); - } - }, 100); - return defer.promise; - }; - var addQue = function addQue(queID) { - _q.push(queID); - }; - var clearQue = function clearQue(queID) { - _q.splice(_q.indexOf(queID), 1); - }; - /* Persistence Methods */ let _clone = function _clone(e) { _pc[e._id] = JSON.stringify(e); }; - var _stack = function _stack(e) { - let ss = JSON.stringify(e); - let cs = _pc[e[_e.key]]; - if (cs !== undefined && cs === ss) return; - _s[e[_e.key]] = ss; + em.uuid = function uuid() { + return JH.uuid(); }; - var _findPersisted = function _findPersisted(ids) { + em.allInTable = function allInTable(table, f = false) { + let ap = _allPersisted(table); + if (ap.length > 0 && !f) { + return JH.promise(ap); + } else { + return em.fetchAllInTable(table); + } + }; + em.fetchAllInTable = function fetchAllInTable(table) { let queID = JH.uuid(); addQue(queID); return que(queID).then(function () { - let picks = []; - ids.map(function (id) { - let x = ids.indexOf(id); - if (JH.hasProp(_p, id)) { - picks.push(_p[id]); - ids.splice(x, 1); - } + return DBA.selectAll(table).then(function (r) { + let picks = []; + r.map(function (ent) { + if(JH.hasProp(_p, ent[_e.key])) { + picks.push(_p[ent[_e.key]]); + } else { + let isDeleted = JH.hasProp(ent, _e.deleted) && ent[_e.deleted] == true; + if(!isDeleted) { + picks.push(ent); + } + } + }); + clearQue(queID); + return em.convert(picks); }); - clearQue(queID); - return picks; }); + } + em.find = function find(i) { + return em.findMany(i); }; - /* Data Mapping Methods */ - var _convert = function _convert(docs) { + em.clear = function clear() { + _p = {}; + _pc = {}; + _s = {}; + }; + em.convert = function convert(docs) { if (docs === undefined) return null; let bulk = []; - docs.map(function (l) { - if(JH.hasProp(_p, l[_e.key])) { - bulk.push(_p[l[_e.key]]); + docs.map(function (d) { + let isDeleted = JH.hasProp(d, _e.deleted) && d[_e.deleted] == true; + if(JH.hasProp(_p, d[_e.key]) && !isDeleted) { + bulk.push(_p[d[_e.key]]); + } else if (isDeleted) { + _rmMemory(d); } else { - let ent = _entity(l, true); + let ent = em.entity(d, true); if (ent != null) { bulk.push(ent); } @@ -212,45 +167,99 @@ function EntitiesManager(JH) { }); return JH.promise(bulk); }; + em.findMany = function findMany(ids) { + let queID = JH.uuid(); + addQue(queID); + return que(queID).then(function () { + clearQue(queID); + let links = angular.copy(ids); + links = links.constructor === Array ? links : [links]; + let po = _findPersisted(links); + if (links.length > 0) { + return DBA.findThese(links).then(function (r) { + return em.convert(r).then(function (x) { + x.map(function (d) { + if(d[_e.deleted] != true ) { + po.push(d); + } else { + _rmMemory(d); + } + }); + return JH.promise(JH.extractIfOne(po)); + }); + }); + } + return JH.promise(JH.extractIfOne(po)); + }); + }; + var _rmMemory = function _rmMemory(d) { + if (JH.hasProp(_p, d[_e.key])) { + delete _p[d[_e.key]]; + } + if (JH.hasProp(_s, d[_e.key])) { + delete _s[d[_e.key]]; + } + } var _revive = function _revive(e) { if (!JH.hasProp(e, _e.key)) { - e[_e.key] = JH.uuid(); + e[_e.key] = e[_e.table] + ':' + JH.uuid(); } e.grab = _grab; e.hook = _hook; e.unhook = _unhook; e.emptyHook = _emptyHook; e.assign = _assign; + e.unassign = _unassign; + e.holdBallast = _ballast; + e.grabBallast = _grabBallast; + e.updateBallast = _updateBallast; e.countAssigned = _countAssigned; return e; }; - var _repo = function _revive(r) { - r.find = _find; - r.findAll = _findAll; - r.findBy = _findBy; - r.total = _total; - return r; - }; var _prepare = function _prepare(e) { let rel = _e.relations; let hooks = _e.hooks; + let ballast = _e.ballast; if (JH.hasProp(e, rel)) { e[rel] = JSON.stringify(e[rel]); } if (JH.hasProp(e, hooks)) { e[hooks] = JSON.stringify(e[hooks]); } + if (JH.hasProp(e, ballast)) { + e[ballast] = JSON.stringify(e[ballast]); + } + if (JH.hasProp(e, 'ballastLoad')) { + delete e.ballastLoad; + } return e; }; - var _entity = function _entity(obj, persist = false) { + var _spark = function _spark(e) { + let rel = _e.relations; + let hooks = _e.hooks; + let ballast = _e.ballast; + if (JH.hasProp(e, rel) && JH.needsParsing(e[rel])) { + e[rel] = JSON.parse(e[rel]); + } + if (JH.hasProp(e, hooks) && JH.needsParsing(e[hooks])) { + e[hooks] = JSON.parse(e[hooks]); + } + if (JH.hasProp(e, ballast) && JH.needsParsing(e[ballast])) { + e[ballast] = JSON.parse(e[ballast]); + } + return e; + }; + em.entity = function entity(obj, persist = false) { let keys = Object.keys(obj); if (keys.length === 0 || !JH.hasProp(obj, _e.table)) { return null; + } else if (JH.hasProp(obj, _e.deleted) && obj[_e.deleted] == true) { + _rmMemory(obj); } else { let t = obj[_e.table]; let entity = JH.acquire(t.sentenceCase()); keys.map(function (attr) { - if ([_e.relations, _e.hooks].indexOf(attr) !== -1) { + if ([_e.relations, _e.hooks, _e.ballast].indexOf(attr) !== -1) { entity[attr] = JSON.parse(obj[attr]); } else { entity[attr] = obj[attr]; @@ -264,7 +273,41 @@ function EntitiesManager(JH) { return entity; } }; - /* Hooks Logic Methods */ + em.model = function model(entity, persist = false) { + let keys = Object.keys(entity); + if (keys.length === 0 || !JH.hasProp(entity, _e.table)) { + return null; + } else { + entity = _revive(entity); + if (persist) { + _clone(entity); + em.persist(entity); + } + return entity; + } + }; + var _findPersisted = function _findPersisted(ids) { + let picks = []; + ids.map(function (id) { + let x = ids.indexOf(id); + if (JH.hasProp(_p, id)) { + picks.push(_p[id]); + ids.splice(x, 1); + } + }); + return picks; + }; + var _allPersisted = function _allPersisted(table) { + let picks = []; + let keys = Object.keys(_p); + keys.map(function (k) { + let x = keys.indexOf(k); + if (k.indexOf(table + ':') !== -1) { + picks.push(_p[k]); + } + }); + return picks; + }; var _hook_required = ['reverse', 'type','strict', 'mirror']; var _relations = ['o2o', 'o2m', 'm2o', 'm2m']; var _reverse = { @@ -297,6 +340,100 @@ function EntitiesManager(JH) { nh.strict = old.strict; return nh; }; + var _grab = function _grab(name) { + var subject = this; + let rel = _e.relations; + let hooks = _e.hooks; + let gotHook = !_noHook(subject, name); + if (gotHook) { + let map = subject[hooks][name]; + let noRel = _noRel(subject, map.type, name); + let lnk = subject[rel][map.type][name]; + if (!noRel && lnk) { + return em.findMany(lnk).then(function(r) { + r = r.constructor === Array ? r : [r]; + var po = []; + r.map(function(i) { + _prepBallast(subject, name, i); + po.push(i); + }); + return JH.promise(JH.extractIfOne(po)); + }); + } else { + return JH.promise(null); + } + } + return JH.promise(null); + }; + var ready = function ready() { + let defer = JH.defer(); + var int = setInterval(function () { + console.log('check'); + if (_q.length === 0) { + clearInterval(int); + defer.resolve(true); + } + }, 100); + return defer.promise; + }; + var que = function que(start) { + let defer = JH.defer(); + var int = setInterval(function () { + console.log('queuing'); + if (_q[0] === start) { + clearInterval(int); + defer.resolve(true); + } + }, 100); + return defer.promise; + }; + var addQue = function addQue(queID) { + _q.push(queID); + }; + var clearQue = function clearQue(queID) { + _q.splice(_q.indexOf(queID), 1); + }; + var _hook = function _hook(name, hook) { + var subject = this; + let hooks = _e.hooks; + let exists = _noHook(subject, name); + if (exists && _validHook(hook)) { + subject[hooks][name] = hook; + } + return JH.promise(true); + }; + var _unhook = function _unhook(name) { + var subject = this; + let gotHook = !_noHook(subject, name); + if (gotHook) { + let queID = JH.uuid(); + addQue(queID); + return que(queID).then(function () { + return _clearHook(subject, name, 'drop').then(function () { + _rmHook(subject, name); + clearQue(queID); + return JH.promise(true); + }); + }); + } + }; + var _emptyHook = function _emptyHook(name) { + var subject = this; + let gotHook = !_noHook(subject, name); + if (gotHook) { + let queID = JH.uuid(); + addQue(queID); + return que(queID).then(function () { + return _clearHook(subject, name, 'empty').then(function () { + let type = subject[_e.hooks][name].type; + _emptyRel(subject, type, name); + clearQue(queID); + return JH.promise(true); + }); + }); + } + return JH.promise(null); + }; var _single = function _single(type) { return type === 'm2o' || type === 'o2o'; }; @@ -324,6 +461,7 @@ function EntitiesManager(JH) { if (JH.empty(ent[rel][type])) { delete ent[rel][type]; } + _rmBallast(ent, type, name); }; var _noHook = function _noHook(ent, name) { let hooks = _e.hooks; @@ -337,6 +475,27 @@ function EntitiesManager(JH) { let hooks = _e.hooks; delete ent[hooks][name]; }; + var _countAssigned = function _countAssigned(name) { + var subject = this; + let rel = _e.relations; + let hooks = _e.hooks; + if (_noHook(subject, name)) { + return 0; + } else { + let map = subject[hooks][name]; + let noRel = _noRel(subject, map.type, name); + if (noRel) { + return 0; + } else { + let assigned = subject[rel][map.type][name]; + if (assigned.constructor === Array) { + return assigned.length; + } else { + return assigned === null ? 0 : 1; + } + } + } + }; var _makeRel = function _makeRel(ent, type, name, id) { let rel = _e.relations; let noRel = _noRel(ent, type, name); @@ -374,17 +533,21 @@ function EntitiesManager(JH) { _rmRel(ent, linkType, name); _rmHook(ent, name); } else { - _vanish(ent, linkType, name, id); + _vanish(ent, type, name, id); } } + return JH.promise(true); }; var _vanish = function _vanish(ent, type, name, id) { let rel = _e.relations; if (_single(type)) { ent[rel][type][name] = null; } else { - ent[rel][type][name].splice(id, 1); + let index = ent[rel][type][name].indexOf(id); + ent[rel][type][name].splice(index, 1); } + _unloadBallast(ent, type, name, id); + return JH.promise(true); }; var _emptyRel = function _emptyRel(ent, type, name) { let rel = _e.relations; @@ -393,6 +556,7 @@ function EntitiesManager(JH) { } else { ent[rel][type][name] = []; } + _emptyBallast(ent, type, name); }; var _mark = function _mark(ent, type, name, id, clear = false) { if (clear === true) { @@ -403,6 +567,14 @@ function EntitiesManager(JH) { return _makeRel(ent, type, name, id); } }; + /* + * methods = [ + * 'change', --- when assign different (o2o, m2o only, if m2m or o2m no effect) + * 'empty', --- when truncate hook + * 'drop', --- when 'unhook' + * 'delete' --- when entity is deleted + * ]; + */ var _clearHook = function _clearHook(ent, name, method) { let hooks = _e.hooks; let gotHook = !_noHook(ent, name); @@ -419,10 +591,11 @@ function EntitiesManager(JH) { if (_many(type) && method === 'change') { return JH.promise(true); } else { - _clearRel(ent, name, action).then(function () { + return _clearRel(ent, name, action).then(function () { if (['destroy', 'forget', 'detach'].indexOf(action) > -1) { _rmRel(ent, type, name); _rmHook(ent, name); + return JH.promise(true); } }); } @@ -460,17 +633,19 @@ function EntitiesManager(JH) { }); }; var _markHook = function _markHook(subject, name, ent) { + _spark(subject); + _spark(ent); let hooks = _e.hooks; let table = _e.table; let key = _e.key; if (!JH.hasProp(ent, key)) { throw new Error( - 'Association Entity is invalid: Missing primary UUID key "'+ key + '"' + 'Association Entity is invalid: Missing primary UUID key "' + key + '"' ); } if (!JH.hasProp(subject, key)) { throw new Error( - 'Current Entity is invalid: Missing primary UUID key "'+ key + '"' + 'Current Entity is invalid: Missing primary UUID key "' + key + '"' ); } if (JH.hasProp(subject[hooks], name)) { @@ -480,7 +655,7 @@ function EntitiesManager(JH) { if (map.table !== ent[table]) { throw new Error( 'Hook "strict" restriction violated: ' + - 'Entity must be from "'+ map.table +'" table only.' + 'Entity must be from "' + map.table + '" table only.' ); } } @@ -504,260 +679,195 @@ function EntitiesManager(JH) { } return JH.promise(null); }; - /* Entity Methods */ - var _grab = function _grab(name) { - var subject = this; - return ready().then(function () { - let rel = _e.relations; - let hooks = _e.hooks; - let gotHook = !_noHook(subject, name); - if (gotHook) { - let map = subject[hooks][name]; - let noRel = _noRel(subject, map.type, name); - let lnk = subject[rel][map.type][name]; - if (!noRel && lnk) { - return em.findMany(lnk); - } else { - return JH.promise(null); - } - } - return JH.promise(null); - }); - }; - var _hook = function _hook(name, hook) { - var subject = this; - let hooks = _e.hooks; - let exists = _noHook(subject, name); - if (exists && _validHook(hook)) { - this[hooks][name] = hook; - } - }; - var _unhook = function _unhook(name) { - var subject = this; - let gotHook = !_noHook(subject, name); - if (gotHook) { - let queID = JH.uuid(); - addQue(queID); - que(queID).then(function () { - _clearHook(subject, name, 'drop').then(function () { - _rmHook(subject, name); - clearQue(queID); - }); - }); - } - }; - var _emptyHook = function _emptyHook(name) { - var subject = this; - let gotHook = !_noHook(subject, name); - if (gotHook) { - let queID = JH.uuid(); - addQue(queID); - que(queID).then(function () { - _clearHook(subject, name, 'empty').then(function () { - let type = subject[_e.hooks][name].type; - _emptyRel(subject, type, name); - clearQue(queID); - }); - }); - } - }; var _assign = function _assign(name, ent){ var subject = this; let queID = JH.uuid(); addQue(queID); - que(queID).then(function () { - _markHook(subject, name, ent).then(function () { + return que(queID).then(function () { + return _markHook(subject, name, ent).then(function () { clearQue(queID); + return JH.promise(true); }); }); }; - var _countAssigned = function _countAssigned(name) { + var _unassign = function _unassign(name, ent){ var subject = this; - let rel = _e.relations; - let hooks = _e.hooks; - if (_noHook(subject, name)) { - return 0; - } else { - let map = subject[hooks][name]; - let noRel = _noRel(subject, map.type, name); - if (noRel) { - return 0; - } else { - let assigned = subject[rel][map.type][name]; - if (assigned.constructor === Array) { - return assigned.length; - } else { - return assigned === null ? 0 : 1; - } - } - } - }; - /* Repository Methods */ - var _find = function _find(i) { + _spark(subject); + _spark(ent); let queID = JH.uuid(); + var hooks = _e.hooks; + var key = _e.key; + var map = subject[hooks][name]; + var reverseType = _reverse[type]; + var type = map.type; + var reverseName = map.reverse; + var id = ent[key]; + var sid = subject[key]; addQue(queID); return que(queID).then(function () { - let po = _findPersisted(i); - if (po.length == 0) { - return DBA.findOne(i).then(function (r) { - clearQue(queID); - return _convert(r).then(function (x) { - x.map(function (d) { - po.push(d); - }); - return JH.promise(JH.extractIfOne(po)); + return _vanish(subject, type, name, id).then(function(){ + if (map.mirror) { + return _vanish(ent, reverseType, reverseName, sid).then(function(){ + clearQue(queID); + return JH.promise(true); }); - }); - } - return JH.promise(JH.extractIfOne(po)); + } else { + clearQue(queID); + return JH.promise(true); + } + }); }); }; - var _findAll = function _findAll() { - var subject = this; - let table; - if (!JH.hasProp(subject, _e.table)) { - table = subject[_e.table]; - } else { - // Todo: finish logic - let repo = subject.constructor.name; - let entity= repo.replace(_c.repo.suffix, ''); - table = entity.toLowerCase(); + + var _unloadBallast = function _unloadBallast(ent, type, name, id) { + let ball = _e.ballast; + delete ent[ball][type][name][id]; + }; + var _rmBallast = function _rmBallast(ent, type, name) { + let ball = _e.ballast; + delete ent[ball][type][name]; + if (JH.empty(ent[ball][type])) { + delete ent[ball][type]; + } + }; + var _emptyBallast = function _emptyBallast(ent, type, name) { + let ball = _e.ballast; + ent[ball][type][name] = {}; + }; + var _noBallast = function _noBallast(ent, type, name) { + let ball = _e.ballast; + let noball = !JH.hasProp(ent, ball); + if (noball) { + ent[ball] = {}; + } + let noballtype = !JH.hasProp(ent[ball], type); + if (noballtype) { + ent[ball][type] = {}; + } + return !JH.hasProp(ent[ball][type], name); + }; + var _setBallast = function _setBallast(subject, name, ent, ballast) { + let hooks = _e.hooks; + let ball = _e.ballast; + let key = _e.key; + let map = subject[hooks][name]; + let type = map.type; + let id = ent[key]; + let noBallast = _noBallast(subject, type, name); + if (noBallast) { + subject[ball][type][name] = {}; } + subject[ball][type][name][id] = JSON.stringify(ballast); + return JH.promise(true); + }; + var _prepBallast = function _prepBallast(subject, name, ent) { + let hooks = _e.hooks; + let ball = _e.ballast; + let key = _e.key; + let map = subject[hooks][name]; + let type = map.type; + let id = ent[key]; + if (JH.hasProp(subject[ball][type][name], id)){ + ent.ballastLoad = JSON.parse(subject[ball][type][name][id]); + } + }; + var _updateBallast = function _updateBallast(name, ent) { + var subject = this; let queID = JH.uuid(); addQue(queID); return que(queID).then(function () { - return DBA.selectAll(table).then(function (r) { - let picks = []; - r.map(function (ent) { - if(JH.hasProp(_p, ent[_e.key])) { - picks.push(_p[ent[_e.key]]); - } else { - picks.push(_p[ent[_e.key]]); - } - }); + return _setBallast(subject, name, ent, ent.ballastLoad).then(function () { + em.persist(ent); clearQue(queID); - return _convert(r); + return JH.promise(true); }); }); }; - var _findBy = function _findBy(criteria) { + var _grabBallast = function _grabBallast(name, ent) { var subject = this; - let table = subject[_e.table]; - if (JH.empty(criteria)) { - return subject.findAll(); - } else { - // TODO: finish logic. - return table; + _spark(subject); + _spark(ent); + let hooks = _e.hooks; + let ball = _e.ballast; + let key = _e.key; + let map = subject[hooks][name]; + let type = map.type; + let id = ent[key]; + var load = null; + if (JH.hasProp(subject[ball][type][name], id)){ + load = JSON.parse(subject[ball][type][name][id]); } + return JH.promise(load); }; - var _total = function _total() { + var _ballast = function _ballast(name, ent, ballast){ var subject = this; - let table = subject[_e.table]; - // TODO: finish logic. - return 0; - }; - /* Public Methods */ - em.uuid = function uuid() { - return JH.uuid(); - }; - em.allInTable = function allInTable(table) { let queID = JH.uuid(); addQue(queID); return que(queID).then(function () { - return DBA.selectAll(table).then(function (r) { - let picks = []; - r.map(function (ent) { - if(JH.hasProp(_p, ent[_e.key])) { - picks.push(_p[ent[_e.key]]); - } else { - picks.push(_p[ent[_e.key]]); - } - }); + return _setBallast(subject, name, ent, ballast).then(function () { clearQue(queID); - return _convert(r); + return JH.promise(true); }); }); }; - em.find = function find(i) { - let queID = JH.uuid(); - addQue(queID); - return que(queID).then(function () { - let po = _findPersisted(i); - if (po.length == 0) { - return DBA.findOne(i).then(function (r) { - clearQue(queID); - return _convert(r).then(function (x) { - x.map(function (d) { - po.push(d); - }); - return JH.promise(JH.extractIfOne(po)); - }); - }); - } - return JH.promise(JH.extractIfOne(po)); - }); + em.getRepository = function getRepository(t) { + if (!t) return null; + return JH.acquire(t.sentenceCase() + _c.repo.suffix); }; - em.findMany = function findMany(ids) { - let links = angular.copy(ids); - links = links.constructor === Array ? links : [links]; - let po = _findPersisted(links); - if (links.length > 0) { - return DBA.findThese(links).then(function (r) { - return _convert(r).then(function (x) { - x.map(function (d) { - po.push(d); + em.remove = function remove(e) { + e[_e.deleted] = true; + delete _p[e[_e.key]]; + delete _s[e[_e.key]]; + delete _pc[e[_e.key]]; + DBA.findOne(e[_e.key], {latest:true, conflict:true}).then(function (doc) { + // Get the latest doc from db to prevent conflicts. + e = doc; // Not sure about it. TBD. + let queID = JH.uuid(); + addQue(queID); + que(queID).then(function () { + let hooks = e[_e.hooks]; + if (hooks != null) { + Object.keys(hooks).map(function (name) { + let queHookID = JH.uuid(); + addQue(queHookID); + que(queHookID).then(function () { + _clearHook(e, name, 'delete').then(function () { + let type = hooks[name].type; + _rmRel(e, type, name); + _rmHook(e, name); + clearQue(queHookID); + }); + }); }); - return JH.promise(JH.extractIfOne(po)); + } + clearQue(queID); + DBA.remove(doc).then(function (result) { + + }).catch(function (err) { + console.log(err); }); }); - } - return JH.promise(JH.extractIfOne(po)); + }).catch(function (err) { + console.log(err); + }); }; - em.model = function model(entity, persist = false) { - let keys = Object.keys(entity); - if (keys.length === 0 || !JH.hasProp(entity, _e.table)) { - return entity; - } else { - entity = _revive(entity); - if (persist) { - _clone(entity); - em.persist(entity); - } - return entity; + em.queryBuilder = function queryBuilder() { + if (_c.queryBuilder != undefined) { + return JH.acquire(_c.queryBuilder); } + return null; }; - em.getRepository = function getRepository(t) { - if (!t) return null; - let r = JH.acquire(t.sentenceCase() + _c.repo.suffix); - r = _repo(r); - return r; + em.stack = function stack(e) { + let ss = JSON.stringify(e); + let cs = _pc[e[_e.key]]; + if (cs !== undefined && cs === ss) return; + _s[e[_e.key]] = ss; }; em.persist = function persist(e) { if (!JH.hasProp(_p, e[_e.key])) { _p[e[_e.key]] = e; } }; - em.remove = function remove(e) { - e[_e.deleted] = true; - let queID = JH.uuid(); - addQue(queID); - que(queID).then(function () { - let hooks = e[_e.hooks]; - Object.keys(hooks).map(function (name) { - let queHookID = JH.uuid(); - addQue(queHookID); - que(queHookID).then(function () { - _clearHook(e, name, 'delete').then(function () { - let type = hooks[name].type; - _rmRel(e, type, name); - _rmHook(e, name); - clearQue(queHookID); - }); - }); - }); - clearQue(queID); - }); - }; em.flush = function flush() { return ready().then(function () { let b = []; @@ -765,7 +875,7 @@ function EntitiesManager(JH) { for (var p in _p) { if (!_p.hasOwnProperty(p)) continue; _p[p] = _prepare(_p[p]); - _stack(_p[p]); + em.stack(_p[p]); } } if (!JH.empty(_s)) { @@ -775,21 +885,19 @@ function EntitiesManager(JH) { } } if (!JH.empty(b)) { - return DBA.bulk(b); + return JH.promise( + DBA.bulk(b).then(function (result) { + }).catch(function (err) { + console.log(err); + }) + ); } return JH.promise(null); }); }; - em.clear = function clear() { - let queID = JH.uuid(); - addQue(queID); - que(queID).then(function () { - _p = {}; - _pc = {}; - _s = {}; - clearQue(queID); - }); - }; + em.hooker = function (name){ + return DBA.join(name); + } } return new EntitiesManager; } \ No newline at end of file diff --git a/src/queryBuilders/fancyQueryBuilder.js b/src/queryBuilders/fancyQueryBuilder.js new file mode 100644 index 0000000..04ab9cf --- /dev/null +++ b/src/queryBuilders/fancyQueryBuilder.js @@ -0,0 +1,172 @@ +angular.module('angular-hook-orm') +.factory('fancyQueryBuilder', ['JH', FancyQueryBuilder]); + +function FancyQueryBuilder(JH){ + function QB() { + var qb = this; + var _c; + var _q = {}; + (function () { + _c = JH.acquire('HookConfig'); + console.log('started fancy Query Builder'); + })(); + qb.select = function select() { + let args = Object.values(arguments); + _q.action = 'select'; + _q.select = args.join(', '); + return qb; + }; + qb.update = function select() { + let args = Object.values(arguments); + _q.action = 'select'; + _q.select = args.join(', '); + return qb; + }; + qb.select = function select() { + let args = Object.values(arguments); + _q.action = 'select'; + _q.select = args.join(', '); + return qb; + }; + qb.from = function from(table, alias = null) { + _q.from = table; + if (alias) { + _q.from += ' ' + alias; + } + return qb; + }; + qb.sqlSafe = function from(str) { + if (typeof str != 'string') { + return str; + } +// var regex = new RegExp(/[\0\x08\x09\x1a\n\r"'\\\%]/g); +// var swapper = function swapper(char){ +// var m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', "'", '"', "\\", '\\\\', "%"]; +// var r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', "''", '""', '\\\\', '\\\\\\\\', '\\%']; +// return r[m.indexOf(char)]; +// }; +// return str.replace(regex, swapper); + // https://stackoverflow.com/a/32648526 + return str.replace(/[\0\x08\x09\x1a\n\r"'\\\%]/g, function (char) { + switch (char) { + case "\0": + return "\\0"; + case "\x08": + return "\\b"; + case "\x09": + return "\\t"; + case "\x1a": + return "\\z"; + case "\n": + return "\\n"; + case "\r": + return "\\r"; + case "\"": + case "'": + case "\\": + case "%": + return "\\"+char; + } + }); + }; + qb.condition = function condition(type, field, operator, value) { + if (!JH.hasProp(_q, 'conditions')) { + _q.conditions = {}; + } + _q.conditions[type] = [field, operator, value]; + return qb; + }; + qb.where = function where(field, operator, value) { + qb.condition('where' , field, operator, value); + return qb; + }; + qb.andWhere = function andWhere(field, operator, value) { + qb.condition('andWhere' , field, operator, value); + return qb; + }; + qb.orWhere = function orWhere(field, operator, value) { + qb.condition('orWhere' , field, operator, value); + return qb; + }; + qb.join = function join(table, alias = null) { + if (!JH.hasProp(_q, 'joins')) { + _q.joins = []; + } + if (alias) { + _q.joins[alias] = table; + } else { + _q.joins.push(table); + } + return qb; + }; + qb.orderBy = function orderBy(field, method) { + if (!JH.hasProp(_q, 'orderBy')) { + _q.orderBy = []; + } + _q.orderBy.push([field, method]); + return qb; + }; + // TBD + qb.groupBy = function groupBy(field) { + if (!JH.hasProp(_q, 'groupBy')) { + _q.groupBy = []; + } + _q.groupBy.push(field); + return qb; + }; + qb.sqlStart = function sqlStart() { + let sql = ''; + let actions = { + 'select':'SELECT', + 'update': 'UPDATE', + 'delete': 'DELETE' + }; + if (_q.action === 'select') { + sql += 'SELECT '+ _q.select.replace(/,\s*$/, "") + ' FROM '+ _q.from; + + } + + }; + qb.dumpSql = function dumpSql() { + let sql = ''; + let words = { + 'where':'WHERE', + 'andWhere': 'AND WHERE', + 'orWhere': 'OR WHERE' + }; + sql += 'SELECT '+ _q.select.replace(/,\s*$/, "") + ' FROM '+ _q.from; + if (JH.hasProp(_q, 'joins')) { + Object.keys(_q.joins).forEach(function(k) { + let v = _q.joins[k]; + if (typeof k === 'string' && k != 0) { + sql += ' JOIN ' + v + ' ' + k; + } else { + sql += ' JOIN ' + v; + } + }); + } + for (var clause in _q.conditions) { + if (!_q.conditions.hasOwnProperty(clause)) continue; + var data = _q.conditions[clause]; + let last = data.length - 1; + data[last] = "'" + qb.sqlSafe(data[last]) + "'"; + sql += ' ' + words[clause] + ' ' + data.join(' '); + } + if (_q.action == 'select' && JH.hasProp(_q, 'orderBy')) { + sql += ' ORDER BY '; + let orders = []; + _q.orderBy.forEach(function(v) { + orders.push(v.join(' ')); + }); + sql += orders.join(', '); + } + if (_q.action == 'select' && JH.hasProp(_q, 'groupBy')) { + sql += ' GROUP BY '; + sql += _q.groupBy.join(', '); + } + + return sql; + }; + } + return QB; +} \ No newline at end of file