diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..91b9678 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.8' +services: + mongodb: + image: mongo:6-jammy + ports: + - '27017:27017' + volumes: + - dbdata6:/data/db +volumes: + dbdata6: \ No newline at end of file diff --git a/sample/index.js b/sample/index.js new file mode 100644 index 0000000..bbedfb7 --- /dev/null +++ b/sample/index.js @@ -0,0 +1,33 @@ +import {SqlToNoSql} from "../dist/index.mjs" + +const runner = new SqlToNoSql({ + srcDBtype: "postgresql", + destDBtype: "mongodb", + connection: "mongodb://localhost:27017/admin", +}); + + + +const main = async () => { + // Create a users table + const createUsers = await runner.run( + "create users" + ); + + + const resp = await runner.run( + "select * from users where email = devarifhossain@gmail.com", + ); + + console.log(resp); + return resp; +} + +main() +/** ☝️ [{ + _id: new ObjectId("622f07d56852c662cb8b953b"), + role: 'admin', + name: 'Arif Hossain', + email: 'devarifhossain@gmail.com', + __v: 0 + }]*/ \ No newline at end of file diff --git a/src/index.mts b/src/index.mts index 935b1c4..6f7db80 100644 --- a/src/index.mts +++ b/src/index.mts @@ -1,4 +1,4 @@ -import { MongoClient } from "mongodb"; +import { MongoClient, MongoServerError } from "mongodb"; import { mappings } from "./config/mapping.mjs"; import { parseQuery } from "./utils/parser.mjs"; @@ -22,46 +22,68 @@ export class SqlToNoSql { const q = parseQuery(query); - const filters: { - [key: string]: { - [operator: string]: string | number; - }; - } = {}; - - // Convert parsed filters to MongoDB query - q.filters?.forEach((filter) => { - const { column, operator, value } = filter; - - if (!filters[column]) { - filters[column] = { - [mappings["mongodb"]["operators"][operator]]: value, + if (q.command === "select"){ + const filters: { + [key: string]: { + [operator: string]: string | number; }; + } = {}; + + // Convert parsed filters to MongoDB query + q.filters?.forEach((filter) => { + const { column, operator, value } = filter; + + if (!filters[column]) { + filters[column] = { + [mappings["mongodb"]["operators"][operator]]: value, + }; + } + }); + + const mongoQuery = { + collection: q.table, + [q.command]: mappings["mongodb"]["commands"][q.command], + query: filters, + }; + + try { + if (!this.client) { + this.client = await connect(this.config.connection); + await this.client.connect(); + } + + const db = this.client.db(); + const collection = db.collection(mongoQuery.collection); + + const data = await collection[mongoQuery[q.command]]( + mongoQuery.query, + ).toArray(); + + return data; + } catch (err) { + console.error(err); + throw Error("Something went wrong!"); } - }); - - const mongoQuery = { - collection: q.table, - [q.command]: mappings["mongodb"]["commands"][q.command], - query: filters, - }; - - try { - if (!this.client) { - this.client = await connect(this.config.connection); - await this.client.connect(); + } else if (q.command === "create") { + try { + if (!this.client) { + this.client = await connect(this.config.connection); + await this.client.connect(); + } + + const db = this.client.db(); + const result = await db.createCollection(q.table); + console.log("Mongo result", result); + return result; + } catch (err) { + if (err instanceof MongoServerError) { + if (err.codeName === 'NamespaceExists'){ + console.error("Collection already exists: " + q.table) + } + } else { + throw Error("Something went wrong!"); + } } - - const db = this.client.db(); - const collection = db.collection(mongoQuery.collection); - - const data = await collection[mongoQuery[q.command]]( - mongoQuery.query, - ).toArray(); - - return data; - } catch (err) { - console.error(err); - throw Error("Something went wrong!"); } } } diff --git a/src/types/sql.mts b/src/types/sql.mts index e06bc65..be69ce5 100644 --- a/src/types/sql.mts +++ b/src/types/sql.mts @@ -5,7 +5,7 @@ interface filterType { } export interface ParsedSqlType { - command: "select"; + command: "select" | "create"; table: string; columns: string[]; filters: filterType[] | null; diff --git a/src/utils/parser.mts b/src/utils/parser.mts index 47cd9b8..997b9fa 100644 --- a/src/utils/parser.mts +++ b/src/utils/parser.mts @@ -2,6 +2,8 @@ import { ParsedSqlType } from "types/sql.mjs"; +const supportedCommands = ["select", "create"]; + export const parseQuery = (query: string): ParsedSqlType => { const parsedQuery: ParsedSqlType = { command: "select", @@ -13,32 +15,36 @@ export const parseQuery = (query: string): ParsedSqlType => { const [command, ...rest] = query.split(" "); const lowerCaseCommand = command.toLowerCase(); - if (lowerCaseCommand !== "select") { - throw new Error("Only select queries are supported"); + if (supportedCommands.indexOf(lowerCaseCommand) < 0) { + throw new Error(`Only following commands are supported: ${supportedCommands} given: ${lowerCaseCommand}`); } // parsedQuery.command = command; - - const fromIndex = rest.findIndex((word) => word.toLowerCase() === "from"); - if (fromIndex === -1) { - throw new Error("Invalid query, missing FROM keyword"); + if (lowerCaseCommand === "select"){ + const fromIndex = rest.findIndex((word) => word.toLowerCase() === "from"); + if (fromIndex === -1) { + throw new Error("Invalid query, missing FROM keyword"); + } + parsedQuery.table = rest[fromIndex + 1]; + parsedQuery.columns = rest.slice(0, fromIndex); + + const whereIndex = rest.findIndex((word) => word.toLowerCase() === "where"); + if (whereIndex !== -1) { + parsedQuery.filters = [ + { + column: rest[whereIndex + 1], + operator: rest[whereIndex + 2] as "=", + // to handle string and number values + value: + Number(rest[whereIndex + 3]) || + // remove quotes from string values + String(rest[whereIndex + 3]).replace(/^'(.*)'$/, "$1"), + }, + ]; + } + } else if (lowerCaseCommand === "create") { + parsedQuery.command = "create"; + const table_name = rest[0]; + parsedQuery.table = table_name; } - parsedQuery.table = rest[fromIndex + 1]; - parsedQuery.columns = rest.slice(0, fromIndex); - - const whereIndex = rest.findIndex((word) => word.toLowerCase() === "where"); - if (whereIndex !== -1) { - parsedQuery.filters = [ - { - column: rest[whereIndex + 1], - operator: rest[whereIndex + 2] as "=", - // to handle string and number values - value: - Number(rest[whereIndex + 3]) || - // remove quotes from string values - String(rest[whereIndex + 3]).replace(/^'(.*)'$/, "$1"), - }, - ]; - } - return parsedQuery; };