diff --git a/index.js b/index.js index edaa2e3..df97ad8 100644 --- a/index.js +++ b/index.js @@ -11,14 +11,15 @@ let db = await new Database("testIndex", { let tableName = "documents"; -await db.table(tableName).ensureIndex("name"); await db.table(tableName).ensureIndex("test"); +await db.table(tableName).ensureIndex("name"); -let id = await db.table(tableName).save({ title: "Mr." }); -let id2 = await db.table(tableName).save({ title: "" }); - -await db.table(tableName).ensureIndex("title"); +let id = await db.table(tableName).save({ name: "Max Mustermann" }); +let id2 = await db.table(tableName).save({ test: false }); -let r = await db.table(tableName).find((row) => row.title == "Mr."); +let r = await db.table(tableName).filter((row) => { + return row.nested.nested.not.there; +}); console.log(r); +console.dir(r.getQuery(), { depth: null }); diff --git a/lib/CodeInterpreter.js b/lib/CodeInterpreter.js index a72be09..6b3cc68 100644 --- a/lib/CodeInterpreter.js +++ b/lib/CodeInterpreter.js @@ -11,6 +11,7 @@ export default class CodeInterpreter { #context; #db; #meta; + #rowName; constructor(code, context, db, meta, virtualDB) { this.#code = code; @@ -25,7 +26,7 @@ export default class CodeInterpreter { ecmaVersion: "latest", sourceType: "script", }); - return interpreteStart(this.#parsed, this.#context) + return this.interpreteStart(this.#parsed) } interprete() { @@ -76,22 +77,29 @@ export default class CodeInterpreter { let dbKey = (id) => tableKey(this.#meta.name, id); let vm = this.#vm let code = this.parse(); - let key; let value; - if(Array.isArray(code)) { if (code[0].$return) { - let values = Object.keys(code[0].$return); - key = values[0]; - value = code[0].$return[key]; + if(code[0].$return === Object(code[0].$return)) { + let values = Object.keys(code[0].$return); + key = values[0]; + value = code[0].$return[key]; + } else { + key = code[0].$return; + value = true; + } } - } else { + } else if(code === Object(code)) { key = Object.keys(code)[0]; value = code[key]; + } else { + key = code; + value = true; } let result = this.queryCode(key, value); + result.query.code = code; result.query.interpreterRows = 0; let recordset = { @@ -285,161 +293,225 @@ export default class CodeInterpreter { } throw new Error("IDX " + key + " not avaiable") } -} - -function interpreteStart(code, context) { - let expression = code.body[0].declarations[0].init; - if (["ArrowFunctionExpression", "FunctionExpression"].includes(expression.type)) { - let name = expression.params[0]?.name; - //console.log(expression.body) - let r = interpreteNode(expression.body, name, context); - return r - } - throw new ReferenceError("Not a function"); -} - -function interpreteNode(node, rowName, context, options = {}) { - switch (node.type) { - case "BlockStatement": - return BlockStatement(node, rowName, context, options); - case "ReturnStatement": - return ReturnStatement(node, rowName, context, options); - case "BinaryExpression": - return BinaryExpression(node, rowName, context, options); - case "MemberExpression": - return MemberExpression(node, rowName, context, options); - case "Identifier": - return Identifier(node, rowName, context, options); - case "Literal": - return Literal(node, rowName, context, options); - case "LogicalExpression": - return LogicalExpression(node, rowName, context, options); - case "ArrayExpression": - return ArrayExpression(node, rowName, context, options); - case "CallExpression": - return CallExpression(node, rowName, context, options); - default: - throw new Error("Unknown node type " + node.type); + + interpreteStart(code) { + let expression = code.body[0].declarations[0].init; + if (["ArrowFunctionExpression", "FunctionExpression"].includes(expression.type)) { + this.#rowName = expression.params[0]?.name; + + if(expression.expression === true) { + + let s = this.interpreteNode(expression.body) + return s + } + let r = this.interpreteNode(expression.body); + return r + } + throw new ReferenceError("Not a function"); } -} -function BlockStatement(node, rowName, context, options) { - let result = []; - for (let i = 0; i < node.body.length; i++) { - let body = node.body[i]; - result.push(interpreteNode(body, rowName, context, options)); + interpreteNode(node, options = {}) { + switch (node.type) { + case "BlockStatement": + return this.interpreteBlockStatement(node, options); + case "ReturnStatement": + return this.interpreteReturnStatement(node, options); + case "BinaryExpression": + return this.interpreteBinaryExpression(node, options); + case "MemberExpression": + return this.interpreteMemberExpression(node, options); + case "Identifier": + return this.interpreteIdentifier(node, options); + case "Literal": + return this.interpreteLiteral(node, options); + case "LogicalExpression": + return this.interpreteLogicalExpression(node, options); + case "ArrayExpression": + return this.interpreteArrayExpression(node, options); + case "CallExpression": + return this.interpreteCallExpression(node, options); + default: + throw new Error("Unknown node type " + node.type); + } } - return result; -} - -function ReturnStatement(node, rowName, context) { - let argument = interpreteNode(node.argument, rowName, context); - return { $return: argument }; -} - -function BinaryExpression(node, rowName, context) { - let left = interpreteNode(node.left, rowName, context); - let right = interpreteNode(node.right, rowName, context); - - if(node.left.type != "MemberExpression") { - let oldLeft = left; - left = right; - right = oldLeft + + interpreteBlockStatement(node, options) { + let result = []; + for (let i = 0; i < node.body.length; i++) { + let body = node.body[i]; + result.push(this.interpreteNode(body, options)); + } + return result; } - let result = {}; - switch(node.operator) { - case "==": - case "===": - result[left] = right; break; - case "!=": - case "!==": - result[left] = { $not: right }; break; - case ">=": - result[left] = { $gte: right }; break; - case "<=": - result[left] = { $lte: right }; break; - case ">": - result[left] = { $gt: right }; break; - case "<": - result[left] = { $lt: right }; break; + interpreteReturnStatement(node, options) { + let argument = this.interpreteNode(node.argument, options); + return { $return: argument }; } - return result -} - -function MemberExpression(node, rowName, context, { replace = true } = {}) { - let obj = interpreteNode(node.object, rowName, context, { - replace: false, - }); - let prop = interpreteNode(node.property, rowName, context, { - replace: false, - }); - - if (replace) { - if (obj.startsWith(rowName)) { - return `${obj}.${prop}`.substring(rowName.length + 1); + + interpreteBinaryExpression(node) { + let left = this.interpreteNode(node.left, { + withDbField: true + }); + let right = this.interpreteNode(node.right, { + withDbField: true + }); + + let operator = node.operator + + if(right.dbField && !left.dbField) { + let oldLeft = left; + left = right; + right = oldLeft + + switch(operator) { + case ">=": + operator = "<="; break; + case "<=": + operator = ">="; break; + case ">": + operator = "<"; break; + case "<": + operator = ">"; break; + } } - return getProp(context, `${obj}.${prop}`); + + let result = {}; + + switch(operator) { + case "==": + case "===": + result[left.value] = right.value; break; + case "!=": + case "!==": + result[left.value] = { $not: right.value }; break; + case ">=": + result[left.value] = { $gte: right.value }; break; + case "<=": + result[left.value] = { $lte: right.value }; break; + case ">": + result[left.value] = { $gt: right.value }; break; + case "<": + result[left.value] = { $lt: right.value }; break; + } + return result } - return `${obj}.${prop}`; -} + interpreteMemberExpression(node, { replace = true, withDbField = false } = {}) { + let obj = this.interpreteNode(node.object, { + replace: false, + }); + let prop = this.interpreteNode(node.property, { + replace: false, + }); -function Identifier(node, rowName, context, { replace = true } = {}) { - if (replace) { - if(context[node.name] == undefined) { - throw new ReferenceError(node.name + " is not defined"); + if (replace) { + if (obj.startsWith(this.#rowName)) { + let val = `${obj}.${prop}`.substring(this.#rowName.length + 1) + if(withDbField) { + return { + dbField: true, + value: val, + } + } + return val + + } + + let val = getProp(this.#context, `${obj}.${prop}`) + if(withDbField) { + return { + dbField: false, + value: val + } + } + + } + + return `${obj}.${prop}`; + } + + interpreteIdentifier(node, { replace = true, withDbField = false } = {}) { + let val = node.name; + if (replace) { + if(this.#context[node.name] == undefined) { + throw new ReferenceError(node.name + " is not defined"); + } + val = this.#context[node.name]; } - return context[node.name]; + if(withDbField) { + return { + dbField: false, + value: val, + } + } + return val + } - return node.name; -} -function Literal(node, rowName, context) { - return node.value; -} + interpreteLiteral(node, {withDbField = false}) { + let val = node.value + if(withDbField) { + return { + dbField: true, + value: val, + } + } + return val + } -function LogicalExpression(node, rowName, context) { - let left = interpreteNode(node.left, rowName, context); - let right = interpreteNode(node.right, rowName, context); + interpreteLogicalExpression(node) { + let left = this.interpreteNode(node.left, ); + let right = this.interpreteNode(node.right, ); - if(node.operator === "||") { + if(node.operator === "||") { + return { + $or: [left, right], + }; + } + //&& return { - $or: [left, right], - }; - } - //&& - return { - $and: [left, right], + $and: [left, right], + } } -} -function ArrayExpression(node, rowName, context, options) { - let ret = []; - for(let element of node.elements) { - ret.push(interpreteNode(element, rowName, context)) + interpreteArrayExpression(node) { + let ret = []; + for(let element of node.elements) { + ret.push(this.interpreteNode(element)) + } + return ret; } - return ret; -} - -function CallExpression(node, rowName, context, options) { - let calleeType = node.callee.object.type - let calleeObject = interpreteNode(node.callee.object, rowName, context); - let property = node.callee.property.name; - let arg = interpreteNode(node.arguments[0], rowName, context) - - if(calleeType == "ArrayExpression") { - if(property == "includes") { - let ret = [] - for(let element of calleeObject) { - let val = {} - val[arg] = element; - ret.push(val); + + interpreteCallExpression(node) { + let calleeType = node.callee.object.type + let calleeObject = this.interpreteNode(node.callee.object); + let property = node.callee.property.name; + let arg = this.interpreteNode(node.arguments[0]) + + + if(calleeType == "MemberExpression") { + if(property == "includes") { + let r = this.interpreteMemberExpression(node.callee.object, { withDbField: true}) + if(r.dbField) { + let val = {} + val[`${r.value}[]`] = arg + return val + } + } + } else if(calleeType == "ArrayExpression") { + if(property == "includes") { + let ret = [] + for(let element of calleeObject) { + let val = {} + val[arg] = element; + ret.push(val); + } + return { + $or: ret + }; } - return { - $or: ret - }; } + throw new TypeError(`${calleeObject}.${property} is not a function`) } - throw new TypeError(`${calleeObject}.${property} is not a function`) -} +} \ No newline at end of file diff --git a/lib/Database.js b/lib/Database.js index ab385fa..694f457 100644 --- a/lib/Database.js +++ b/lib/Database.js @@ -210,9 +210,9 @@ export default class Database { await this.#threadPool.stop(); await this._db.drop(); await this.close(); - await fs.rm(path.join(this.#path, name), { + /*await fs.rm(path.join(this.#path, name), { recursive: true, - }); + });*/ } async close() { diff --git a/lib/Storage.js b/lib/Storage.js index 28fdfd3..7e0a42b 100644 --- a/lib/Storage.js +++ b/lib/Storage.js @@ -36,6 +36,10 @@ export default class Storage { return this.#db.getKeys(options); } + getKeysIdx(options) { + return this.#idx.getKeys(options); + } + putSync(key, value) { return this.#db.putSync(key, value); } diff --git a/lib/Thread.js b/lib/Thread.js index d2e4dd6..20c64d1 100644 --- a/lib/Thread.js +++ b/lib/Thread.js @@ -128,11 +128,11 @@ const fns = { }, async removeIndex({ name, meta }) { await db.transaction(() => { - let table = db.getRangeIdx({ + let table = db.getKeysIdx({ start: indexKey(meta.name, name, dbValues.LO), end: indexKey(meta.name, name, dbValues.HI), }); - for (let { key } of table) { + for (let key of table) { db.removeIdx(key); } }); diff --git a/lib/VirtualMaschine.js b/lib/VirtualMaschine.js index e5c2cd2..d5666f7 100644 --- a/lib/VirtualMaschine.js +++ b/lib/VirtualMaschine.js @@ -11,7 +11,7 @@ export default class VirtualMaschine { this.#context = context; if (!scripts[this.#code]) { - (scripts[this.#code] = new VMScript("module.exports = (" + this.#code + ")")), scripts[this.#code].compile(); + (scripts[this.#code] = new VMScript(`module.exports = (${this.#code})`)), scripts[this.#code].compile(); } let vm = new NodeVM({ sandbox: this.#context, diff --git a/lib/utils.js b/lib/utils.js index 93badab..c36834c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -3,13 +3,17 @@ export function tableKey(table, id) { } const nullVal = Symbol.for("NULL"); +const undefinedVal = Symbol.for("UNDEFINED"); const idxVal = Symbol.for("IDX"); export function indexKey(table, indexName, value, ext) { - if (value === null || value === undefined) { - return [table, indexName, idxTypes.VAL_IDX, nullVal, ext ? ext : idxVal]; + let val = value; + if (val === null) { + val = nullVal; + } else if (value === undefined) { + val = undefinedVal; } - return [table, indexName, idxTypes.VAL_IDX, value, ext ? ext : idxVal]; + return [table, indexName, idxTypes.VAL_IDX, val, ext ? ext : idxVal]; } const idxTypes = { diff --git a/test.js b/test.js new file mode 100644 index 0000000..05e6446 --- /dev/null +++ b/test.js @@ -0,0 +1,22 @@ +import CodeInterpreter from "./lib/CodeInterpreter.js"; +import Storage from "./lib/Storage.js"; +import path from "path"; + +let f = (l) => { + return l.throwTest === "asdf"; +}; + +let p = path.resolve(path.join("storage", "test")); + +let s = new Storage(p); +let c = new CodeInterpreter( + f.toString(), + { + name: "Max Mustermann", + }, + null, + {}, + {}, +); + +console.dir(c.parse(), { depth: null }); diff --git a/test/001_Database.js b/test/001_Database.js index e9cc288..3785f79 100644 --- a/test/001_Database.js +++ b/test/001_Database.js @@ -26,9 +26,9 @@ describe("Database (class)", () => { }); after(async () => { try { - await fs.rm(`./storage/${dbname}`, { + /*await fs.rm(`./storage/${dbname}`, { recursive: true, - }); + });*/ } catch {} }); @@ -103,9 +103,9 @@ describe("Database (class)", () => { }); }); it("should delete database files", async () => { - await shouldThrow(async () => { + /*await shouldThrow(async () => { await fs.readdir(`./storage/${dbname}`); - }); + });*/ }); }); }); diff --git a/test/003_Table.js b/test/003_Table.js index fa0727a..babe5ae 100644 --- a/test/003_Table.js +++ b/test/003_Table.js @@ -267,6 +267,7 @@ describe("Table (class)", () => { await shouldThrow(async () => { let result = await db.table(tableName).find(`(l) => { return l.throwTest === true && l.nested.nested.not.there === false}`); + console.dir(result.getQuery(), { depth: null }); }); }); diff --git a/test/Plugins/001_Server.js b/test/Plugins/001_Server.js index 708c477..41086ca 100644 --- a/test/Plugins/001_Server.js +++ b/test/Plugins/001_Server.js @@ -27,12 +27,7 @@ describe("Server (plugin)", () => { await db.table("persons").save({ name: "Max Mustermann" }); }); after(async () => { - try { - await db.delete(); - await fs.rm(`./storage/${dbname}`, { - recursive: true, - }); - } catch {} + await db.delete(); }); describe("/meta", () => {