Search This Blog

2025/12/23

Query JSON Object using TSQL (Non Nexted JSON Object Attempt)

Building a Lightweight SQL Engine for In-Memory JSON Data

Have you ever wished you could query your JavaScript objects using standard SQL syntax? While libraries like AlaSQL exist, there is a certain magic (and performance benefit) in building a custom, lightweight execution pipeline tailored to your specific data needs.

In this post, I’ll walk through the architecture of JSON-SQLParser, a project I developed to bring the power of SQL—including nested functions, math libraries, and complex filtering—directly to JSON data structures.
The Challenge

Most in-memory filtering involves messy, nested .filter() and .map() chains that are hard to read and even harder to change dynamically. I wanted a way to pass a string like:
SQL

SELECT name, lpad(name, 10, '.') AS padded, age
FROM data.$friends
WHERE age > 25 AND contains(city, 'New')

And have it return a beautifully formatted result set.
The Architecture: A 3-Step Pipeline

The engine doesn't just "run" the string; it follows a professional compiler-style pipeline:
1. The Parser (Tokenization)

First, the engine breaks the SQL string into "Tokens." It identifies what is a column, what is a function call, and what is an alias (using the AS keyword).

Nested Support: The parser uses a stack-based approach to handle nested functions like upper(substr(name, 1, 3)).

2. The Execution Plan (The Blueprint)

Instead of executing on the fly, the engine builds a Plan. This is a series of steps that calculate intermediate "temporary variables" (like $t0, $t1). This prevents redundant calculations and allows us to handle complex dependencies in the SELECT clause.
3. The Executor (Virtual Machine)

The executor takes the Plan and the Data. It uses a custom evaluateExpression function that safely maps SQL logic into JavaScript operations. It handles:

Math Library: Automatic integration of the JS Math object (sqrt, sin, log, etc.).
String Utilities: Custom implementations of lpad, trim, instr, and contains.

Key Features
🚀 Case-Insensitive String Searching

In standard JS, .includes() is case-sensitive. My engine's contains() and instr() functions use .toLocaleLowerCase() internally, making queries more robust and user-friendly.
🧮 Dynamic Math Integration

By dynamically reducing the JavaScript Math object, the engine supports dozens of mathematical functions out-of-the-box without manual boilerplate code.
🔍 Advanced WHERE Logic

The engine translates SQL’s AND, OR, and NOT into JavaScript’s &&, ||, and !. This allows for complex conditional filtering that feels exactly like writing a Postgres or MySQL query.
Putting it to the Test

Using a sample dataset of "friends," we ran a Master Query to stress-test the pipeline:
JavaScript

let sqlQuery = `select
name,
lpad(name, 12, '-') AS formatted_name,
age,
instr(city, 'New') AS city_index,
upper(city) AS location
from data.$friends
where contains(city, 'man') OR (age > 20 AND length(name) <= 5)`;

The Result:

The output is rendered in a classic MySQL-style CLI table, proving that the aliasing and formatting logic work in harmony:

+----------+----------------+-----+------------+-----------+
name | formatted_name | age | city_index | location |
+----------+----------------+-----+------------+-----------+
| Joe | ---------Joe | 32 | 1 | NEW YORK |
| Robert | ------Robert | 45 | 0 | MANHATTAN |
+----------+----------------+-----+------------+-----------+

What's Next?

The current version of JSON-SQLParser is a strong proof-of-concept for high-performance in-memory querying. Future updates will focus on:

ORDER BY: To allow sorting of the result sets.

LIMIT: For pagination support.

JOINs: To link multiple JSON arrays together.

Building this engine was a deep dive into string parsing and execution logic. It proves that with just a few hundred lines of vanilla JavaScript, you can create powerful data tools that bridge the gap between SQL and JSON.

Check out the full source code here: GitHub - JSON-SQLParser
https://github.com/gitsangramdesai/JSON-SQLParser
This code achieves:

Flexible Parsing: Dynamically extracts columns and functions even when nested deep.

Aliasing: Supports clean, user-defined headers using AS.
 
Powerful Filtering: The WHERE clause handles complex logic and string searching.

Extensible Functions: Adding a new function is as simple as adding a key-value pair to the sqlFunctions registry.

Professional Output: The built-in formatter produces a standard CLI-style database table.

If you found this project interesting, feel free to star the repo or reach out with suggestions!

2025/12/16

Adaptation of Sudoku Solver for Node.js

Instead of cell wise backtracking it does box wise back tracking,code work
better in scenarios where solution is unique & board is harder as human.

Below is code that is generated by using chat gpt with my own logic thanks to
chatgpt for fixing my code else it might have taken month to perfect for
such complicated task.

// =======================
// PARALLEL SUDOKU SOLVER USING ROW PERMUTATION GENERATOR + WORKERS
// =======================

const { Worker, isMainThread, parentPort, workerData } = require("worker_threads");
const os = require("os");

const GRID_SIZE = 9;
const oneToNine = [1,2,3,4,5,6,7,8,9];

let board1 = [
[7, 0, 2, 0, 5, 0, 6, 0, 0],
[0, 0, 0, 0, 0, 3, 0, 0, 0],
[1, 0, 0, 0, 0, 9, 5, 0, 0],
[8, 0, 0, 0, 0, 0, 0, 9, 0],
[0, 4, 3, 0, 0, 0, 7, 5, 0],
[0, 9, 0, 0, 0, 0, 0, 0, 8],
[0, 0, 9, 7, 0, 0, 0, 0, 5],
[0, 0, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 7, 0, 4, 0, 2, 0, 3]
];

let board2 = [
[0, 6, 0, 1, 0, 4, 0, 5, 0],
[0, 0, 8, 3, 0, 5, 6, 0, 0],
[2, 0, 0, 0, 0, 0, 0, 0, 1],
[8, 0, 0, 4, 0, 7, 0, 0, 6],
[0, 0, 6, 0, 0, 0, 3, 0, 0],
[7, 0, 0, 9, 0, 1, 0, 0, 4],
[5, 0, 0, 0, 0, 0, 0, 0, 2],
[0, 0, 7, 2, 0, 6, 9, 0, 0],
[0, 4, 0, 5, 0, 8, 0, 7, 0]
];

let board3 = [
[0, 0, 0, 6, 0, 0, 4, 0, 0],
[7, 0, 0, 0, 0, 3, 6, 0, 0],
[0, 0, 0, 0, 9, 1, 0, 8, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 5, 0, 1, 8, 0, 0, 0, 3],
[0, 0, 0, 3, 0, 6, 0, 4, 5],
[0, 4, 0, 2, 0, 0, 0, 6, 0],
[9, 0, 3, 0, 0, 0, 0, 0, 0],
[0, 2, 0, 0, 0, 0, 1, 0, 0]
];

let board4 = [
[0, 0, 0, 0, 0, 6, 0, 8, 0],
[0, 0, 0, 0, 7, 0, 0, 0, 9],
[9, 0, 0, 3, 0, 0, 0, 0, 0],
[0, 0, 7, 0, 0, 0, 2, 0, 0],
[0, 8, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 3, 0, 0, 0, 7, 0, 0],
[0, 0, 0, 0, 0, 5, 0, 0, 8],
[1, 0, 0, 0, 9, 0, 0, 0, 0],
[0, 4, 0, 8, 0, 0, 0, 0, 0]
];


let board5 = [
[2, 0, 0, 0, 8, 0, 3, 0, 0],
[0, 6, 0, 0, 7, 0, 0, 8, 4],
[0, 3, 0, 5, 0, 0, 2, 0, 9],
[0, 0, 0, 1, 0, 5, 0, 0, 0],
[8, 0, 0, 0, 0, 0, 0, 0, 7],
[0, 0, 0, 6, 0, 2, 0, 0, 0],
[4, 0, 2, 0, 0, 7, 0, 6, 0],
[6, 8, 0, 0, 2, 0, 0, 7, 0],
[0, 0, 5, 0, 1, 0, 0, 0, 3]
];

let board6 = [
[0, 0, 4, 0, 0, 0, 8, 0, 5],
[0, 3, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 7, 0, 0, 0, 3, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 0],
[9, 0, 0, 0, 6, 0, 0, 0, 4],
[0, 8, 0, 0, 0, 0, 0, 0, 0],
[1, 7, 0, 0, 0, 5, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 7, 0],
[5, 0, 0, 0, 0, 0, 4, 0, 0]
];

let board7 = [
[0, 2, 0, 6, 0, 8, 0, 0, 0],
[5, 8, 0, 0, 0, 9, 7, 0, 0],
[0, 0, 0, 0, 4, 0, 0, 0, 0],
[3, 7, 0, 0, 0, 0, 5, 0, 0],
[6, 0, 0, 0, 0, 0, 0, 0, 4],
[0, 0, 8, 0, 0, 0, 0, 1, 3],
[0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 9, 8, 0, 0, 0, 3, 6],
[0, 0, 0, 3, 0, 6, 0, 9, 0]
];

let board8 = [
[0, 0, 0, 0, 0, 0, 0, 1, 2],
[0, 0, 6, 0, 0, 0, 0, 0, 0],
[0, 9, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 5, 0, 0, 4, 0, 0],
[0, 7, 0, 0, 0, 0, 0, 6, 0],
[0, 0, 3, 0, 0, 8, 0, 0, 0],
[0, 0, 0, 8, 0, 0, 0, 4, 0],
[0, 0, 0, 0, 0, 0, 7, 0, 0],
[6, 4, 0, 0, 0, 0, 0, 0, 0]
];

let board9 = [
[0, 0, 5, 0, 0, 0, 0, 7, 0],
[0, 7, 0, 0, 1, 0, 0, 0, 9],
[0, 0, 0, 6, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 4, 0, 0, 0],
[4, 0, 0, 0, 0, 0, 0, 0, 6],
[0, 0, 0, 9, 0, 0, 0, 0, 0],
[0, 0, 9, 0, 0, 1, 0, 0, 0],
[8, 0, 0, 0, 6, 0, 0, 4, 0],
[0, 1, 0, 0, 0, 0, 7, 0, 0]
];

//hard
let board10 = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 3, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 9, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 8, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 6, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 7, 0],
[0, 0, 0, 2, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 5]
];

let board11 = [
[0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 2],
[0, 0, 0, 0, 3, 5, 0, 0, 0],
[0, 0, 0, 7, 0, 0, 0, 0, 0],
[0, 0, 4, 0, 0, 0, 1, 0, 0],
[0, 9, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 5, 0, 0, 0, 0, 0],
[3, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 6, 0, 0]
];

let board12 = [
[0, 0, 0, 0, 0, 0, 0, 0, 4],
[0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 6, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 5, 0, 0, 0, 0, 0, 6, 0],
[0, 0, 0, 0, 0, 7, 0, 0, 0],
[0, 0, 0, 0, 8, 0, 0, 0, 0],
[3, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 9, 0, 0]
];

let board13 = [
[0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 2, 0, 0, 0],
[0, 0, 0, 3, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 4, 0, 0, 0, 0],
[0, 0, 5, 0, 0, 0, 6, 0, 0],
[0, 0, 0, 0, 0, 7, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 3, 0],
[0, 0, 8, 0, 0, 0, 0, 0, 0],
[2, 0, 0, 0, 0, 0, 0, 0, 0]
];

let board = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 2, 0, 0, 0, 0],
[0, 0, 0, 3, 0, 0, 0, 0, 0],
[0, 4, 0, 0, 0, 0, 0, 5, 0],
[0, 0, 0, 0, 0, 6, 0, 0, 0],
[0, 0, 0, 0, 7, 0, 0, 0, 0],
[8, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 9]
];

// -----------------------
// HELPERS
// -----------------------
function getColumn(board, col) {
return board.map(row => row[col]);
}

function getBox(board, row, col) {
const br = row - (row % 3);
const bc = col - (col % 3);
const box = [];
for (let r = br; r < br + 3; r++) {
for (let c = bc; c < bc + 3; c++) {
box.push(board[r][c]);
}
}
return box;
}

function isValidPartial(arr) {
const seen = new Set();
for (const n of arr) {
if (n === 0) continue;
if (seen.has(n)) return false;
seen.add(n);
}
return true;
}

function analyzeRow(row) {
const zeroIndexes = [];
const existing = new Set();
row.forEach((v,i)=>{
if(v===0) zeroIndexes.push(i);
else existing.add(v);
});
const missing = oneToNine.filter(n=>!existing.has(n));
return { zeroIndexes, missing };
}

function* permute(arr, path = [], used = []) {
if(path.length === arr.length) { yield [...path]; return; }
for(let i=0;i<arr.length;i++){
if(used[i]) continue;
used[i] = true;
path.push(arr[i]);
yield* permute(arr, path, used);
path.pop();
used[i] = false;
}
}

function* rowCandidateGenerator(row) {
const { zeroIndexes, missing } = analyzeRow(row);
if(missing.length===0) { yield row.slice(); return; }
for(const perm of permute(missing)){
const newRow = row.slice();
perm.forEach((val,i)=>newRow[zeroIndexes[i]]=val);
yield newRow;
}
}

// -----------------------
// BACKTRACK USING ROW PERM GENERATOR
// -----------------------
function solveUsingRowPermGen(board) {
function backtrack(rowIndex){
if(rowIndex===9) return true;

for(const candidateRow of rowCandidateGenerator(board[rowIndex])){
board[rowIndex] = candidateRow;

let valid = true;
for(let c=0;c<9&&valid;c++){
if(!isValidPartial(getColumn(board,c))) valid=false;
}
for(let r=0;r<=rowIndex&&valid;r++){
for(let c=0;c<9&&valid;c++){
if(!isValidPartial(getBox(board,r,c))) valid=false;
}
}

if(!valid){ board[rowIndex]=Array(9).fill(0); continue; }

if(backtrack(rowIndex+1)) return true;
board[rowIndex]=Array(9).fill(0);
}
return false;
}

return backtrack(0) ? board : null;
}

// -----------------------
// MULTI-THREADING SETUP
// -----------------------
if(isMainThread){
console.time("SudokuSolver");

const numCPUs = os.cpus().length;
const firstRowCandidates = [...rowCandidateGenerator(board[0])];
const chunkSize = Math.ceil(firstRowCandidates.length/numCPUs);

let solved = null;
let completedWorkers = 0;

for(let i=0;i<numCPUs;i++){
const chunk = firstRowCandidates.slice(i*chunkSize, (i+1)*chunkSize);
const worker = new Worker(__filename,{workerData:{board, firstRowChunk:chunk}});

worker.on("message", msg=>{
if(msg.solved && !solved){
solved = msg.board;
console.timeEnd("SudokuSolver");
console.log("Solved Board:");
console.table(solved);
process.exit(0);
}
});

worker.on("exit", ()=>{completedWorkers++; if(completedWorkers===numCPUs && !solved){
console.log("No solution found");
console.timeEnd("SudokuSolver");
}});
}

}else{
const boardCopy = JSON.parse(JSON.stringify(workerData.board));
const chunk = workerData.firstRowChunk;

for(const firstRow of chunk){
boardCopy[0] = firstRow;
const solved = solveUsingRowPermGen(boardCopy.map(r=>r.slice()));
if(solved){
parentPort.postMessage({solved:true, board:solved});
break;
}
}
}

If you are interested in ubuntu app check my github link
https://github.com/gitsangramdesai/deltaCharm