UNPKG

orchidb

Version:

A lightweight, JSON-Document-Oriented database managment module for node.js

464 lines (444 loc) 18.1 kB
/* * MIT License * * Copyright (c) 2023 Leon Cotten * * 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. */ const fs = require('fs'); const path = require('path'); const prompt = require('prompt-sync')(); class Collection{ constructor(dbPath, name, logToConsole){ this.dbPath = dbPath; this.name = name; this.colPath = path.join(dbPath, this.name); if (!fs.existsSync(this.colPath)) { fs.mkdirSync(this.colPath); fs.mkdirSync(path.join(this.colPath, 'TRASH')); if(logToConsole == true){ console.log(`Collection ${name} created`) } }else{ if(logToConsole == true){ console.log(`Collection ${name} already exists`); } } this.terminal = { promptMessage: `${path.basename(dbPath)}@${this.name}:~$`, run: () => { console.log(`\n|--------------------${this.name} Terminal--------------------|`); while (true) { let command = prompt(this.terminal.promptMessage).toLowerCase().trim(); switch (command){ case 'exit': console.log('Exiting Terminal'); console.log('|-------------------------------------------------------------| \n'); return; case 'insert': console.log('Enter Doc Name:'); const newDocName = prompt(this.terminal.promptMessage); console.log('Enter Content:'); const newContentStr = prompt(this.terminal.promptMessage); const newContent = JSON.parse(newContentStr); this.insert(newDocName, newContent, true); break; case 'get': console.log('Enter Doc Name:'); const readDocName = prompt(this.terminal.promptMessage); this.get(readDocName, true); break; case 'update': console.log('Enter Doc Name:'); const updatedDocName = prompt(this.terminal.promptMessage); console.log('Enter Content:'); const updatedContentStr = prompt(this.terminal.promptMessage); const updatedContent = JSON.parse(updatedContentStr); this.update(updatedDocName, updatedContent, true); break; case 'del': console.log('Enter Doc Name:'); const deleteDocName = prompt(this.terminal.promptMessage); this.delete(deleteDocName, true); break; case 'restore': console.log('Enter Doc Name:'); const restoreDocName = prompt(this.terminal.promptMessage); this.restore(restoreDocName, true); break; case 'empty_trash': this.emptyTrash(true); break; case 'rename': console.log('Enter Doc Name:'); const renameDocName = prompt(this.terminal.promptMessage); console.log('Enter New Doc Name:'); const newName = prompt(this.terminal.promptMessage); this.rename(renameDocName, newName, true); break; case 'get_path': console.log('Enter Doc Name:'); const returnPathName = prompt(this.terminal.promptMessage); this.getPath(returnPathName, true); break; case 'copy_col': console.log('Enter Target Collection:'); const copyColName = prompt(this.terminal.promptMessage); this.copyCol(copyColName, true); break; case 'copy_doc_to_col': console.log('Enter Doc Name:'); const copyDocName = prompt(this.terminal.promptMessage); console.log('Enter Target Collection:'); const copy_ColName = prompt(this.terminal.promptMessage); this.copyDocToCol(copyDocName, copy_ColName, true); break; case 'del_col': this.deleteCol(true); break; case 'clear': console.clear(); console.log(`|--------------------${this.name} Terminal--------------------|`); break; case 'del_var': console.log('Enter Doc Name:'); const delDocName = prompt(this.terminal.promptMessage); console.log('Enter Variable Name:'); const delVarName = prompt(this.terminal.promptMessage); this.deleteVar(delDocName, delVarName); break; case 'find': console.log('[Optional] Enter Properties: '); const properties = JSON.parse(prompt(this.terminal.promptMessage)); this.find(properties); break; case 'get_var': console.log('Enter Doc Name:'); const readDocname = prompt(this.terminal.promptMessage); console.log('Enter List of Variables:'); const readVarList = prompt(this.terminal.promptMessage).split(', '); this.getVar(readDocname, readVarList, true); break; case 'overwrite': console.log('Enter Doc Name:'); const overwriteDocName = prompt(this.terminal.promptMessage); console.log('Enter Content:'); const overwriteContentStr = prompt(this.terminal.promptMessage); const overwriteContent = JSON.parse(overwriteContentStr); this.overwrite(overwriteDocName, overwriteContent, true); break; case 'help': console.log('|------------------------------------------COMMANDS-----------------------------------------|') console.log("exit: Exit the terminal."); console.log(''); console.log("insert: Create a new document in the collection with specified name and content."); console.log(''); console.log("get: Read the content of a document with specified name."); console.log(''); console.log("update: Update the content of a document with specified name."); console.log(''); console.log("del: Delete a document with specified name and move it to the TRASH folder."); console.log(''); console.log("restore: Restore a document from the TRASH folder to its original location."); console.log(''); console.log("empty_trash: Empty the TRASH folder and permanently delete all its contents."); console.log(''); console.log("rename: Rename a document by providing its current name and the new name."); console.log(''); console.log("get_path: Return the file path of a document with specified name."); console.log(''); console.log("copy_col: Create a copy of the entire collection with a new name."); console.log(''); console.log("copy_doc_to_col: Copy a document from the current collection to another collection."); console.log(''); console.log("del_col: Delete the entire collection, including all its documents and the TRASH folder."); console.log(''); console.log("clear: Clear the console screen."); console.log(''); console.log("del_var: Delete a variable from a document."); console.log(''); console.log("find: Finds every document in a collection with specified input properties."); console.log(''); console.log("get_var: Return the value of a variable in a document."); console.log(''); console.log("help: Display information about the available commands."); console.log(''); console.log('Capitalization does not matter when using terminal commands, but spelling does.'); console.log('|--------------------------------------------------------------------------------------|'); console.log(''); break; default: console.log('Invalid Command'); break; } } } }; }; insert(docName, data, logToConsole){ const filePath = path.join(this.colPath, docName + '.json'); if (fs.existsSync(filePath)) { if(logToConsole === true){ return console.log(`Document ${filePath} exists.`); } } fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); if(logToConsole === true){ console.log(`Document ${docName} created.`); } }; getPath(docName, logToConsole){ const log = logToConsole; if(log){ console.log(`Path to ${docName}: ${path.join(this.colPath, docName + '.json')}`); } return path.join(this.colPath, docName + '.json'); }; delete(docName, logToConsole){ const filePath = path.join(this.colPath, docName + '.json'); const targetPath = path.join(this.colPath, 'TRASH', docName + '.json'); if (fs.existsSync(targetPath) && fs.lstatSync(targetPath).isDirectory()) { fs.rmdirSync(targetPath, { recursive: true }); } fs.renameSync(filePath, targetPath); if(logToConsole === true){ console.log(`Document ${docName} moved to TRASH folder.`); } }; restore(docName, logToConsole){ const filePath = path.join(this.colPath, 'TRASH', docName + '.json'); if (!fs.existsSync(filePath)) { if(logToConsole === true){ console.log('Document does not exist in trash.'); } return; } const newFilePath = path.join(this.colPath, docName + '.json'); fs.copyFileSync(filePath, newFilePath); fs.unlinkSync(filePath); if(logToConsole === true){ console.log(`Document ${docName} restored.`); } }; emptyTrash(logToConsole){ const trashPath = path.join(this.colPath, 'TRASH'); fs.readdirSync(trashPath).forEach((file) => { const filePath = path.join(trashPath, file); fs.unlinkSync(filePath); }); if(logToConsole === true){ console.log('Trash emptied.'); } }; rename(docName, newDocName, logToConsole){ const filePath = path.join(this.colPath, docName + '.json'); const newFilePath = path.join(this.colPath, newDocName + '.json'); if (fs.existsSync(newFilePath)) { if(logToConsole === true){ console.log(`Document ${newDocName} already exists.`); } return; } fs.renameSync(filePath, newFilePath); if(logToConsole === true){ console.log(`Document ${docName} changed to ${newDocName}.`); } }; get(docName, logToConsole){ const logToConsoleFlag = logToConsole; const filePath = path.join(this.colPath, docName + '.json'); const data = fs.readFileSync(filePath, 'utf8'); if(logToConsoleFlag == true){ console.log(`${docName}:\n${data}`); } return JSON.parse(data); }; update(docName, variables, logToConsole){ const filePath = path.join(this.colPath, docName + '.json'); if (fs.existsSync(filePath)) { const data = fs.readFileSync(filePath, 'utf8'); const jsonData = JSON.parse(data); const keys = Object.keys(variables); const values = Object.values(variables); for(let i = 0; i < keys.length; i++){ jsonData[keys[i]] = values[i]; } fs.writeFileSync(filePath, JSON.stringify(jsonData, null, 2)); if(logToConsole === true){ console.log(`File '${docName}.json' updated.`); } } else { if(logToConsole === true){ console.log(`File '${docName}.json' does not exist.`); } } }; overwrite(docName, data, logToConsole){ const filePath = path.join(this.colPath, docName + '.json'); fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); if(logToConsole === true){ console.log(`File '${docName}.json' overwritten.`); } }; copyCol(targetCollection, logToConsole) { function copyDir(source, target){ if (fs.lstatSync(source).isDirectory()) { fs.readdirSync(source).forEach((file) => { const curSource = path.join(source, file); const curTarget = path.join(target, file); this.copyDir(curSource, curTarget); }); } else { fs.copyFileSync(source, target); } } const targetColPath = path.join(this.dbPath, targetCollection); if (!fs.existsSync(targetColPath)) { fs.mkdirSync(targetColPath); } const targetTrashPath = path.join(targetColPath, 'TRASH'); if (!fs.existsSync(targetTrashPath)) { fs.mkdirSync(targetTrashPath); } const fileList = fs.readdirSync(this.colPath); fileList.forEach((file) => { const filePath = path.join(this.colPath, file); const targetPath = path.join(targetColPath, file); const isDirectory = fs.lstatSync(filePath).isDirectory(); if (isDirectory) { fs.mkdirSync(targetPath, { recursive: true }); copyDir(filePath, targetPath); } else { fs.copyFileSync(filePath, targetPath); } }); if(logToConsole === true){ console.log(`All files copied to collection '${targetCollection}' and overwritten if exists.`); } } copyDocToCol(docName, targetCollection, logToConsole){ const filePath = path.join(this.colPath, `${docName}.json`); const targetColPath = path.join(this.dbPath, targetCollection); if (!fs.existsSync(targetColPath)) { fs.mkdirSync(targetColPath); } const targetPath = path.join(targetColPath, `${docName}.json`); fs.copyFileSync(filePath, targetPath); if(logToConsole === true){ console.log(`Document '${docName}.json' copied to collection '${targetCollection}'.`); } }; deleteCol(logToConsole){ const deleteFolderRecursive = (directory) => { if (fs.existsSync(directory)) { fs.readdirSync(directory).forEach((file, index) => { const curPath = path.join(directory, file); if (fs.lstatSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); } else { // delete file fs.unlinkSync(curPath); } }); fs.rmdirSync(directory); } }; try { const colPath = path.join(this.dbPath, this.name); deleteFolderRecursive(colPath); if(logToConsole === true){ console.log(`Collection ${this.name} deleted.`); } } catch (err) { console.error(`Error while deleting collection ${this.name}: ${err}`); } }; find(inputProperties, logToConsole){ const properties = inputProperties || {}; let outputArr = []; const fileList = fs.readdirSync(this.colPath).filter(file => !fs.lstatSync(path.join(this.colPath, file)).isDirectory()); if (Object.keys(properties).length === 0) { fileList.forEach((file) => { const data = fs.readFileSync(file, 'utf-8'); outputArr.push(file); if(logToConsole === true){ console.log(`${file}: `); console.log(data); } }); } else { fileList.forEach((file) => { const data = fs.readFileSync(path.join(this.colPath, file), 'utf-8'); const filteredData = Object.fromEntries( Object.entries(data).filter(([key, value]) => properties[key] === value) ); if (Object.keys(filteredData).length > 0) { outputArr.push(file); if(logToConsole === true){ console.log(`${file}: `); console.log(JSON.stringify(filteredData, null, 2)); } } }); } return outputArr; }; getVar(docName, variables, logToConsole) { const docPath = path.join(this.colPath, docName + '.json'); const doc = JSON.parse(fs.readFileSync(docPath, 'utf-8')); const log = logToConsole; if (!doc) { throw new Error(`Document "${docName}" not found`); } const result = {}; variables.forEach(variable => { if (doc.hasOwnProperty(variable)) { result[variable] = doc[variable]; } }); if (log) { console.log(result); } return result; } deleteVar(docName, variables, logToConsole) { const filePath = path.join(this.colPath, docName + '.json'); const data = fs.readFileSync(filePath, 'utf8'); const jsonData = JSON.parse(data); variables.forEach((variable) => { delete jsonData[variable]; }); fs.writeFileSync(filePath, JSON.stringify(jsonData, null, 2)); if(logToConsole == true){ console.log(`Variables ${variables} removed from file '${docName}.json'.`); } }; has(docName, logToConsole){ const log = logToConsole; const filePath = path.join(this.colPath, docName + '.json'); if(fs.existsSync(filePath)){ if(log){ console.log(`Document ${docName} exists in collection ${this.name}`); } }else{ console.log(`Document ${docName} does not exist in collection ${this.name}`); } return fs.existsSync(filePath); }; } module.exports = { Collection };