|
|
|
@ -61,16 +61,29 @@ class Persistence { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @callback Persistence~persistCachedDatabaseCallback |
|
|
|
|
* @param {Error?} err |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Persist cached database |
|
|
|
|
* This serves as a compaction function since the cache always contains only the number of documents in the collection |
|
|
|
|
* while the data file is append-only so it may grow larger |
|
|
|
|
* @param {Function} callback Optional callback, signature: err |
|
|
|
|
* This is an internal function, use compactDataFile which uses the executor |
|
|
|
|
* @param {Persistence~persistCachedDatabaseCallback} callback Optional callback, signature: err |
|
|
|
|
*/ |
|
|
|
|
persistCachedDatabase (callback = () => {}) { |
|
|
|
|
return callbackify(this.persistCachedDatabaseAsync.bind(this))(callback) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Persist cached database |
|
|
|
|
* This serves as a compaction function since the cache always contains only the number of documents in the collection |
|
|
|
|
* while the data file is append-only so it may grow larger |
|
|
|
|
* This is an internal function, use compactDataFileAsync which uses the executor |
|
|
|
|
* @return {Promise<void>} |
|
|
|
|
*/ |
|
|
|
|
async persistCachedDatabaseAsync () { |
|
|
|
|
const lines = [] |
|
|
|
|
|
|
|
|
@ -95,13 +108,23 @@ class Persistence { |
|
|
|
|
this.db.emit('compaction.done') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @callback Persistence~compactDataFileCallback |
|
|
|
|
* @param {Error?} err |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Queue a rewrite of the datafile |
|
|
|
|
* @param {Persistence~compactDataFileCallback} [callback = () => {}] Optional callback, signature: err |
|
|
|
|
*/ |
|
|
|
|
compactDatafile () { |
|
|
|
|
this.db.executor.push({ this: this, fn: this.persistCachedDatabase, arguments: [] }) |
|
|
|
|
compactDatafile (callback = () => {}) { |
|
|
|
|
this.db.executor.push({ this: this, fn: this.persistCachedDatabase, arguments: [callback] }) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Queue a rewrite of the datafile |
|
|
|
|
* @async |
|
|
|
|
*/ |
|
|
|
|
compactDatafileAsync () { |
|
|
|
|
return this.db.executor.pushAsync(() => this.persistCachedDatabaseAsync()) |
|
|
|
|
} |
|
|
|
@ -128,16 +151,27 @@ class Persistence { |
|
|
|
|
if (this.autocompactionIntervalId) clearInterval(this.autocompactionIntervalId) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @callback Persistence~persistNewStateCallback |
|
|
|
|
* @param {Error?} err |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Persist new state for the given newDocs (can be insertion, update or removal) |
|
|
|
|
* Use an append-only format |
|
|
|
|
* @param {Array} newDocs Can be empty if no doc was updated/removed |
|
|
|
|
* @param {Function} callback Optional, signature: err |
|
|
|
|
* @param {string[]} newDocs Can be empty if no doc was updated/removed |
|
|
|
|
* @param {Persistence~persistNewStateCallback} [callback = () => {}] Optional, signature: err |
|
|
|
|
*/ |
|
|
|
|
persistNewState (newDocs, callback = () => {}) { |
|
|
|
|
callbackify(this.persistNewStateAsync.bind(this))(newDocs, err => callback(err)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Persist new state for the given newDocs (can be insertion, update or removal) |
|
|
|
|
* Use an append-only format |
|
|
|
|
* @param {string[]} newDocs Can be empty if no doc was updated/removed |
|
|
|
|
* @return {Promise} |
|
|
|
|
*/ |
|
|
|
|
async persistNewStateAsync (newDocs) { |
|
|
|
|
let toPersist = '' |
|
|
|
|
|
|
|
|
@ -154,8 +188,16 @@ class Persistence { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* From a database's raw data, return the corresponding |
|
|
|
|
* machine understandable collection |
|
|
|
|
* @typedef rawIndex |
|
|
|
|
* @property {string} fieldName |
|
|
|
|
* @property {boolean} [unique] |
|
|
|
|
* @property {boolean} [sparse] |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* From a database's raw data, return the corresponding machine understandable collection |
|
|
|
|
* @param {string} rawData database file |
|
|
|
|
* @return {{data: document[], indexes: Object.<string, rawIndex>}} |
|
|
|
|
*/ |
|
|
|
|
treatRawData (rawData) { |
|
|
|
|
const data = rawData.split('\n') |
|
|
|
@ -190,8 +232,15 @@ class Persistence { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* From a database's raw stream, return the corresponding |
|
|
|
|
* machine understandable collection |
|
|
|
|
* @callback Persistence~treatRawStreamCallback |
|
|
|
|
* @param {Error?} err |
|
|
|
|
* @param {{data: document[], indexes: Object.<string, rawIndex>}} data |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* From a database's raw data stream, return the corresponding machine understandable collection |
|
|
|
|
* @param {Readable} rawStream |
|
|
|
|
* @param {Persistence~treatRawStreamCallback} cb |
|
|
|
|
*/ |
|
|
|
|
treatRawStream (rawStream, cb) { |
|
|
|
|
const dataById = {} |
|
|
|
@ -236,10 +285,21 @@ class Persistence { |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async treatRawStreamAsync (rawStream) { |
|
|
|
|
/** |
|
|
|
|
* From a database's raw data stream, return the corresponding machine understandable collection |
|
|
|
|
* @param {Readable} rawStream |
|
|
|
|
* @return {Promise<{data: document[], indexes: Object.<string, rawIndex>}>} |
|
|
|
|
* @async |
|
|
|
|
*/ |
|
|
|
|
treatRawStreamAsync (rawStream) { |
|
|
|
|
return promisify(this.treatRawStream.bind(this))(rawStream) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @callback Persistence~loadDatabaseCallback |
|
|
|
|
* @param {Error?} err |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Load the database |
|
|
|
|
* 1) Create all indexes |
|
|
|
@ -248,12 +308,22 @@ class Persistence { |
|
|
|
|
* This means pulling data out of the data file or creating it if it doesn't exist |
|
|
|
|
* Also, all data is persisted right away, which has the effect of compacting the database file |
|
|
|
|
* This operation is very quick at startup for a big collection (60ms for ~10k docs) |
|
|
|
|
* @param {Function} callback Optional callback, signature: err |
|
|
|
|
* @param {Persistence~loadDatabaseCallback} callback Optional callback, signature: err |
|
|
|
|
*/ |
|
|
|
|
loadDatabase (callback = () => {}) { |
|
|
|
|
callbackify(this.loadDatabaseAsync.bind(this))(err => callback(err)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Load the database |
|
|
|
|
* 1) Create all indexes |
|
|
|
|
* 2) Insert all data |
|
|
|
|
* 3) Compact the database |
|
|
|
|
* This means pulling data out of the data file or creating it if it doesn't exist |
|
|
|
|
* Also, all data is persisted right away, which has the effect of compacting the database file |
|
|
|
|
* This operation is very quick at startup for a big collection (60ms for ~10k docs) |
|
|
|
|
* @return {Promise<void>} |
|
|
|
|
*/ |
|
|
|
|
async loadDatabaseAsync () { |
|
|
|
|
this.db.resetIndexes() |
|
|
|
|
|
|
|
|
@ -289,14 +359,25 @@ class Persistence { |
|
|
|
|
this.db.executor.processBuffer() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @callback Persistence~ensureDirectoryExistsCallback |
|
|
|
|
* @param {Error?} err |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Check if a directory stat and create it on the fly if it is not the case |
|
|
|
|
* cb is optional, signature: err |
|
|
|
|
* @param {string} dir |
|
|
|
|
* @param {Persistence~ensureDirectoryExistsCallback} [callback = () => {}] optional callback, signature: err |
|
|
|
|
*/ |
|
|
|
|
static ensureDirectoryExists (dir, callback = () => {}) { |
|
|
|
|
storage.mkdir(dir, { recursive: true }, err => { callback(err) }) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Check if a directory stat and create it on the fly if it is not the case |
|
|
|
|
* @param {string} dir |
|
|
|
|
* @return {Promise<void>} |
|
|
|
|
*/ |
|
|
|
|
static async ensureDirectoryExistsAsync (dir) { |
|
|
|
|
await storage.mkdirAsync(dir, { recursive: true }) |
|
|
|
|
} |
|
|
|
@ -304,6 +385,10 @@ class Persistence { |
|
|
|
|
/** |
|
|
|
|
* Return the path the datafile if the given filename is relative to the directory where Node Webkit stores |
|
|
|
|
* data for this application. Probably the best place to store data |
|
|
|
|
* @param {string} appName |
|
|
|
|
* @param {string} relativeFilename |
|
|
|
|
* @return {string} |
|
|
|
|
* @deprecated |
|
|
|
|
*/ |
|
|
|
|
static getNWAppFilename (appName, relativeFilename) { |
|
|
|
|
return deprecate(() => { |
|
|
|
|