total.js
Version:
MVC framework for Node.js
2,022 lines (1,664 loc) • 182 kB
JavaScript
// Copyright 2012-2020 (c) Peter Širka <petersirka@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* @module NoSQL
* @version 3.4.4
*/
'use strict';
const Readable = require('stream').Readable;
const Fs = require('fs');
const Path = require('path');
const NoSQLStream = require('./nosqlstream');
const REG_FIELDS_CLEANER = /"|`|\||'|\s/g;
if (!global.framework_utils)
global.framework_utils = require('./utils');
if (!global.framework_image)
global.framework_image = require('./image');
if (!global.framework_nosql)
global.framework_nosql = exports;
if (!global.framework_builders)
global.framework_builders = require('./builders');
const EXTENSION = '.nosql';
const EXTENSION_TABLE = '.table';
const EXTENSION_TABLE_BACKUP = '.table-backup';
const EXTENSION_BINARY = '.nosql-binary';
const EXTENSION_LOG = '.nosql-log';
const EXTENSION_MAPREDUCE = '.nosql-mapreduce';
const EXTENSION_BACKUP = '.nosql-backup';
const EXTENSION_META = '.meta';
const EXTENSION_COUNTER = '-counter2';
const BINARY_HEADER_LENGTH = 2000;
const COUNTER_MMA = [0, 0];
const DIRECTORYLENGTH = 9;
const FLAGS_READ = ['get'];
const INMEMORY = {};
const JSONBOOL = '":true ';
const NEWLINE = '\n';
const REGBOOL = /":true/g; // for updates of boolean types
const REGCHINA = /[\u3400-\u9FBF]/;
const REGCLEAN = /^[\s]+|[\s]+$/g;
const REGTESCAPE = /\||\n|\r/g;
const REGTUNESCAPE = /%7C|%0D|%0A/g;
const REGTESCAPETEST = /\||\n|\r/;
const IMAGES = { gif: 1, jpg: 1, jpeg: 1, png: 1, svg: 1 };
const BINARYREADDATA = { start: BINARY_HEADER_LENGTH };
const BINARYREADDATABASE64 = { start: BINARY_HEADER_LENGTH, encoding: 'base64' };
const BINARYREADMETA = { start: 0, end: BINARY_HEADER_LENGTH - 1, encoding: 'binary' };
const BOOLEAN = { '1': 1, 'true': 1, 'on': 1 };
const TABLERECORD = { '+': 1, '-': 1, '*': 1 };
const CLUSTERMETA = {};
const UNKNOWN = 'unknown';
const MKDIR = { recursive: true };
const COMPARER = global.Intl ? global.Intl.Collator().compare : function(a, b) {
return a.removeDiacritics().localeCompare(b.removeDiacritics());
};
const NEWLINEBUF = Buffer.from('\n', 'utf8');
const CACHE = {};
var JSONBUFFER = process.argv.findIndex(n => n.endsWith('nosqlworker.js')) === -1 ? 20 : 40;
var FORK;
var FORKCALLBACKS;
function clusterlock(db, method) {
Fs.open(db.filenameLock, 'wx', function(err, fd) {
if (err) {
setTimeout(clusterlock, 100, db, method);
return;
}
Fs.write(fd, F.id.toString(), function(err) {
err && F.error('NoSQLStream.lock.write()', err);
Fs.close(fd, function(err) {
err && F.error('NoSQLStream.lock.close()', err);
db.locked = true;
db[method]();
});
});
});
}
function clusterunlock(db) {
if (db.locked) {
db.locked = false;
Fs.unlink(db.filenameLock, NOOP);
}
}
function promise(fn) {
var self = this;
return new Promise(function(resolve, reject) {
self.callback(function(err, result) {
if (err)
reject(err);
else
resolve(fn == null ? result : fn(result));
});
});
}
exports.kill = function(signal) {
FORK && TRY(() => FORK && FORK.kill && FORK.kill(signal || 'SIGTERM'));
};
exports.pid = function() {
return FORK ? FORK.pid : 0;
};
exports.worker = function() {
if (FORK || F.isCluster)
return;
// Clears unhandled callbacks
ON('service', function() {
var keys = Object.keys(FORKCALLBACKS);
if (!keys.length)
return;
var time = Date.now();
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i];
var item = FORKCALLBACKS[key];
if (item && item.time) {
var diff = time - item.time;
if (diff >= 60000) {
delete FORKCALLBACKS[key];
var err = new Error('NoSQL worker timeout.');
switch (item.type) {
case 'find':
item.builder && item.builder.$callback2(err, EMPTYARRAY, 0, EMPTYOBJECT);
break;
case 'count':
item.builder && item.builder.$callback2(err, EMPTYOBJECT, 0, EMPTYOBJECT);
break;
case 'insert':
case 'update':
case 'remove':
item.builder && item.builder.$callback && item.builder.$callback(err, EMPTYOBJECT, EMPTYOBJECT);
break;
case 'clean':
case 'clear':
item.callback && item.callback(err);
break;
case 'stream':
item.callback && item.callback(err, EMPTYOBJECT, 0);
break;
default:
item.callback && item.callback(err, EMPTYOBJECT, EMPTYOBJECT);
break;
}
}
}
}
});
FORKCALLBACKS = {};
FORK = require('child_process').fork(module.filename.replace(/\.js$/, '') + 'worker.js', [], { cwd: F.directory });
FORK.send({ TYPE: 'init', directory: F.path.root() });
FORK.on('message', function(msg) {
switch (msg.TYPE) {
case 'find':
var obj = FORKCALLBACKS[msg.id];
obj && obj.builder.$callback2(msg.err, msg.response, msg.count, msg.repository);
break;
case 'count':
var obj = FORKCALLBACKS[msg.id];
obj && obj.builder.$callback2(msg.err, msg.response, msg.count, msg.repository);
break;
case 'insert':
var obj = FORKCALLBACKS[msg.id];
obj && obj.builder.$callback && obj.builder.$callback(msg.err, msg.response, msg.repository);
break;
case 'update':
var obj = FORKCALLBACKS[msg.id];
obj && obj.builder.$callback && obj.builder.$callback(msg.err, msg.response, msg.repository);
break;
case 'remove':
var obj = FORKCALLBACKS[msg.id];
obj && obj.builder.$callback && obj.builder.$callback(msg.err, msg.response, msg.repository);
break;
case 'backup':
case 'restore':
case 'counter.read':
case 'counter.stats':
case 'counter.clear':
case 'storage.stats':
case 'storage.clear':
var obj = FORKCALLBACKS[msg.id];
obj && obj.callback && obj.callback(msg.err, msg.response);
break;
case 'stream':
var obj = FORKCALLBACKS[msg.id];
obj && obj.callback && obj.callback(msg.err, msg.repository || {}, msg.count);
break;
case 'storage.scan':
var obj = FORKCALLBACKS[msg.id];
obj && obj.callback && obj.callback(msg.err, msg.response, msg.repository);
break;
case 'callback':
var obj = FORKCALLBACKS[msg.id];
obj && obj.callback && obj.callback(msg.err);
break;
}
delete FORKCALLBACKS[msg.id];
});
var CMD = {};
function send(instance, type) {
var obj = {};
obj.type = type;
obj.name = instance.name;
obj.time = Date.now();
obj.t = instance instanceof Table;
if (arguments.length > 2) {
obj.arg = [];
for (var i = 2; i < arguments.length; i++)
obj.arg.push(arguments[i]);
}
setImmediate(send2, obj);
return obj;
}
function notify(instance, type) {
var obj = {};
obj.type = type;
obj.name = instance.name;
obj.time = Date.now();
obj.t = instance instanceof Table;
if (arguments.length > 2) {
obj.arg = [];
for (var i = 2; i < arguments.length; i++)
obj.arg.push(arguments[i]);
}
setImmediate(send2, obj, false);
return obj;
}
function send2(obj, callback) {
CMD.TYPE = obj.type;
CMD.arg = obj.arg;
CMD.data = obj.builder ? obj.builder.stringify() : null;
CMD.name = obj.name;
CMD.t = obj.t;
if (callback !== false) {
CMD.id = Math.random().toString(32).substring(2);
FORKCALLBACKS[CMD.id] = obj;
}
FORK.send(CMD);
}
var DP = Database.prototype;
var CP = Counter.prototype;
var SP = Storage.prototype;
TP.once = TP.on = TP.emit = TP.removeListener = TP.removeAllListeners = DP.once = DP.on = DP.emit = DP.removeListener = DP.removeAllListeners = CP.on = CP.once = CP.emit = CP.removeListener = CP.removeAllListeners = function() {
PRINTLN('ERROR --> NoSQL events are not supported in fork mode.');
};
TP.listing = TP.list = DP.listing = DP.list = function(builder) {
if (builder instanceof DatabaseBuilder)
builder.db = this;
else
builder = new DatabaseBuilder(this);
builder.$options.listing = true;
builder.$take = builder.$options.take = 100;
return send(this, 'find').builder = builder;
};
TP.find = DP.find = function(builder) {
if (builder instanceof DatabaseBuilder)
builder.db = this;
else
builder = new DatabaseBuilder(this);
return send(this, 'find').builder = builder;
};
TP.find2 = DP.find2 = function(builder) {
if (builder instanceof DatabaseBuilder)
builder.db = this;
else
builder = new DatabaseBuilder(this);
return send(this, 'find2').builder = builder;
};
TP.top = DP.top = function(max) {
var builder = new DatabaseBuilder(this);
builder.take(max);
return send(this, 'find').builder = builder;
};
TP.one = DP.one = function() {
var builder = new DatabaseBuilder(this);
builder.first();
return send(this, 'one').builder = builder;
};
TP.insert = DP.insert = function(doc, unique) {
var self = this;
var builder;
if (doc.$$schema)
doc = doc.$clean();
if (unique) {
builder = self.one();
var callback;
builder.callback(function(err, d) {
if (d)
callback && callback(null, 0);
else {
var tmp = self.insert(doc);
tmp.callback(callback);
tmp.$options.log = builder.$options.log;
}
});
builder.callback = function(fn) {
callback = fn;
return builder;
};
return builder;
}
return send(self, 'insert', doc).builder = new DatabaseBuilder2(self);
};
TP.count = DP.count = function() {
var builder = new DatabaseBuilder(this);
return send(this, 'count').builder = builder;
};
DP.view = function() {
throw new Error('NoSQL Views are not supported.');
};
TP.update = DP.update = function(doc, insert) {
var val = doc.$$schema ? doc.$clean() : doc;
if (typeof(val) === 'function')
val = val.toString();
return send(this, 'update', val, insert).builder = new DatabaseBuilder(this);
};
TP.modify = DP.modify = function(doc, insert) {
var val = doc.$$schema ? doc.$clean() : doc;
if (typeof(val) === 'function')
val = val.toString();
return send(this, 'modify', val, insert).builder = new DatabaseBuilder(this);
};
DP.restore = function(filename, callback) {
var obj = send(this, 'restore', filename);
obj.callback = callback;
return this;
};
DP.backup = function(filename, callback) {
var obj = send(this, 'backup', filename);
obj.callback = callback;
return this;
};
DP.refresh = function() {
return this;
};
DP.drop = function() {
notify(this, 'drop');
return this;
};
TP.clear = DP.clear = function(callback) {
send(this, 'clear').callback = callback;
return this;
};
TP.clean = DP.clean = function(callback) {
send(this, 'clean').callback = callback;
return this;
};
TP.ready = DP.ready = function(callback) {
callback && callback();
return this;
};
TP.remove = DP.remove = function(filename) {
return send(this, 'remove', filename).builder = new DatabaseBuilder(this);
};
TP.stream = DP.stream = function(fn, repository, callback) {
if (typeof(repository) === 'function') {
callback = repository;
repository = undefined;
}
send(this, 'stream', fn.toString(), repository).callback = callback;
return this;
};
CP.min = function(id, count) {
notify(this.db, 'counter.min', id, count);
return this;
};
CP.max = function(id, count) {
notify(this.db, 'counter.max', id, count);
return this;
};
CP.sum = CP.inc = CP.hit = function(id, count) {
notify(this.db, 'counter.hit', id, count);
return this;
};
CP.remove = function(id) {
notify(this.db, 'counter.remove', id);
return this;
};
CP.read = function(options, callback) {
send(this.db, 'counter.read', options).callback = callback;
return this;
};
CP.stats = CP.stats_sum = function(top, year, month, day, type, callback) {
if (typeof(day) == 'function') {
callback = day;
day = null;
} else if (typeof(month) == 'function') {
callback = month;
month = null;
} else if (typeof(year) === 'function') {
callback = year;
year = month = null;
}
send(this.db, 'counter.stats', top, year, month, day, type).callback = callback;
return this;
};
CP.clear = function(callback) {
send(this.db, 'counter.clear').callback = callback;
return this;
};
SP.insert = function(doc) {
notify(this.db, 'storage.insert', doc.$$schema ? doc.$clean() : doc);
return this;
};
SP.scan = function(beg, end, mapreduce, callback) {
if (typeof(beg) === 'function') {
mapreduce = beg;
callback = end;
beg = null;
end = null;
} else if (typeof(end) === 'function') {
callback = mapreduce;
mapreduce = end;
end = null;
}
send(this.db, 'storage.scan', beg, end, mapreduce.toString()).callback = callback;
return this;
};
SP.mapreduce = function(name, fn) {
send(this.db, 'storage.mapreduce', name, fn);
return this;
};
SP.stats = function(name, callback) {
if (typeof(name) === 'function') {
callback = name;
name = undefined;
}
send(this.db, 'storage.stats', name).callback = callback;
return this;
};
SP.clear = function(beg, end, callback) {
if (typeof(beg) === 'function') {
callback = end;
beg = null;
end = null;
} else if (typeof(end) === 'function') {
callback = end;
end = null;
}
send(this.db, 'storage.clear', beg, end).callback = callback;
return this;
};
};
function Table(name, filename, readonly, specific) {
var t = this;
t.filename = readonly ? filename : filename + (specific ? '' : EXTENSION_TABLE);
t.filenameBackup = readonly ? '' : filename + EXTENSION_TABLE_BACKUP;
t.filenameCounter = readonly ? '' : filename + (specific ? '' : EXTENSION_TABLE) + EXTENSION_COUNTER;
t.filenameMeta = readonly ? '' : filename + (specific ? '' : EXTENSION_TABLE) + '-meta';
t.directory = Path.dirname(filename);
t.filenameLock = t.filename + '-lock';
t.name = name;
t.$name = '$' + name;
t.pending_reader = [];
t.pending_reader2 = [];
t.pending_update = [];
t.pending_append = [];
t.pending_reader = [];
t.pending_remove = [];
t.pending_streamer = [];
t.pending_clean = [];
t.pending_clear = [];
t.pending_locks = [];
t.$events = {};
t.step = 0;
t.ready = false;
t.$free = true;
t.$writting = false;
t.$reading = false;
t.$allocations = true;
t.counter = readonly ? null : new Counter(t);
t.$meta();
var schema = CONF['table_' + name] || CONF['table.' + name];
Fs.createReadStream(t.filename, { end: 1200 }).once('data', function(chunk) {
if (schema) {
t.parseSchema(schema.replace(/;|,/g, '|').trim().split('|'));
schema = t.stringifySchema();
}
t.parseSchema(chunk.toString('utf8').split('\n', 1)[0].split('|'));
t.ready = true;
if (schema && t.stringifySchema() !== schema) {
t.$header = Buffer.byteLength(t.stringifySchema()) + 1;
t.extend(schema);
} else
t.$header = Buffer.byteLength(schema ? schema : t.stringifySchema()) + 1;
t.next(0);
}).on('error', function(e) {
if (schema) {
t.parseSchema(schema.replace(/;|,/g, '|').trim().split('|'));
var bschema = t.stringifySchema();
t.$header = Buffer.byteLength(bschema) + 1;
Fs.writeFileSync(t.filename, bschema + NEWLINE, 'utf8');
t.ready = true;
t.next(0);
} else {
t.readonly = true;
t.pending_reader.length && (t.pending_reader = []);
t.pending_update.length && (t.pending_update = []);
t.pending_append.length && (t.pending_append = []);
t.pending_reader.length && (t.pending_reader = []);
t.pending_remove.length && (t.pending_remove = []);
t.pending_streamer.length && (t.pending_streamer = []);
t.pending_locks.length && (t.pending_locks = []);
t.pending_clean.length && (t.pending_clean = []);
t.pending_clear.length && (t.pending_clear = []);
t.throwReadonly(e);
}
});
}
function Database(name, filename, readonly, specific) {
var self = this;
var http = filename.substring(0, 6);
self.readonly = http === 'http:/' || http === 'https:';
self.filename = self.readonly ? filename.format('') : readonly ? filename : filename + (specific ? '' : EXTENSION);
self.directory = Path.dirname(filename);
if (!readonly) {
self.filenameLock = self.filename + '-lock';
self.filenameCounter = self.readonly ? filename.format('counter', '-') : filename + (specific ? '' : EXTENSION) + EXTENSION_COUNTER;
self.filenameLog = self.readonly || readonly ? '' : filename + EXTENSION_LOG;
self.filenameBackup = self.readonly || readonly ? '' : filename + EXTENSION_BACKUP;
self.filenameStorage = self.readonly || readonly ? '' : filename + '-storage/{0}' + (specific ? '' : EXTENSION);
self.filenameMeta = filename + EXTENSION_META;
self.filenameBackup2 = framework_utils.join(self.directory, name + '_backup' + (specific ? '' : EXTENSION));
self.inmemory = {};
self.inmemorylastusage;
// self.metadata;
self.$meta();
}
self.name = name;
self.pending_update = [];
self.pending_append = [];
self.pending_reader = [];
self.pending_remove = [];
self.pending_reader2 = [];
self.pending_streamer = [];
self.pending_clean = [];
self.pending_clear = [];
self.pending_locks = [];
self.step = 0;
self.pending_drops = false;
self.pending_reindex = false;
self.binary = self.readonly || readonly ? null : new Binary(self, self.directory + '/' + self.name + '-binary/');
self.storage = self.readonly || readonly ? null : new Storage(self, self.directory + '/' + self.name + '-storage/');
self.counter = readonly ? null : new Counter(self);
self.$timeoutmeta;
self.$events = {};
self.$free = true;
self.$writting = false;
self.$reading = false;
}
const TP = Table.prototype;
const DP = Database.prototype;
TP.memory = DP.memory = function(count, size) {
var self = this;
count && (self.buffercount = count + 1); // def: 15 - count of stored documents in memory while reading/writing
size && (self.buffersize = size * 1024); // def: 32 - size of buffer in kB
return self;
};
TP.view = DP.view = function() {
throw new Error('NoSQL Views are not supported in this version.');
};
TP.emit = DP.emit = function(name, a, b, c, d, e, f, g) {
var evt = this.$events[name];
if (evt) {
var clean = false;
for (var i = 0, length = evt.length; i < length; i++) {
if (evt[i].$once)
clean = true;
evt[i].call(this, a, b, c, d, e, f, g);
}
if (clean) {
evt = evt.remove(n => n.$once);
if (evt.length)
this.$events[name] = evt;
else
this.$events[name] = undefined;
}
}
return this;
};
TP.on = DP.on = function(name, fn) {
if (!fn.$once)
this.$free = false;
if (this.$events[name])
this.$events[name].push(fn);
else
this.$events[name] = [fn];
return this;
};
TP.once = DP.once = function(name, fn) {
fn.$once = true;
return this.on(name, fn);
};
TP.removeListener = DP.removeListener = function(name, fn) {
var evt = this.$events[name];
if (evt) {
evt = evt.remove(n => n === fn);
if (evt.length)
this.$events[name] = evt;
else
this.$events[name] = undefined;
}
return this;
};
TP.removeAllListeners = DP.removeAllListeners = function(name) {
if (name === true)
this.$events = EMPTYOBJECT;
else if (name)
this.$events[name] = undefined;
else
this.$events[name] = {};
return this;
};
exports.Database = Database;
exports.DatabaseBuilder = DatabaseBuilder;
exports.DatabaseBuilder2 = DatabaseBuilder2;
exports.DatabaseCounter = Counter;
exports.DatabaseBinary = Binary;
exports.DatabaseStorage = Storage;
exports.DatabaseTable = Table;
exports.load = function(name, filename, specific) {
return new Database(name, filename, undefined, specific);
};
exports.table = function(name, filename, specific) {
return new Table(name, filename, undefined, specific);
};
exports.memory = exports.inmemory = function(name) {
return INMEMORY[name] = true;
};
TP.get = DP.get = function(name) {
return this.meta(name);
};
TP.set = DP.set = function(name, value) {
return this.meta(name, value);
};
TP.meta = DP.meta = function(name, value, nosave) {
var self = this;
if (value === undefined)
return self.metadata ? self.metadata[name] : undefined;
if (!self.metadata)
self.metadata = {};
self.metadata[name] = value;
clearTimeout(self.timeoutmeta);
if (!nosave)
self.timeoutmeta = setTimeout(() => self.$meta(true), 500);
if (F.isCluster && !nosave) {
CLUSTERMETA.ID = F.id;
CLUSTERMETA.TYPE = (self instanceof Table ? 'table' : 'nosql') + '-meta';
CLUSTERMETA.name = self.name;
CLUSTERMETA.key = name;
CLUSTERMETA.value = value;
process.send(CLUSTERMETA);
}
return self;
};
TP.backups = DP.backups = function(filter, callback) {
if (callback === undefined) {
callback = filter;
filter = null;
}
var self = this;
var isTable = self instanceof Table;
if (isTable && !self.ready) {
setTimeout((self, filter, callback) => self.backups(filter, callback), 500, self, filter, callback);
return self;
}
var stream = Fs.createReadStream(self.filenameBackup);
var output = [];
var tmp = {};
tmp.keys = self.$keys;
stream.on('data', U.streamer(NEWLINEBUF, function(item, index) {
var end = item.indexOf('|', item.indexOf('|') + 2);
var meta = item.substring(0, end);
var arr = meta.split('|');
var dv = arr[0].trim().replace(' ', 'T') + ':00.000Z';
tmp.line = item.substring(end + 1).trim();
if (isTable)
tmp.line = tmp.line.split('|');
var obj = { id: index + 1, date: dv.parseDate(), user: arr[1].trim(), data: self instanceof Table ? self.parseData(tmp) : tmp.line.parseJSON(true) };
if (!filter || filter(obj))
output.push(obj);
}), stream);
CLEANUP(stream, () => callback(null, output));
return self;
};
function next_operation(self, type) {
self.next(type);
}
DP.ready = function(fn) {
var self = this;
fn.call(self);
return self;
};
DP.insert = function(doc, unique) {
var self = this;
var builder;
self.readonly && self.throwReadonly();
if (unique) {
builder = self.one();
var callback;
builder.callback(function(err, d) {
if (d)
callback && callback(null, 0);
else
self.insert(doc).callback(callback);
});
builder.callback = function(fn) {
callback = fn;
return builder;
};
return builder;
}
builder = new DatabaseBuilder2(self);
var json = doc.$$schema ? doc.$clean() : doc;
self.pending_append.push({ doc: JSON.stringify(json).replace(REGBOOL, JSONBOOL), raw: doc, builder: builder });
setImmediate(next_operation, self, 1);
self.$events.insert && self.emit('insert', json);
return builder;
};
DP.upsert = function(doc) {
return this.insert(doc, true);
};
DP.update = function(doc, insert) {
var self = this;
self.readonly && self.throwReadonly();
var builder = new DatabaseBuilder(self);
var data = doc.$$schema ? doc.$clean() : doc;
builder.$options.readertype = 1;
if (typeof(data) === 'string')
data = new Function('doc', 'repository', 'arg', data.indexOf('return ') === -1 ? ('return (' + data + ')') : data);
self.pending_update.push({ builder: builder, doc: data, insert: insert === true ? data : insert });
setImmediate(next_operation, self, 2);
return builder;
};
DP.modify = function(doc, insert) {
var self = this;
self.readonly && self.throwReadonly();
var builder = new DatabaseBuilder(self);
var data = doc.$$schema ? doc.$clean() : doc;
var keys = Object.keys(data);
var inc = null;
builder.$options.readertype = 1;
if (keys.length) {
var tmp;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
switch (key[0]) {
case '!':
case '+':
case '-':
case '*':
case '/':
!inc && (inc = {});
tmp = key.substring(1);
inc[tmp] = key[0];
doc[tmp] = doc[key];
doc[key] = undefined;
keys[i] = tmp;
break;
case '$':
tmp = key.substring(1);
doc[tmp] = new Function('val', 'doc', 'repository', 'arg', doc[key].indexOf('return ') === -1 ? ('return (' + doc[key] + ')') : doc[key]);
doc[key] = undefined;
keys[i] = tmp;
break;
}
}
self.pending_update.push({ builder: builder, doc: data, keys: keys, inc: inc, insert: insert === true ? data : insert });
setImmediate(next_operation, self, 2);
}
return builder;
};
DP.restore = function(filename, callback) {
var self = this;
self.readonly && self.throwReadonly();
U.wait(() => !self.type, function(err) {
if (err)
throw new Error('Database can\'t be restored because it\'s busy.');
self.type = 9;
F.restore(filename, F.path.root(), function(err, response) {
self.type = 0;
if (!err) {
self.$meta();
self.binary.$refresh();
self.refresh();
self.storage && self.storage.refresh();
}
self.$events.change && self.emit('change', 'restore');
callback && callback(err, response);
});
});
return self;
};
DP.backup = function(filename, callback) {
var self = this;
self.readonly && self.throwReadonly();
var list = [];
var pending = [];
pending.push(function(next) {
F.path.exists(self.filename, function(e) {
e && list.push(Path.join(CONF.directory_databases, self.name + EXTENSION));
next();
});
});
pending.push(function(next) {
F.path.exists(F.path.databases(self.name + EXTENSION_META), function(e) {
e && list.push(Path.join(CONF.directory_databases, self.name + EXTENSION_META));
next();
});
});
pending.push(function(next) {
F.path.exists(self.filenameBackup, function(e) {
e && list.push(Path.join(CONF.directory_databases, self.name + EXTENSION_BACKUP));
next();
});
});
pending.push(function(next) {
F.path.exists(self.filenameCounter, function(e) {
e && list.push(Path.join(CONF.directory_databases, self.name + EXTENSION + EXTENSION_COUNTER));
next();
});
});
pending.push(function(next) {
F.path.exists(self.filenameLog, function(e) {
e && list.push(Path.join(CONF.directory_databases, self.name + EXTENSION_LOG));
next();
});
});
pending.push(function(next) {
F.path.exists(F.path.databases(self.name + '-binary'), function(e, size, file) {
e && !file && list.push(Path.join(CONF.directory_databases, self.name + '-binary'));
next();
});
});
pending.push(function(next) {
F.path.exists(F.path.databases(self.name + '-storage'), function(e, size, file) {
e && !file && list.push(Path.join(CONF.directory_databases, self.name + '-storage'));
next();
});
});
pending.push(function(next) {
var filename = Path.join(CONF.directory_databases, self.name + EXTENSION_MAPREDUCE);
F.path.exists(F.path.root(filename), function(e) {
e && list.push(filename);
next();
});
});
pending.async(function() {
if (list.length)
F.backup(filename, list, callback);
else
callback(new Error('No files for backuping.'));
});
return self;
};
DP.backup2 = function(filename, remove) {
if (typeof(filename) === 'boolean') {
remove = filename;
filename = '';
}
var self = this;
self.readonly && self.throwReadonly();
if (remove)
return self.remove(filename || '');
var builder = new DatabaseBuilder2(self);
var stream = Fs.createReadStream(self.filename);
stream.pipe(Fs.createWriteStream(filename || self.filenameBackup2));
stream.on('error', function(err) {
builder.$options.log && builder.log();
builder.$callback && builder.$callback(errorhandling(err, builder));
builder.$callback = null;
});
stream.on('end', function() {
builder.$options.log && builder.log();
builder.$callback && builder.$callback(errorhandling(null, builder, true), true);
builder.$callback = null;
});
return builder;
};
DP.drop = function() {
var self = this;
self.readonly && self.throwReadonly();
self.pending_drops = true;
setImmediate(next_operation, self, 7);
return self;
};
DP.free = function(force) {
var self = this;
if (!force && !self.$free)
return self;
self.counter.removeAllListeners(true);
self.binary.removeAllListeners(true);
self.removeAllListeners(true);
self.binary = null;
self.counter = null;
delete F.databases[self.name];
return self;
};
DP.release = function() {
var self = this;
self.inmemory = {};
self.inmemorylastusage = undefined;
return self;
};
TP.clear = DP.clear = function(callback) {
var self = this;
self.readonly && self.throwReadonly();
self.pending_clear.push(callback || NOOP);
setImmediate(next_operation, self, 12);
return self;
};
TP.clean = DP.clean = function(callback) {
var self = this;
self.readonly && self.throwReadonly();
self.pending_clean.push(callback || NOOP);
setImmediate(next_operation, self, 13);
return self;
};
TP.lock = DP.lock = function(callback) {
var self = this;
self.readonly && self.throwReadonly();
self.pending_locks.push(callback || NOOP);
setImmediate(next_operation, self, 14);
return self;
};
DP.remove = function() {
var self = this;
self.readonly && self.throwReadonly();
var builder = new DatabaseBuilder(self);
self.pending_remove.push(builder);
builder.$options.readertype = 1;
setImmediate(next_operation, self, 3);
return builder;
};
DP.listing = DP.list = function(builder) {
var self = this;
if (builder)
builder.db = self;
else
builder = new DatabaseBuilder(self);
builder.$options.listing = true;
builder.$take = builder.$options.take = 100;
self.pending_reader.push(builder);
setImmediate(next_operation, self, 4);
return builder;
};
DP.find = function(builder) {
var self = this;
if (builder instanceof DatabaseBuilder)
builder.db = self;
else
builder = new DatabaseBuilder(self);
self.pending_reader.push(builder);
setImmediate(next_operation, self, 4);
return builder;
};
DP.find2 = function(builder) {
var self = this;
if (builder instanceof DatabaseBuilder)
builder.db = self;
else {
builder = new DatabaseBuilder(self);
builder.$options.notall = true;
}
if (self.readonly)
return self.find(builder);
self.pending_reader2.push(builder);
setImmediate(next_operation, self, 11);
return builder;
};
DP.stream = function(fn, repository, callback) {
var self = this;
if (typeof(repository) === 'function') {
callback = repository;
repository = null;
}
self.pending_streamer.push({ fn: fn, callback: callback, repository: repository || {} });
setImmediate(next_operation, self, 10);
return self;
};
DP.throwReadonly = function(e) {
throw new Error('Database "{0}" is readonly.'.format(this.name) + (e ? '\n' + e.toString() : ''));
};
DP.scalar = function(type, field) {
return this.find().scalar(type, field);
};
DP.count = function() {
var self = this;
var builder = new DatabaseBuilder(self);
builder.$options.readertype = 1;
self.pending_reader.push(builder);
setImmediate(next_operation, self, 4);
return builder;
};
DP.one = DP.read = function() {
var self = this;
var builder = new DatabaseBuilder(self);
builder.first();
self.pending_reader.push(builder);
setImmediate(next_operation, self, 4);
return builder;
};
DP.one2 = DP.read2 = function() {
var self = this;
var builder = new DatabaseBuilder(self);
builder.first();
self.pending_reader2.push(builder);
setImmediate(next_operation, self, 11);
return builder;
};
DP.top = function(max) {
var self = this;
var builder = new DatabaseBuilder(self);
builder.take(max);
self.pending_reader.push(builder);
setImmediate(next_operation, self, 4);
return builder;
};
// 1 append
// 2 update
// 3 remove
// 4 reader
// 5 views
// 6 reader views
// 7 drop
// 8 backup
// 9 restore
// 10 streamer
// 11 reader reverse
// 12 clear
// 13 clean
// 14 locks
const NEXTWAIT = { 7: true, 8: true, 9: true, 12: true, 13: true, 14: true };
DP.next = function(type) {
if (type && NEXTWAIT[this.step])
return;
if (F.isCluster && type === 0 && this.locked)
clusterunlock(this);
if (!this.$writting && !this.$reading) {
if (this.step !== 12 && this.pending_clear.length) {
if (!this.readonly && F.isCluster)
clusterlock(this, '$clear');
else if (INMEMORY[this.name])
this.$clear_inmemory();
else
this.$clear();
return;
}
if (this.step !== 13 && this.pending_clean.length) {
if (!this.readonly && F.isCluster)
clusterlock(this, '$clean');
else
this.$clean();
return;
}
if (this.step !== 7 && !this.pending_reindex && this.pending_drops) {
this.$drop();
return;
}
if (this.step !== 14 && this.pending_locks.length) {
this.$lock();
return;
}
}
if (!this.$writting) {
if (this.step !== 1 && !this.pending_reindex && this.pending_append.length) {
if (INMEMORY[this.name])
this.$append_inmemory();
else
this.$append();
return;
}
if (this.step !== 2 && !this.$writting && this.pending_update.length) {
if (!this.readonly && F.isCluster)
clusterlock(this, '$update');
else if (INMEMORY[this.name])
this.$update_inmemory();
else
this.$update();
return;
}
if (this.step !== 3 && !this.$writting && this.pending_remove.length) {
if (!this.readonly && F.isCluster)
clusterlock(this, '$remove');
if (INMEMORY[this.name])
this.$remove_inmemory();
else
this.$remove();
return;
}
}
if (!this.$reading) {
if (this.step !== 4 && this.pending_reader.length) {
this.$reader();
return;
}
if (this.step !== 11 && this.pending_reader2.length) {
this.$reader3();
return;
}
if (this.step !== 10 && this.pending_streamer.length) {
this.$streamer();
return;
}
}
if (this.step !== type) {
this.step = 0;
setImmediate(next_operation, this, 0);
}
};
// ======================================================================
// FILE OPERATIONS
// ======================================================================
// InMemory saving
DP.$save = function() {
var self = this;
setTimeout2('nosql.' + self.name, function() {
var data = self.inmemory['#'] || EMPTYARRAY;
var builder = [];
for (var i = 0, length = data.length; i < length; i++)
builder.push(JSON.stringify(data[i]).replace(REGBOOL, JSONBOOL));
Fs.writeFile(self.filename, builder.join(NEWLINE) + NEWLINE, F.errorcallback);
}, 50, 100);
return self;
};
DP.$inmemory = function(callback) {
var self = this;
var view = '#';
self.readonly && self.throwReadonly();
// Last usage
self.inmemorylastusage = global.F ? global.NOW : undefined;
if (self.inmemory[view])
return callback();
var filename = self.filename;
if (view !== '#')
filename = filename.replace(/\.nosql/, '#' + view + '.nosql');
self.inmemory[view] = [];
Fs.readFile(filename, function(err, data) {
if (err)
return callback();
var arr = data.toString('utf8').split('\n');
for (var i = 0, length = arr.length; i < length; i++) {
var item = arr[i];
if (item) {
try {
item = JSON.parse(item.trim(), jsonparser);
item && self.inmemory[view].push(item);
} catch (e) {}
}
}
callback();
});
return self;
};
TP.$meta = DP.$meta = function(write) {
var self = this;
if (write) {
self.readonly && self.throwReadonly();
Fs.writeFile(self.filenameMeta, JSON.stringify(self.metadata), F.errorcallback);
return self;
}
if (self.readonly)
return self;
try {
self.metadata = JSON.parse(Fs.readFileSync(self.filenameMeta).toString('utf8'), jsonparser);
} catch (err) {}
return self;
};
DP.$append = function() {
var self = this;
self.step = 1;
if (!self.pending_append.length) {
self.next(0);
return;
}
self.$writting = true;
self.pending_append.splice(0).limit(JSONBUFFER, function(items, next) {
var json = '';
for (var i = 0, length = items.length; i < length; i++) {
json += items[i].doc + NEWLINE;
}
Fs.appendFile(self.filename, json, function(err) {
err && F.error(err, 'NoSQL insert: ' + self.name);
for (var i = 0, length = items.length; i < length; i++) {
items[i].builder.$options.log && items[i].builder.log();
var callback = items[i].builder.$callback;
callback && callback(err, 1);
}
next();
});
}, () => setImmediate(next_append, self));
};
function next_append(self) {
self.$writting = false;
self.next(0);
self.$events.change && self.emit('change', 'insert');
}
DP.$append_inmemory = function() {
var self = this;
self.step = 1;
if (!self.pending_append.length) {
self.next(0);
return self;
}
var items = self.pending_append.splice(0);
return self.$inmemory(function() {
for (var i = 0, length = items.length; i < length; i++) {
self.inmemory['#'].push(JSON.parse(items[i].doc, jsonparser));
items[i].builder.$options.log && items[i].builder.log();
var callback = items[i].builder.$callback;
callback && callback(null, 1);
}
self.$save();
setImmediate(next_append, self);
});
};
DP.$update = function() {
var self = this;
self.step = 2;
if (!self.pending_update.length) {
self.next(0);
return self;
}
self.$writting = true;
var filter = self.pending_update.splice(0);
var filters = new NoSQLReader();
var fs = new NoSQLStream(self.filename);
var change = false;
for (var i = 0; i < filter.length; i++)
filters.add(filter[i].builder, true);
if (self.buffersize)
fs.buffersize = self.buffersize;
if (self.buffercount)
fs.buffercount = self.buffercount;
var update = function(docs, doc, dindex, f, findex) {
var rec = fs.docsbuffer[dindex];
var fil = filter[findex];
var e = fil.keys ? 'modify' : 'update';
var old = self.$events[e] ? CLONE(doc) : 0;
if (f.first)
f.canceled = true;
if (fil.keys) {
for (var j = 0; j < fil.keys.length; j++) {
var key = fil.keys[j];
var val = fil.doc[key];
if (val !== undefined) {
if (typeof(val) === 'function')
doc[key] = val(doc[key], doc, f.filter.repository, f.filter.arg);
else if (fil.inc && fil.inc[key]) {
switch (fil.inc[key]) {
case '!':
doc[key] = !doc[key];
break;
case '+':
doc[key] = (doc[key] || 0) + val;
break;
case '-':
doc[key] = (doc[key] || 0) - val;
break;
case '*':
doc[key] = (doc[key] || 0) + val;
break;
case '/':
doc[key] = (doc[key] || 0) / val;
break;
}
} else
doc[key] = val;
}
}
} else
docs[dindex] = typeof(fil.doc) === 'function' ? (fil.doc(doc, f.filter.repository, f.filter.arg) || doc) : fil.doc;
self.$events[e] && self.emit(e, doc, old);
f.builder.$options.backup && f.builder.$backupdoc(rec.doc);
};
var updateflush = function(docs, doc, dindex) {
doc = docs[dindex];
var rec = fs.docsbuffer[dindex];
var upd = JSON.stringify(doc).replace(REGBOOL, JSONBOOL);
if (upd === rec.doc)
return;
!change && (change = true);
var was = true;
if (rec.doc.length === upd.length) {
var b = Buffer.byteLength(upd);
if (rec.length === b) {
fs.write(upd + NEWLINE, rec.position);
was = false;
}
}
if (was) {
var tmp = fs.remchar + rec.doc.substring(1) + NEWLINE;
fs.write(tmp, rec.position);
fs.write2(upd + NEWLINE);
}
};
fs.ondocuments = function() {
filters.compare2(JSON.parse('[' + fs.docs + ']', jsonparser), update, updateflush);
};
fs.$callback = function() {
fs = null;
self.$writting = false;
self.next(0);
for (var i = 0; i < filters.builders.length; i++) {
var item = filters.builders[i];
var fil = filter[i];
if (fil.insert && !item.counter) {
item.builder.$insertcallback && item.builder.$insertcallback(fil.insert, item.filter ? item.filter.repository : EMPTYOBJECT);
var tmp = self.insert(fil.insert);
tmp.$callback = item.builder.$callback;
tmp.$options.log = item.builder.$options.log;
item.builder.$callback = null;
} else {
item.builder.$options.log && item.builder.log();
item.builder.$callback && item.builder.$callback(errorhandling(null, item.builder, item.counter), item.counter, item.count, item.filter ? item.filter.repository : EMPTYOBJECT);
}
}
if (change) {
self.$events.change && self.emit('change', 'update');
!F.databasescleaner[self.name] && (F.databasescleaner[self.name] = 1);
}
};
fs.openupdate();
return self;
};
DP.$update_inmemory = function() {
var self = this;
self.step = 2;
if (!self.pending_update.length) {
self.next(0);
return self;
}
var filter = self.pending_update.splice(0);
var change = false;
var filters = new NoSQLReader();
for (var i = 0; i < filter.length; i++)
filters.add(filter[i].builder, true);
return self.$inmemory(function() {
var old;
var update = function(docs, doc, dindex, f, findex) {
var fil = filter[findex];
var e = fil.keys ? 'modify' : 'update';
if (!old)
old = self.$events[e] ? CLONE(doc) : 0;
if (f.first)
f.canceled = true;
if (fil.keys) {
for (var j = 0; j < fil.keys.length; j++) {
var key = fil.keys[j];
var val = fil.doc[key];
if (val !== undefined) {
if (typeof(val) === 'function')
doc[key] = val(doc[key], doc);
else if (fil.inc && fil.inc[key]) {
switch (fil.inc[key]) {
case '!':
doc[key] = !doc[key];
break;
case '+':
doc[key] = (doc[key] || 0) + val;
break;
case '-':
doc[key] = (doc[key] || 0) - val;
break;
case '*':
doc[key] = (doc[key] || 0) + val;
break;
case '/':
doc[key] = (doc[key] || 0) / val;
break;
}
} else
doc[key] = val;
}
}
} else
docs[dindex] = typeof(fil.doc) === 'function' ? fil.doc(doc, f.filter.repository) : fil.doc;
self.$events[e] && self.emit(e, doc, old);
f.builder.$options.backup && f.builder.$backupdoc(old);
return 1;
};
var updateflush = function(docs, doc) {
!change && (change = true);
self.$events.update && self.emit('update', doc, old);
old = null;
};
filters.compare2(self.inmemory['#'], update, updateflush);
change && self.$save();
for (var i = 0; i < filters.builders.length; i++) {
var item = filters.builders[i];
if (item.insert && !item.counter) {
item.builder.$insertcallback && item.builder.$insertcallback(item.insert, item.filter.repository || EMPTYOBJECT);
var tmp = self.insert(item.insert);
tmp.$callback = item.builder.$callback;
tmp.$options.log = item.builder.$options.log;
item.builder.$callback = null;
} else {
item.builder.$options.log && item.builder.log();
item.builder.$callback && item.builder.$callback(errorhandling(null, item.builder, item.counter), item.counter, item.count, item.filter.repository);
}
}
setImmediate(function() {
self.next(0);
change && self.$events.change && self.emit('change', 'update');
});
});
};
DP.$reader = function() {
var self = this;
self.step = 4;
if (!self.pending_reader.length) {
self.next(0);
return self;
}
var list = self.pending_reader.splice(0);
if (INMEMORY[self.name]) {
self.$reader2_inmemory(list, () => self.next(0));
} else {
self.$reading = true;
self.$reader2(self.filename, list, function() {
self.$reading = false;
self.next(0);
});
}
return self;
};
function listing(builder, item) {
var skip = builder.$options.skip || 0;
var take = builder.$options.take || 0;
return { page: ((skip / take) + 1), pages: item.count ? Math.ceil(item.count / take) : 0, limit: take, count: item.count, items: item.response || [] };
}
DP.$reader2 = function(filename, items, callback, reader) {
var self = this;
if (self.readonly) {
if (reader === undefined) {
U.download(filename, FLAGS_READ, function(err, response) {
err && F.error(err, 'NoSQL database download: ' + self.name);
self.$reader2(filename, items, callback, err ? null : response);
});
return self;
}
}
var fs = new NoSQLStream(self.filename);
var filters = new NoSQLReader(items);
if (self.buffersize)
fs.buffersize = self.buffersize;
if (self.buffercount)
fs.buffercount = self.buffercount;
fs.ondocuments = function() {
return filters.compare(JSON.parse('[' + fs.docs + ']', jsonparser));
};
fs.$callback = function() {
filters.done();
fs = null;
callback();
};
if (reader)
fs.openstream(reader);
else
fs.openread();
return self;
};
DP.$reader3 = function() {
var self = this;
self.step = 11;
if (!self.pending_reader2.length) {
self.next(0);
return self;
}
self.$reading = true;
var fs = new NoSQLStream(self.filename);
var filters = new NoSQLReader(self.pending_reader2.splice(0));
if (self.buffersize)
fs.buffersize = self.buffersize;
if (self.buffercount)
fs.buffercount = self.buffercount;
fs.ondocuments = function() {
return filters.compare(JSON.parse('[' + fs.docs + ']', jsonparser));
};
fs.$callback = function() {
filters.done();
self.$reading = false;
fs = null;
self.next(0);
};
fs.openreadreverse();
return self;
};
DP.$streamer = function() {
var self = this;
self.step = 10;
if (!self.pending_streamer.length) {
self.next(0);
return self;
}
self.$reading = true;
var filter = self.pending_streamer.splice(0);
var length = filter.length;
var count = 0;
var fs = new NoSQLStream(self.filename);
if (self.buffersize)
fs.buffersize = self.buffersize;
if (self.buffercount)
fs.buffercount = self.buffercount;
fs.ondocuments = function() {
var docs = JSON.parse('[' + fs.docs + ']', jsonparser);
for (var j = 0; j < docs.length; j++) {
var json = docs[j];
count++;
for (var i = 0; i < length; i++)
filter[i].fn(json, filter[i].repository, count);
}
};
fs.$callback = function() {
for (var i = 0; i < length; i++)
filter[i].callback && filter[i].callback(null, filter[i].repository, count);
self.$reading = false;
self.next(0);
fs = null;
};
fs.openread();
return self;
};
function nosqlinlinesorter(item, builder, doc) {
if (!item.response) {
item.response = [doc];
return;
}
var length = item.response.length;
if (length < builder.$limit) {
item.response.push(doc);
length + 1 >= builder.$limit && item.response.quicksort(builder.$options.sort.name, builder.$options.sort.asc);
} else
nosqlresort(item.response, builder, doc);
}
function nosqlsortvalue(a, b, sorter) {
var type = typeof(a);
if (type === 'number')
return sorter.asc ? a > b : a < b;
else if (type === 'string') {
var c = COMPARER(a, b);
return sorter.asc ? c === 1 : c === -1;
} else if (type === 'boolean')
// return sorter.asc ? a > b : a < b;
return sorter.asc ? (a && !b) : (!a && b);
else if (a instanceof Date)
return sorter.asc ? a > b : a < b;
return false;
}
function nosqlresort(arr, builder, doc) {
var b = doc[builder.$options.sort.name];
var beg = 0;
var length = arr.length;
var tmp = length - 1;
var sort = nosqlsortvalue(arr[tmp][builder.$options.sort.name], b, builder.$options.sort);
if (!sort)
return;
tmp = arr.length / 2 >> 0;
sort = nosqlsortvalue(arr[tmp][builder.$options.sort.name], b, builder.$options.sort);
if (!sort)
beg = tmp + 1;
for (var i = beg; i < length; i++) {
var item = arr[i];
var sort = nosqlsortvalue(item[builder.$options.sort.name], b, builder.$options.sort);
if (!sort)
continue;
for (var j = length - 1; j > i; j--)
arr[j] = arr[j - 1];
arr[i] = doc;
return;
}
}
DP.$reader2_inmemory = function(items, callback) {
var self = this;
return self.$inmemory(function() {
var filters = new NoSQLReader(items);
filters.clone = true;
filters.compare(self.inmemory['#']);
filters.done();
callback();
});
};
DP.$remove = function() {
var self = this;
self.step = 3;
if (!self.pending_remove.length) {
self.next(0);
return;
}
self.$writting = true;
var fs = new NoSQLStream(self.filename);
var filter = self.pending_remove.splice(0);
var filters = new NoSQLReader(filter);
var change = false;
if (self.buffersize)
fs.buffersize = self.buffersize;
if (self.buffercount)
fs.buffercount = self.buffercount;
var remove = function(docs, d, dindex, f) {
var rec = fs.docsbuffer[dindex];
f.builder.$options.backup && f.builder.$backupdoc(rec.doc);
return 1;
};
var removeflush = function(docs, d, dindex) {
var rec = fs.docsbuffer[dindex];
!change && (change = true);
self.$events.remove && self.emit('remove', d);
fs.write(fs.remchar + rec.doc.substring(1) + NEWLINE, rec.position);
};
fs.ondocuments = function() {
filters.compare2(JSON.parse('[' + fs.docs + ']', jsonparser), remove, removeflush);
};
fs.$callback = function() {
filters.done();
fs = null;
self.$writting = false;
self.next(0);
if (change) {
self.$events.change && self.emit('change', 'remove');
!F.databasescleaner[self.name] && (F.databasescleaner[self.name] = 1);
}
};
fs.openupdate();
};
DP.$clear = function() {
var self = this;
self.step = 12;
if (!self.pending_clear.length) {
self.next(0);
return;
}
var filter = self.pending_clear.splice(0);
Fs.unlink(self.filename, function() {
for (var i = 0; i < filter.length; i++)
filter[i]();
self.$events.change && self.emit('change', 'clear');
self.next(0);
});
};
DP.$clean = function() {
var self = this;
self.step = 13;
if (!self.pending_clean.length) {
self.next(0);
return;
}
var filter = self.pending_clean.splice(0);
var length = filter.length;
var now = Date.now();
F.databasescleaner[self.name] = undefined;
CONF.nosql_logger && PRINTLN('NoSQL embedded "{0}" cleaning (beg)'.format(self.name));
var fs = new NoSQLStream(self.filename);