PageRenderTime 59ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/node_modules/tingodb/lib/tcoll.js

https://gitlab.com/sapobogdan/hackatoning
JavaScript | 1332 lines | 1174 code | 90 blank | 68 comment | 293 complexity | 41665fff1db7bdda6b6481288436844d MD5 | raw file
Possible License(s): Apache-2.0, CC-BY-SA-3.0, MPL-2.0-no-copyleft-exception, MIT, 0BSD, BSD-3-Clause, JSON, BSD-2-Clause, LGPL-2.1
  1. var safe = require('safe');
  2. var _ = require('lodash');
  3. var crypto = require('crypto');
  4. var fs = require('fs');
  5. var path = require('path');
  6. var tcursor = require('./tcursor');
  7. var wqueue = require('./wqueue');
  8. var tindex = require('./tindex');
  9. var tcache = require("./tcache");
  10. var Code = require('./tcode').Code;
  11. var tutils = require('./utils');
  12. var Updater = require('./updater');
  13. function tcoll(tdb) {
  14. var self = this;
  15. this._tdb = null;
  16. this._name = null;
  17. this._store = {};
  18. this._fd = null;
  19. this._fsize = null;
  20. this._id = 1;
  21. this._wq = new wqueue();
  22. this._tq = null;
  23. this._idx = {};
  24. this._cache = null;
  25. this._mc = {};
  26. this._check1 = Math.random()*100+1;
  27. // native mongo db compatibility attrs
  28. this.collectionName = null;
  29. if (tdb._stype=="mem") {
  30. this.init = this.initM;
  31. this._put = this._putM;
  32. this._get = this._getM;
  33. } else {
  34. this.init = this.initFS;
  35. this._put = this._putFS;
  36. this._get = this._getFS;
  37. }
  38. }
  39. module.exports = tcoll;
  40. tcoll.prototype.initM = function (tdb, name, options, create, cb) {
  41. var self= this;
  42. this._tdb = tdb;
  43. tdb._mstore = tdb._mstore || {};
  44. this.collectionName = this._name = name;
  45. if (options.strict) {
  46. var exists = tdb._mstore[name];
  47. if (exists && create) return cb(new Error("Collection " + self._name + " already exists. Currently in safe mode."));
  48. else if (!exists && !create) return cb(new Error("Collection " + self._name + " does not exist. Currently in safe mode."));
  49. }
  50. tdb._mstore[name] = this._mstore = tdb._mstore[name] || [];
  51. for (var k=0; k< this._mstore.length; k++) {
  52. var o = this._mstore[k];
  53. if (o) {
  54. self._store[simplifyKey(o._id)]={pos:k+1};
  55. }
  56. }
  57. this._tq = new wqueue(100, function (cb) {
  58. // update indexes
  59. safe.forEachSeries(_.values(self._store), function (rec, cb) {
  60. self._get(rec.pos, false, safe.sure(cb, function (obj) {
  61. var id = simplifyKey(obj._id);
  62. _.forEach(self._idx,function(v, k) {
  63. v.set(obj, id);
  64. });
  65. cb();
  66. }));
  67. }, cb);
  68. });
  69. self.ensureIndex({_id: 1}, {name: '_id_', unique: true}, cb);
  70. };
  71. tcoll.prototype.initFS = function (tdb, name, options, create, cb) {
  72. var self= this;
  73. this._tdb = tdb;
  74. this._cache = new tcache(tdb, tdb._gopts.cacheSize);
  75. this._cmaxobj = tdb._gopts.cacheMaxObjSize || 1024;
  76. this.collectionName = this._name = name;
  77. this._filename = path.join(this._tdb._path, this._name);
  78. if (options.strict) {
  79. var exists = fs.existsSync(self._filename);
  80. if (exists && create) return cb(new Error("Collection " + self._name + " already exists. Currently in safe mode."));
  81. else if (!exists && !create) return cb(new Error("Collection " + self._name + " does not exist. Currently in safe mode."));
  82. }
  83. var pos = 0;
  84. var deleted = 0;
  85. var found = 0;
  86. this._tq = new wqueue(100, function (cb) {
  87. (function (cb) {
  88. fs.open(self._filename, "a+", safe.sure(cb, function (fd) {
  89. self._fd = fd;
  90. var b1 = new Buffer(45);
  91. safe.whilst(function () { return self._fsize===null; }, function(cb) {
  92. (function (cb) {
  93. fs.read(fd, b1, 0, 45, pos, safe.trap_sure(cb, function (bytes, data) {
  94. if (bytes===0) {
  95. self._fsize = pos;
  96. return cb();
  97. }
  98. var h1 = JSON.parse(data.toString());
  99. h1.o = parseInt(h1.o,10);
  100. h1.k = parseInt(h1.k,10);
  101. var b2 = new Buffer(h1.k);
  102. fs.read(fd,b2,0,h1.k,pos+45+1, safe.sure(cb, function (bytes, data) {
  103. var k = JSON.parse(data.toString());
  104. self._id = k._uid;
  105. if (k._a=='del') {
  106. delete self._store[k._id];
  107. deleted++;
  108. } else {
  109. if (self._store[k._id]) deleted++;
  110. self._store[k._id] = { pos: pos, sum: k._s };
  111. }
  112. pos+=45+3+h1.o+h1.k;
  113. found++;
  114. cb();
  115. }));
  116. }));
  117. })(function (err) {
  118. if (err)
  119. cb(new Error(self._name+": Error during load - "+err.toString()));
  120. else
  121. cb();
  122. });
  123. }, cb);
  124. }));
  125. })(function (err) {
  126. if (!found && err)
  127. return cb(err); // nothing read and error, just rise it
  128. safe.run(function (cb) {
  129. var size = _.size(self._store);
  130. // autocompact on certain ratio or err
  131. if (deleted > size || err) {
  132. self._compact(function (errCompact) {
  133. if (errCompact && err)
  134. cb(errCompact);
  135. else {
  136. if (errCompact) console.log(err);
  137. cb();
  138. }
  139. });
  140. } else cb();
  141. }, function () {
  142. self._refreshIndexes(cb);
  143. });
  144. });
  145. });
  146. self.ensureIndex({_id: 1}, {name: '_id_', unique: true}, cb);
  147. };
  148. tcoll.prototype.compactCollection = function (cb) {
  149. var self = this;
  150. self._tq.add(function (cb) {
  151. self._compact(safe.sure(cb, function () {
  152. self._cache.clear();
  153. self._refreshIndexes(cb);
  154. }));
  155. }, true, cb);
  156. };
  157. tcoll.prototype._refreshIndexes = function (cb) {
  158. var self = this;
  159. _.forEach(self._idx,function(v, k) {
  160. v.clear();
  161. });
  162. safe.forEachSeries(_.values(self._store), function (rec, cb) {
  163. self._get(rec.pos, false, safe.sure(cb, function (obj) {
  164. var id = simplifyKey(obj._id);
  165. _.forEach(self._idx,function(v, k) {
  166. v.set(obj, id);
  167. });
  168. cb();
  169. }));
  170. }, cb);
  171. };
  172. tcoll.prototype._compact = function (cb) {
  173. var self = this;
  174. var filename = self._filename + '.compact';
  175. fs.open(filename, 'w+', safe.sure(cb, function (fd) {
  176. var b1 = new Buffer(45);
  177. function get(pos, cb) {
  178. fs.read(self._fd, b1, 0, 45, pos, safe.trap_sure(cb, function (bytes, data) {
  179. var h1 = JSON.parse(data.toString());
  180. h1.o = parseInt(h1.o, 10);
  181. h1.k = parseInt(h1.k, 10);
  182. var b2 = new Buffer(h1.k + h1.o + 3);
  183. fs.read(self._fd, b2, 0, b2.length, pos + 45, safe.sure(cb, function (bytes, data) {
  184. cb(null, Buffer.concat([ b1, b2 ]));
  185. }));
  186. }));
  187. }
  188. var wpos = 0;
  189. var store = {};
  190. safe.forEachSeries(_.keys(self._store), function (k, cb) {
  191. var rec = self._store[k];
  192. get(rec.pos, safe.sure(cb, function (data) {
  193. fs.write(fd, data, 0, data.length, wpos, safe.sure(cb, function (written) {
  194. if (written != data.length) return cb(new Error('Insufficient disk space'));
  195. store[k] = { pos: wpos, sum: rec.sum };
  196. wpos += data.length;
  197. cb();
  198. }));
  199. }));
  200. }, function (err) {
  201. if (err) {
  202. fs.close(fd, function () {
  203. fs.unlink(filename, function () {
  204. cb(err);
  205. });
  206. });
  207. return;
  208. }
  209. if (!!process.platform.match(/^win/)) {
  210. // WINDOWS: unsafe because if something fail while renaming file it will not
  211. // restore automatically
  212. fs.close(self._fd, safe.sure(cb,function() {
  213. fs.close(fd, safe.sure(cb,function() {
  214. fs.unlink(self._filename, safe.sure(cb,function () {
  215. fs.rename(filename, self._filename, safe.sure(cb, function () {
  216. fs.open(self._filename, 'a+', safe.sure(cb, function (fd) {
  217. self._fd = fd;
  218. self._fsize = wpos;
  219. self._store = store;
  220. cb();
  221. }));
  222. }));
  223. }));
  224. }));
  225. }));
  226. } else {
  227. // safe way
  228. fs.rename(filename, self._filename, safe.sure(cb, function () {
  229. fs.close(self._fd);
  230. self._fd = fd;
  231. self._fsize = wpos;
  232. self._store = store;
  233. cb();
  234. }));
  235. }
  236. });
  237. }));
  238. };
  239. tcoll.prototype.drop = function (cb) {
  240. this._tdb.dropCollection(this._name,cb);
  241. };
  242. tcoll.prototype.rename = function (nname, opts, cb) {
  243. var self = this;
  244. if (_.isFunction(opts)) {
  245. cb = opts;
  246. opts = {};
  247. }
  248. var err = self._tdb._nameCheck(nname);
  249. if (err)
  250. return safe.back(cb,err);
  251. if (self._tdb._stype=="mem") {
  252. delete self._tdb._cols[self._name];
  253. self._tdb._cols[nname] = self;
  254. delete self._tdb._mstore[self._name];
  255. self._tdb._mstore[nname] = self._mstore;
  256. safe.back(cb,null);
  257. } else {
  258. self._tq.add(function (cb) {
  259. fs.rename(path.join(self._tdb._path,self._name),path.join(self._tdb._path,nname),safe.sure(cb, function () {
  260. delete self._tdb._cols[self._name];
  261. self._tdb._cols[nname] = self;
  262. self.collectionName = self._name = nname;
  263. cb();
  264. }));
  265. },true,cb);
  266. }
  267. };
  268. tcoll.prototype._stop = function (cb) {
  269. var self = this;
  270. self._tq.add(function (cb) {
  271. // this will prevent any tasks processed on this instance
  272. self._tq._stoped = true;
  273. if (self._fd) {
  274. fs.close(self._fd,safe.sure(cb, function () {
  275. cb(null,true);
  276. }));
  277. } else
  278. cb(null,false);
  279. },true,cb);
  280. };
  281. tcoll.prototype.createIndex = tcoll.prototype.ensureIndex = function (obj, options, cb) {
  282. var self = this;
  283. if (_.isFunction(options)) {
  284. cb = options;
  285. options = {};
  286. }
  287. cb = cb || function () {};
  288. options = options || {};
  289. var c = new tcursor(this,{},{},{});
  290. c.sort(obj);
  291. if (c._err)
  292. return safe.back(cb,c._err);
  293. var key = c._sort;
  294. if (key===null)
  295. return safe.back(cb,new Error("No fields are specified"));
  296. var index = self._idx[key];
  297. if (index)
  298. return safe.back(cb,null, index.name);
  299. // force array support when global option is set
  300. if (_.isUndefined(options._tiarr) && self._tdb._gopts.searchInArray)
  301. options._tiarr = true;
  302. var name = options.name || _.map(key, function (v) { return v[0] + '_' + v[1]; }).join('_');
  303. index = new tindex(key, self, options, name);
  304. if (self._tq._tc==-1) {
  305. // if no operation is pending just register index
  306. self._idx[key] = index;
  307. safe.back(cb, null, index.name);
  308. }
  309. else {
  310. // overwise register index operation
  311. this._tq.add(function (cb) {
  312. var range = _.values(self._store);
  313. safe.forEachSeries(range, function (rec, cb) {
  314. self._get(rec.pos, false, safe.sure(cb, function (obj) {
  315. index.set(obj,simplifyKey(obj._id));
  316. cb();
  317. }));
  318. }, safe.sure(cb, function () {
  319. self._idx[key] = index;
  320. cb();
  321. }));
  322. }, true, function (err) {
  323. if (err) cb(err);
  324. else cb(null, index.name);
  325. });
  326. }
  327. };
  328. tcoll.prototype.indexExists = function (idx, cb) {
  329. if (!_.isArray(idx))
  330. idx = [idx];
  331. var i = _.intersection(idx,_(this._idx).values().map('name').value());
  332. cb(null,i.length == idx.length);
  333. };
  334. tcoll.prototype.indexes = function (cb) {
  335. var self = this;
  336. this._tq.add(function (cb) {
  337. cb(null, _.values(self._idx));
  338. },false,cb);
  339. };
  340. tcoll.prototype._getM = function (pos, unsafe, cb) {
  341. safe.back(cb,null,unsafe?this._mstore[pos-1]:this._tdb._cloneDeep(this._mstore[pos-1]));
  342. };
  343. tcoll.prototype._getFS = function (pos, unsafe, cb) {
  344. var self = this;
  345. var cached = self._cache.get(pos,unsafe);
  346. if (cached)
  347. return safe.back(cb,null,cached);
  348. var b1 = new Buffer(45);
  349. fs.read(self._fd, b1, 0, 45, pos, safe.trap_sure(cb, function (bytes, data) {
  350. var h1 = JSON.parse(data.toString());
  351. h1.o = parseInt(h1.o,10);
  352. h1.k = parseInt(h1.k,10);
  353. var b2 = new Buffer(h1.o);
  354. fs.read(self._fd,b2,0,h1.o,pos+45+2+h1.k, safe.trap_sure(cb, function (bytes, data) {
  355. var obj = self._unwrapTypes(JSON.parse(data.toString()));
  356. if (bytes <= self._cmaxobj)
  357. self._cache.set(pos, obj);
  358. cb(null,obj);
  359. }))
  360. }))
  361. }
  362. tcoll.prototype.insert = function (docs, opts, cb ) {
  363. var self = this;
  364. if (_.isFunction(opts) && cb == null) {
  365. cb = opts;
  366. opts = {};
  367. }
  368. opts = opts || {};
  369. if (opts.w>0 && !_.isFunction(cb))
  370. throw new Error("Callback is required for safe update");
  371. cb = cb || function () {};
  372. if (!_.isArray(docs))
  373. docs = [docs];
  374. this._tq.add(function (cb) {
  375. safe.forEachSeries(docs, function (doc, cb) {
  376. if (_.isUndefined(doc._id)) {
  377. doc._id = new self._tdb.ObjectID();
  378. }
  379. self._put(doc, false, cb);
  380. }, safe.sure(cb, function () {
  381. cb(null, docs);
  382. }))
  383. }, true, cb)
  384. }
  385. tcoll.prototype._wrapTypes = function(obj) {
  386. var self = this;
  387. _.each(obj, function (v,k) {
  388. if (_.isDate(v))
  389. obj[k] = {$wrap:"$date",v:v.valueOf(),h:v}
  390. else if (v instanceof self._tdb.ObjectID)
  391. obj[k] = {$wrap:"$oid",v:v.toJSON()}
  392. else if (v instanceof self._tdb.Binary)
  393. obj[k] = {$wrap: "$bin", v: v.toJSON()};
  394. else if (_.isObject(v))
  395. self._wrapTypes(v)
  396. })
  397. return obj;
  398. }
  399. tcoll.prototype._ensureIds = function(obj) {
  400. var self = this;
  401. _.each(obj, function (v,k) {
  402. if (k.length >0) {
  403. if (k[0]=='$')
  404. throw new Error("key "+k+" must not start with '$'");;
  405. if (k.indexOf('.')!=-1)
  406. throw new Error("key "+k+" must not contain '.'");
  407. }
  408. if (_.isObject(v)) {
  409. if (v instanceof self._tdb.ObjectID) {
  410. if (v.id<0) {
  411. v._persist(++self._id)
  412. }
  413. }
  414. else
  415. self._ensureIds(v)
  416. }
  417. })
  418. return obj;
  419. }
  420. tcoll.prototype._unwrapTypes = function(obj) {
  421. var self = this;
  422. _.each(obj, function (v,k) {
  423. if (_.isObject(v)) {
  424. switch (v.$wrap) {
  425. case "$date": obj[k] = new Date(v.v); break;
  426. case "$oid":
  427. var oid = new self._tdb.ObjectID(v.v);
  428. obj[k]=oid;
  429. break;
  430. case "$bin":
  431. var bin = new self._tdb.Binary(new Buffer(v.v, 'base64'));
  432. obj[k] = bin;
  433. break;
  434. default: self._unwrapTypes(v);
  435. }
  436. }
  437. })
  438. return obj;
  439. }
  440. tcoll.prototype._putM = function (item_, remove, cb) {
  441. var item = this._tdb._cloneDeep(item_);
  442. var self = this;
  443. self._wq.add(function (cb) {
  444. try {
  445. item = self._ensureIds(item);
  446. } catch (err) {
  447. err.errmsg = err.toString();
  448. return cb(err)
  449. }
  450. if (_.isUndefined(item._id))
  451. return cb(new Error("Invalid object key (_id)"));
  452. var key = {_id:simplifyKey(item._id)};
  453. // check index update
  454. if (item && !remove) {
  455. try {
  456. _.forEach(self._idx,function(v,k) {
  457. v.set(item,key._id,true);
  458. })
  459. } catch (err) {
  460. err.errmsg = err.toString();
  461. return cb(err)
  462. }
  463. }
  464. if (remove) {
  465. self._mstore[self._store[key._id].pos-1]=null;
  466. delete self._store[key._id];
  467. }
  468. else {
  469. if (self._store[key._id]) {
  470. self._mstore[self._store[key._id].pos-1] = item;
  471. } else {
  472. self._mstore.push(item);
  473. self._store[key._id] = {pos: self._mstore.length};
  474. }
  475. }
  476. // update index
  477. _.forEach(self._idx,function(v,k) {
  478. if (!remove)
  479. v.set(item,key._id);
  480. else
  481. v.del(item,key._id);
  482. })
  483. cb(null);
  484. }, true, cb);
  485. }
  486. tcoll.prototype._putFS = function (item, remove, cb) {
  487. var self = this;
  488. self._wq.add(function (cb) {
  489. try {
  490. item = self._ensureIds(item);
  491. } catch (err) {
  492. err.errmsg = err.toString();
  493. return cb(err)
  494. }
  495. if (_.isUndefined(item._id))
  496. return cb(new Error("Invalid object key (_id)"));
  497. item = self._wrapTypes(item);
  498. var sobj = new Buffer(remove?"":JSON.stringify(item));
  499. item = self._unwrapTypes(item);
  500. var key = {_id:simplifyKey(item._id),_uid:self._id,_dt:(new Date()).valueOf()};
  501. if (remove) key._a = "del";
  502. else {
  503. var hash = crypto.createHash('md5');
  504. hash.update(sobj, 'utf8');
  505. key._s = hash.digest('hex');
  506. }
  507. var skey = new Buffer(JSON.stringify(key));
  508. var zeros = "0000000000";
  509. var lobj = sobj.length.toString();
  510. var lkey = skey.length.toString();
  511. lobj = zeros.substr(0,zeros.length - lobj.length)+lobj;
  512. lkey = zeros.substr(0,zeros.length - lkey.length)+lkey;
  513. var h1={k:lkey,o:lobj,v:"001"};
  514. var buf = new Buffer(JSON.stringify(h1)+"\n"+skey+"\n"+sobj+"\n");
  515. // check index update
  516. if (item && !remove) {
  517. try {
  518. _.forEach(self._idx,function(v,k) {
  519. v.set(item,key._id,true);
  520. })
  521. } catch (err) {
  522. err.errmsg = err.toString();
  523. return cb(err)
  524. }
  525. }
  526. safe.run(function (cb) {
  527. var rec = self._store[key._id];
  528. if (rec && rec.sum == key._s) return safe.back(cb);
  529. fs.write(self._fd, buf, 0, buf.length, self._fsize, safe.sure(cb, function (written) {
  530. if (remove)
  531. delete self._store[key._id];
  532. else
  533. self._store[key._id] = { pos: self._fsize, sum: key._s };
  534. if (remove || sobj.length > self._cmaxobj)
  535. self._cache.unset(self._fsize)
  536. else
  537. self._cache.set(self._fsize,item);
  538. self._fsize+=written;
  539. // randomly check for non exclusive file usage
  540. // which is growth of file that we are nor aware
  541. // randomly to avoid overhead
  542. if (self._check1==0) {
  543. this._check1 = Math.random()*100+1;
  544. fs.fstat(self._fd, safe.sure(cb, function (stat) {
  545. if (self._fsize!=stat.size)
  546. cb(new Error("File size mismatch. Are you use db/collection exclusively?"))
  547. else
  548. cb()
  549. }))
  550. } else {
  551. self._check1--;
  552. cb();
  553. }
  554. }));
  555. },
  556. function () {
  557. // update index
  558. _.forEach(self._idx,function(v,k) {
  559. if (!remove)
  560. v.set(item,key._id);
  561. else
  562. v.del(item,key._id);
  563. })
  564. cb(null);
  565. });
  566. }, true, cb);
  567. }
  568. tcoll.prototype.count = function (query, options, cb) {
  569. var self = this;
  570. if (arguments.length == 1) {
  571. cb = arguments[0];
  572. options = null;
  573. query = null;
  574. }
  575. if (arguments.length == 2) {
  576. query = arguments[0];
  577. cb = arguments[1];
  578. options = null;
  579. }
  580. if (query==null || _.size(query)==0) {
  581. this._tq.add(function (cb) {
  582. cb(null, _.size(self._store));
  583. },false,cb);
  584. } else
  585. self.find(query, options).count(cb);
  586. }
  587. tcoll.prototype.stats = function (cb) {
  588. var self = this;
  589. this._tq.add(function (cb) {
  590. cb(null, {count:_.size(self._store)});
  591. },false,cb);
  592. }
  593. var findOpts = ['limit','sort','fields','skip','hint','timeout','batchSize','safe','w'];
  594. tcoll.prototype.findOne = function () {
  595. var findArgs = Array.prototype.slice.call(arguments,0,arguments.length-1);
  596. var cb = arguments[arguments.length-1];
  597. this.find.apply(this,findArgs).limit(1).nextObject(cb);
  598. }
  599. tcoll.prototype.find = function () {
  600. var cb = null, query = {}, opts = {}, fields = null, skip = null, limit = null, sort = null;
  601. var argc = arguments.length;
  602. if (argc>0) {
  603. // guess callback, it is always latest
  604. cb = arguments[argc-1];
  605. if (!_.isFunction(cb))
  606. cb=null
  607. else
  608. argc--;
  609. if (argc>0) {
  610. // query should always exist
  611. query = arguments[0]
  612. if (argc>1) {
  613. if (argc==2) {
  614. var val = arguments[1];
  615. // worst case we get either options either fiels
  616. if (_.intersection(_.keys(val),findOpts).length!=0)
  617. opts = val
  618. else
  619. fields = val;
  620. } else {
  621. fields = arguments[1];
  622. if (argc == 3)
  623. opts = arguments[2]
  624. else {
  625. skip = arguments[2];
  626. limit = arguments[3]
  627. }
  628. }
  629. }
  630. }
  631. }
  632. opts = opts || {};
  633. skip = skip || opts.skip || null;
  634. limit = limit || opts.limit || null;
  635. fields = fields || opts.fields || null;
  636. sort = sort || opts.sort || null;
  637. var c = new tcursor(this,query, fields, opts);
  638. if (skip) c.skip(skip);
  639. if (limit) c.limit(limit);
  640. if (sort) c.sort(sort);
  641. if (cb)
  642. cb(null, c)
  643. else
  644. return c;
  645. }
  646. function simplifyKey(key) {
  647. var k = key;
  648. if (key.toJSON)
  649. k = key.toJSON();
  650. if (_.isNumber(k)||_.isString(k))
  651. return k;
  652. return k.toString();
  653. }
  654. tcoll.prototype.update = function (query, doc, opts, cb) {
  655. var self = this;
  656. if (_.isFunction(opts) && cb == null) {
  657. cb = opts;
  658. }
  659. opts = opts || {};
  660. if (opts.w>0 && !_.isFunction(cb))
  661. throw new Error("Callback is required for safe update");
  662. cb = cb || function () {}
  663. if (!_.isObject(query))
  664. throw new Error("selector must be a valid JavaScript object");
  665. if (!_.isObject(doc))
  666. throw new Error("document must be a valid JavaScript object");
  667. var multi = opts.multi || false;
  668. var updater = new Updater(doc, self._tdb);
  669. var $doc = updater.hasAtomic()?null:doc;
  670. this._tq.add(function (cb) {
  671. self.__find(query, null, 0, multi ? null : 1, null, opts.hint, {}, safe.sure(cb, function (res) {
  672. if (res.length==0) {
  673. if (opts.upsert) {
  674. $doc = $doc || query;
  675. $doc = self._tdb._cloneDeep($doc);
  676. updater.update($doc,true);
  677. if (_.isUndefined($doc._id))
  678. $doc._id = new self._tdb.ObjectID();
  679. self._put($doc, false, safe.sure(cb, function () {
  680. cb(null, 1,{updatedExisting:false,upserted:$doc._id,n:1})
  681. }))
  682. } else
  683. cb(null,0);
  684. } else {
  685. safe.forEachSeries(res, function (pos, cb) {
  686. self._get(pos, false, safe.sure(cb, function (obj) {
  687. // remove current version of doc from indexes
  688. _.forEach(self._idx,function(v,k) {
  689. v.del(obj,simplifyKey(obj._id));
  690. })
  691. var udoc = $doc;
  692. if (!$doc) {
  693. udoc = obj;
  694. updater.update(udoc);
  695. }
  696. udoc._id = obj._id;
  697. // put will add it back to indexes
  698. self._put(udoc, false, cb);
  699. }))
  700. }, safe.sure(cb,function () {
  701. cb(null, res.length, {updatedExisting:true,n:res.length});
  702. }))
  703. }
  704. }))
  705. },true,cb);
  706. }
  707. tcoll.prototype.findAndModify = function (query, sort, doc, opts, cb) {
  708. var self = this;
  709. if (_.isFunction(opts) && cb == null) {
  710. cb = opts;
  711. opts = {};
  712. }
  713. opts = opts || {};
  714. doc = doc || {};
  715. var updater = new Updater(doc, self._tdb);
  716. var $doc = updater.hasAtomic()?null:doc;
  717. var c = new tcursor(this,{}, opts.fields || {},{});
  718. c.sort(sort);
  719. if (c._err)
  720. return safe.back(cb,c._err);
  721. this._tq.add(function (cb) {
  722. self.__find(query, null, 0, 1, c._sort, opts.hint, {}, safe.sure(cb, function (res) {
  723. if (res.length==0) {
  724. if (opts.upsert) {
  725. $doc = $doc || query;
  726. $doc = self._tdb._cloneDeep($doc);
  727. updater.update($doc,true);
  728. if (_.isUndefined($doc._id))
  729. $doc._id = new self._tdb.ObjectID();
  730. self._put($doc, false, safe.sure(cb, function () {
  731. cb(null,opts.new?c._projectFields($doc):{})
  732. }))
  733. } else
  734. cb();
  735. } else {
  736. self._get(res[0], false, safe.sure(cb, function (obj) {
  737. var robj = (opts.new && !opts.remove) ? obj : self._tdb._cloneDeep(obj);
  738. // remove current version of doc from indexes
  739. _.forEach(self._idx,function(v,k) {
  740. v.del(obj,simplifyKey(obj._id));
  741. })
  742. var udoc = $doc;
  743. if (!$doc) {
  744. udoc = obj;
  745. updater.update(udoc);
  746. }
  747. udoc._id = obj._id;
  748. // put will add it back to indexes
  749. self._put(udoc, opts.remove?true:false, safe.sure(cb,function () {
  750. cb(null,c._projectFields(robj))
  751. }))
  752. }))
  753. }
  754. }))
  755. },true,cb);
  756. }
  757. tcoll.prototype.save = function (doc, opts, cb) {
  758. var self = this;
  759. cb = _.isFunction(doc)?doc:_.isFunction(opts)?opts:cb;
  760. cb = cb || function () {};
  761. doc = doc || {};
  762. opts = opts || {};
  763. this._tq.add(function (cb) {
  764. var res = doc;
  765. (function(cb) {
  766. if (_.isUndefined(doc._id)) {
  767. doc._id = new self._tdb.ObjectID();
  768. cb()
  769. } else {
  770. var id = simplifyKey(doc._id);
  771. var pos = self._store[id];
  772. // check if document with this id already exist
  773. if (pos) {
  774. // if so we need to fetch it to update index
  775. self._get(pos.pos, false, safe.sure(cb, function (oldDoc) {
  776. // remove current version of doc from indexes
  777. _.forEach(self._idx,function(v,k) {
  778. v.del(oldDoc,id);
  779. })
  780. res = 1;
  781. cb();
  782. }))
  783. } else cb();
  784. }
  785. })(safe.sure(cb, function () {
  786. self._put(doc, false, safe.sure(cb, function () {
  787. cb(null,res); // when update return 1 when new save return obj
  788. }))
  789. }))
  790. },true,cb);
  791. }
  792. tcoll.prototype.remove = function (query, opts, cb) {
  793. var self = this;
  794. if (_.isFunction(query)) {
  795. cb = query;
  796. query = opts = {};
  797. } else if (_.isFunction(opts)) {
  798. cb = opts;
  799. opts = {};
  800. }
  801. opts = opts || {};
  802. if (opts.w>0 && !_.isFunction(cb))
  803. throw new Error("Callback is required");
  804. cb = cb || function () {};
  805. var single = opts.single || false;
  806. this._tq.add(function (cb) {
  807. self.__find(query, null, 0, single ? 1 : null, null, opts.hint, {}, safe.sure(cb, function (res) {
  808. safe.forEachSeries(res, function (pos, cb) {
  809. self._get(pos, false, safe.sure(cb, function (obj) {
  810. self._put(obj,true,cb);
  811. }))
  812. }, safe.sure(cb, function () {
  813. cb(null,res.length);
  814. }))
  815. }))
  816. },true,cb);
  817. }
  818. tcoll.prototype.findAndRemove = function (query,sort,opts,cb) {
  819. var self = this;
  820. if (_.isFunction(sort) && cb == null && opts==null) {
  821. cb = sort;
  822. sort = {}
  823. opts = {};
  824. } else if (_.isFunction(opts) && cb == null) {
  825. cb = opts;
  826. opts = {};
  827. }
  828. opts = opts || {};
  829. sort = sort || {};
  830. var c = new tcursor(this,{},{},{});
  831. // Fix for mongoouse/tungus they pass sort as undefined
  832. c.sort(sort);
  833. if (c._err)
  834. return safe.back(cb,c._err);
  835. this._tq.add(function (cb) {
  836. self.__find(query, null, 0, 1, c._sort, opts.hint, {}, safe.sure(cb, function (res) {
  837. if (res.length==0)
  838. return cb();
  839. self._get(res[0], false, safe.sure(cb, function (obj) {
  840. self._put(obj,true,safe.sure(cb, function () {
  841. cb(null,obj);
  842. }))
  843. }))
  844. }))
  845. },true,cb);
  846. }
  847. tcoll.prototype._bestSortIndex = function (sort) {
  848. // no sort
  849. if (!sort) return null;
  850. // exact match
  851. if (this._idx[sort]) return this._idx[sort];
  852. // find potential sort indexes
  853. var pi = [];
  854. _.forEach(this._idx,function (idx) {
  855. var fields = idx.fields();
  856. var match = _.takeWhile(fields, function (kv, i) {
  857. return i < sort.length ? kv[0] == sort[i][0] : false;
  858. });
  859. if (match.length == sort.length) {
  860. var score = fields.length;
  861. _.forEach(sort,function (kv, i) {
  862. if (kv[1] != fields[i][1]) score += 1;
  863. });
  864. pi.push({ value: idx, score: score });
  865. }
  866. });
  867. if (pi.length === 0) return null;
  868. // select best index
  869. pi = pi.sort(function (l, r) { return l.score < r.score; });
  870. return pi[0].value;
  871. };
  872. function reduceIndexSet(pi) {
  873. var hit;
  874. do {
  875. hit = false;
  876. // compare each potential index with each other
  877. _.forEach(pi,function (v1, i1) {
  878. _.forEach(pi,function (v2, i2) {
  879. if (i1 == i2) return;
  880. // compare the set of possible keys for both indexes
  881. if (_.union(v1.k, v2.k).length == v1.k.length) {
  882. // key for v2 is a subset of key for v1, check equality
  883. if (v1.k.length == v2.k.length && v1.i.depth() > v2.i.depth()) {
  884. // keys are equal, but the depth of v2 is lower;
  885. // v2 is preferable, strike out v1
  886. pi.splice(i1, 1);
  887. } else {
  888. // in other two cases v1 is preferable, strike out v2
  889. pi.splice(i2, 1);
  890. }
  891. hit = true;
  892. return false;
  893. }
  894. });
  895. if (hit) return false;
  896. });
  897. } while (hit);
  898. };
  899. tcoll.prototype.__find = function (query, fields, skip, limit, sort, hint, arFields, cb) {
  900. var self = this;
  901. var range = [];
  902. // find sort index
  903. var si = this._bestSortIndex(sort);
  904. // for non empty query check indexes that we can use
  905. var qt = self._tdb.Finder.matcher(query);
  906. var pi = [];
  907. if (_.size(qt)>0) {
  908. _.forEach(self._idx,function (i) {
  909. var f = _.pluck(i.fields(), 0);
  910. var e = _.takeWhile(f, function (k) {
  911. return qt._ex(k) == 1 && (!hint || hint[k]);
  912. });
  913. if (e.length > 0) pi.push({ i: i, k: e, e: f.length > e.length });
  914. });
  915. }
  916. // if possible indexes found split the query and process
  917. // indexes separately
  918. if (!_.isEmpty(pi)) {
  919. // choose the most appropriate indexes
  920. reduceIndexSet(pi);
  921. // split query
  922. var io = {};
  923. _.forEach(pi,function (v) {
  924. _.forEach(v.k,function (k) {
  925. if (!io[k]) io[k] = qt.split(k);
  926. });
  927. });
  928. // process indexes
  929. var p = [];
  930. _.forEach(pi,function (st) {
  931. // this action applies to all indexes
  932. var r = io[st.k[0]]._index(st.i);
  933. // process subfields of compound index
  934. _.forEach(st.k.slice(1),function (k) {
  935. var v = io[k];
  936. r = _.flatten(_.map(r, function (si) { return v._index(si); }));
  937. });
  938. // expand subindexes to plain ids
  939. if (st.e) r = _.flatten(_.map(r, function (si) { return si.all(); }));
  940. // store result of index search
  941. p.push(r);
  942. });
  943. if (p.length == 1) {
  944. p = p[0];
  945. // optimization for the case when search and sorting indexes are the same
  946. if (si && pi[0].i === si) {
  947. var sif = si.fields();
  948. if (_.every(sort, function (v, i) { return sif[i][1] == v[1]; })) {
  949. // sort order exactly matches index order,
  950. // so the result is already sorted
  951. sort = null;
  952. } else if (_.every(sort, function (v, i) { return sif[i][1] == -v[1]; })) {
  953. // sort order is exactly opposite to index order,
  954. // so the result is sorted, but in reverse direction
  955. p.reverse();
  956. sort = null;
  957. }
  958. }
  959. } else {
  960. // TODO: use sort index as intersect base to speedup sorting
  961. p = tutils.intersectIndexes(p);
  962. }
  963. // nowe we have ids, need to convert them to positions
  964. _.forEach(p,function (_id) {
  965. range.push(self._store[_id].pos)
  966. })
  967. } else {
  968. if (si) {
  969. _.each(si.all(_.pluck(sort, 1)), function (_id) {
  970. range.push(self._store[_id].pos)
  971. })
  972. //if (order==-1)
  973. // range.reverse();
  974. sort = null;
  975. } else
  976. range = _.values(self._store).map(function (rec) { return rec.pos; });
  977. }
  978. if (sort && si) {
  979. var ps = {};
  980. _.each(range,function (pos) {
  981. ps[pos] = true;
  982. });
  983. range = [];
  984. _.each(si.all(_.pluck(sort, 1)),function (_id) {
  985. var pos = self._store[_id].pos;
  986. if (_.has(ps,pos)) range.push(pos);
  987. });
  988. //if (order == -1)
  989. // range.reverse();
  990. sort = null;
  991. }
  992. // no sort, no query then return right away
  993. if (sort==null && (_.size(qt)==0 || qt._args.length==0)) {
  994. if (skip!=0 || limit!=null) {
  995. var c = Math.min(range.length-skip,limit?limit:range.length-skip);
  996. range = range.splice(skip,c)
  997. }
  998. return safe.back(cb,null,range);
  999. }
  1000. var matcher = null;
  1001. // check if we can use simple match or array match function
  1002. var arrayMatch = false;
  1003. if (self._tdb._gopts.searchInArray)
  1004. arrayMatch = true;
  1005. else {
  1006. var fields = qt.fields();
  1007. _.each(fields, function (v,k) {
  1008. if (arFields[k])
  1009. arrayMatch = true;
  1010. })
  1011. }
  1012. eval("matcher = function (obj) { return "+ (arrayMatch?qt.native3():qt.native()) + " }");
  1013. // create sort index
  1014. if (sort) {
  1015. si = new tindex(sort,self);
  1016. }
  1017. // now simple non-index search
  1018. var res = [];
  1019. var found = 0;
  1020. safe.forEachSeries(range, function (pos, cb) {
  1021. if (sort==null && limit && res.length>=limit)
  1022. return safe.back(cb);
  1023. self._get(pos, true, safe.sure(cb, function (obj) {
  1024. if (matcher(obj)) {
  1025. if (sort!=null || found>=skip) {
  1026. if (sort==null)
  1027. res.push(pos);
  1028. else
  1029. si.set(obj,pos);
  1030. }
  1031. found++;
  1032. }
  1033. cb()
  1034. }))
  1035. }, safe.sure(cb, function () {
  1036. if (sort) {
  1037. res = si.all();
  1038. //if (order==-1) {
  1039. // res.reverse();
  1040. //}
  1041. if (skip!=0 || limit!=null) {
  1042. var c = Math.min(res.length-skip,limit?limit:res.length-skip);
  1043. res = res.splice(skip,c)
  1044. }
  1045. }
  1046. cb(null, res);
  1047. }))
  1048. }
  1049. tcoll.prototype._find = function (query, fields, skip, limit, sort_, hint, arFields, cb) {
  1050. var self = this;
  1051. this._tq.add(function (cb) {
  1052. self.__find(query, fields, skip, limit, sort_, hint, arFields, cb);
  1053. }, false, cb);
  1054. }
  1055. function code2fn(obj) {
  1056. if (_.isObject(obj)) {
  1057. _.each(obj,function (value, key) {
  1058. if (value instanceof Code) {
  1059. with (value.scope) {
  1060. obj[key] = eval('(' + value.code + ')');
  1061. }
  1062. }
  1063. else code2fn(value);
  1064. });
  1065. }
  1066. }
  1067. tcoll.prototype.mapReduce = function (map, reduce, opts, cb) {
  1068. var self = this;
  1069. if (_.isFunction(opts)) {
  1070. cb = opts;
  1071. opts = {};
  1072. }
  1073. if (!opts.out) return safe.back(cb, new Error('the out option parameter must be defined'));
  1074. if (!opts.out.inline && !opts.out.replace) {
  1075. return safe.back(cb, new Error('the only supported out options are inline and replace'));
  1076. }
  1077. code2fn(opts.scope);
  1078. var m = {};
  1079. function emit(k, v) {
  1080. var values = m[k];
  1081. if (!values) m[k] = [ v ];
  1082. else {
  1083. values.push(v);
  1084. if (values.length > 1000) values = [ reduce(k, values) ];
  1085. }
  1086. }
  1087. with (opts.scope || {}) {
  1088. try {
  1089. if (map instanceof Code) {
  1090. with (map.scope) {
  1091. map = eval('(' + map.code + ')');
  1092. }
  1093. } else map = eval('(' + map + ')');
  1094. if (reduce instanceof Code) {
  1095. with (reduce.scope) {
  1096. reduce = eval('(' + reduce.code + ')');
  1097. }
  1098. } else reduce = eval('(' + reduce + ')');
  1099. if (finalize instanceof Code) {
  1100. with (finalize.scope) {
  1101. finalize = eval('(' + finalize.code + ')');
  1102. }
  1103. } else var finalize = eval('(' + opts.finalize + ')');
  1104. } catch (e) {
  1105. return safe.back(cb,e);
  1106. }
  1107. }
  1108. self.find(opts.query, null, { limit: opts.limit, sort: opts.sort }, safe.sure(cb, function (c) {
  1109. var doc;
  1110. safe.doUntil(
  1111. function (cb) {
  1112. c.nextObject(safe.trap_sure(cb, function (_doc) {
  1113. doc = _doc;
  1114. if (doc) map.call(doc);
  1115. return cb();
  1116. }));
  1117. },
  1118. function () {
  1119. return doc === null;
  1120. },
  1121. safe.trap_sure(cb, function () {
  1122. _.each(m,function (v, k) {
  1123. v = v.length > 1 ? reduce(k, v) : v[0];
  1124. if (finalize) v = finalize(k, v);
  1125. m[k] = v;
  1126. });
  1127. var stats = {};
  1128. if (opts.out.inline) return process.nextTick(function () {
  1129. cb(null, _.values(m), stats); // execute outside of trap
  1130. });
  1131. // write results to collection
  1132. safe.waterfall([
  1133. function (cb) {
  1134. self._tdb.collection(opts.out.replace, { strict: 1 }, function (err, col) {
  1135. if (err) return cb(null, null);
  1136. col.drop(cb);
  1137. });
  1138. },
  1139. function (arg, cb) {
  1140. self._tdb.collection(opts.out.replace, {}, cb);
  1141. },
  1142. function (col, cb) {
  1143. var docs = [];
  1144. _.each(m,function (value, key) {
  1145. var doc = {
  1146. _id: key,
  1147. value: value
  1148. };
  1149. docs.push(doc);
  1150. });
  1151. col.insert(docs, safe.sure(cb, function () {
  1152. if (opts.verbose) cb(null, col, stats);
  1153. else cb(null, col);
  1154. }));
  1155. }
  1156. ], cb);
  1157. }
  1158. )); // doUntil
  1159. }));
  1160. };
  1161. tcoll.prototype.group = function (keys, condition, initial, reduce, finalize, command, options, callback) {
  1162. var self = this;
  1163. var args = Array.prototype.slice.call(arguments, 3);
  1164. callback = args.pop();
  1165. reduce = args.length ? args.shift() : null;
  1166. finalize = args.length ? args.shift() : null;
  1167. command = args.length ? args.shift() : null;
  1168. options = args.length ? args.shift() : {};
  1169. if (!_.isFunction(finalize)) {
  1170. command = finalize;
  1171. finalize = null;
  1172. }
  1173. code2fn(options.scope);
  1174. with (options.scope || {}) {
  1175. try {
  1176. if (_.isFunction(keys)) keys = eval('(' + keys + ')');
  1177. else if (keys instanceof Code) {
  1178. with (keys.scope) {
  1179. keys = eval('(' + keys.code + ')');
  1180. }
  1181. }
  1182. if (reduce instanceof Code) {
  1183. with (reduce.scope) {
  1184. reduce = eval('(' + reduce.code + ')');
  1185. }
  1186. } else reduce = eval('(' + reduce + ')');
  1187. if (finalize instanceof Code) {
  1188. with (finalize.scope) {
  1189. finalize = eval('(' + finalize.code + ')');
  1190. }
  1191. } else finalize = eval('(' + finalize + ')');
  1192. } catch (e) {
  1193. return callback(e);
  1194. }
  1195. }
  1196. var m = {};
  1197. self.find(condition, safe.sure(callback, function (c) {
  1198. var doc;
  1199. safe.doUntil(
  1200. function (cb) {
  1201. c.nextObject(safe.sure(cb, function (_doc) {
  1202. doc = _doc;
  1203. if (!doc) return cb();
  1204. var keys2 = keys;
  1205. if (_.isFunction(keys)) keys2 = keys(doc);
  1206. if (!_.isArray(keys2)) {
  1207. var keys3 = [];
  1208. _.each(keys2,function (v, k) {
  1209. if (v) keys3.push(k);
  1210. });
  1211. keys2 = keys3;
  1212. }
  1213. var key = {};
  1214. _.each(keys2,function (k) {
  1215. key[k] = doc[k];
  1216. });
  1217. var skey = JSON.stringify(key);
  1218. var obj = m[skey];
  1219. if (!obj) obj = m[skey] = _.extend({}, key, initial);
  1220. try {
  1221. reduce(doc, obj);
  1222. } catch (e) {
  1223. return cb(e);
  1224. }
  1225. cb();
  1226. }));
  1227. },
  1228. function () {
  1229. return doc === null;
  1230. },
  1231. safe.sure(callback, function () {
  1232. var result = _.values(m);
  1233. if (finalize) {
  1234. _.each(result,function (value) {
  1235. finalize(value);
  1236. });
  1237. }
  1238. callback(null, result);
  1239. })
  1240. );
  1241. }));
  1242. };