Hacker News new | ask | show | jobs
Show HN: OmniQL – One Query Language for PostgreSQL, MySQL, MongoDB, and Redis (omniql.com)
2 points by canu21 126 days ago
Hey HN, I built OmniQL — an open-source Go library that compiles a universal query syntax into native database commands.

You write: :GET User WHERE id = 42

It becomes: PostgreSQL → SELECT * FROM "users" WHERE "id" = 42; MongoDB → db.users.find({ id: 42 }); Redis → HGETALL users:42

It's a compiler, not a wrapper. Your query becomes an AST, then translates to native commands. No runtime overhead — it generates the same query you'd write by hand.

Works for DDL too: :CREATE TABLE User WITH id:AUTO, name:STRING:NOTNULL

Becomes native CREATE TABLE for Postgres/MySQL or db.createCollection for MongoDB.

Why I built this: I run a multi-database platform and got tired of maintaining four different query syntaxes. OmniQL started as an internal tool and we're open-sourcing it.

GitHub: https://github.com/omniql-engine/omniql Docs: https://docs.omniql.com

Happy to answer questions about the compiler architecture, AST design, or how we handle the semantic gaps between SQL and NoSQL.

1 comments

The `:` prefix is an interesting choice. I don't fully understand when it is needed. It seems to always be used at the beginning of the query. But why is it not used before WHERE?
The : prefix marks OmniQL commands, the action you want to perform (:GET, :CREATE, :DELETE, etc.). It's how the parser distinguishes OmniQL syntax from native SQL or other query languages. WHERE, AND, OR, LIMIT etc. are clauses. They modify the command but aren't commands themselves. So the rule is simple: commands get :, clauses don't. Think of it like a terminal. You prefix the command (git push) but not the flags (--force). Same idea here.
Why does JOIN get the prefix then? Is it a command? I would assume that it is a GET clause.
In OmniQL, JOIN isn't a clause inside another query. It's a standalone operation. You write :INNER JOIN Order User ON user_id = id, not :GET ... JOIN .... Since it starts the query and tells the engine what to do, it gets the : prefix. The rule is: anything that begins a query gets :, anything that modifies it (WHERE, ON, LIMIT) doesn't. This is actually a deliberate design choice. In SQL, JOIN lives inside a SELECT so you end up with one big statement doing multiple things. In OmniQL each : command does one thing. Flat and predictable rather than nesting clauses inside clauses. This also lets the engine translate consistently whether it outputs SQL JOINs for Postgres and MySQL or a $lookup aggregation pipeline for MongoDB. Same syntax, engine handles the rest.