Convert SQL to MongoDB queries
This SQL to MongoDB converter parses your SQL with a real parser and rewrites each statement as a MongoDB shell command. A simple SELECT becomes db.collection.find() with a filter, projection, sort, skip and limit. Queries with GROUP BY or aggregate functions become an aggregate() pipeline. It is a quick way to learn MongoDB's query language when you already think in SQL, or to port queries during a migration.
Example: SELECT to find()
SELECT name, email, age FROM users
WHERE age >= 18 AND status = 'active'
AND (country IN ('US', 'CA') OR name LIKE 'Jo%')
ORDER BY age DESC
LIMIT 20 OFFSET 40;becomes
db.users.find({
age: { $gte: 18 },
status: 'active',
$or: [
{ country: { $in: ['US', 'CA'] } },
{ name: { $regex: '^Jo' } }
]
}, { name: 1, email: 1, age: 1, _id: 0 }).sort({ age: -1 }).skip(40).limit(20);Conditions joined by AND are merged into one filter object when their field names do not collide, OR becomes $or, and LIKE 'Jo%' turns into an anchored regular expression. Because the column list was explicit, _id: 0 is added so the result matches the SQL columns.
Example: GROUP BY to aggregate()
SELECT city, COUNT(*) AS total, AVG(age) AS avg_age FROM users GROUP BY city HAVING total > 5 ORDER BY total DESC converts to:
db.users.aggregate([
{
$group: { _id: '$city', total: { $sum: 1 }, avg_age: { $avg: '$age' } }
},
{ $match: { total: { $gt: 5 } } },
{ $project: { _id: 0, city: '$_id', total: 1, avg_age: 1 } },
{ $sort: { total: -1 } }
]);
Supported SQL features
- Operators:
=,!=/<>,<,<=,>,>=,IN,NOT IN,BETWEEN,IS NULL,IS NOT NULL,LIKE,NOT LIKE,ILIKE(case-insensitive regex) andREGEXP.NOT (...)becomes$nor. - Aggregates:
COUNT,SUM,AVG,MINandMAXmap to$sum,$avg,$minand$maxin a$groupstage.COUNT(*)is$sum: 1, whileCOUNT(col)becomes a conditional$sumthat skips documents where the field is null or missing;COUNT(DISTINCT col)is not supported and stops with an error.HAVINGbecomes a$matchafter the group, whether it uses an alias or an aggregate such asCOUNT(*) > 5. - Shortcuts: a lone
SELECT COUNT(*)becomescountDocuments(), andSELECT DISTINCT colbecomesdistinct('col', filter). - Writes:
INSERTmaps toinsertOneorinsertMany,UPDATEtoupdateManywith$set, andDELETEtodeleteMany.SET visits = visits + 1is recognized and turned into$inc. - Paging:
LIMIT n OFFSET m, MySQL'sLIMIT m, n, and with the SQL Server dialectTOP nandOFFSET m ROWS FETCH NEXT n ROWS ONLY, becomeskip()andlimit().
The SQL dialect option controls how the input is parsed. Choose SQL Server for TOP or bracketed identifiers, PostgreSQL for ILIKE, or BigQuery for backtick-quoted project paths.
Limitations
- JOINs are not converted. MongoDB uses
$lookupor embedded documents, and the right design depends on your data model, so the tool stops with an explanatory error. Convert the single-table part and add$lookupby hand. - Subqueries,
UNION, window functions and most SQL functions are not supported; subqueries andUNIONstop with a message that says so. In aWHEREfilter,NOW()is emitted as a$$NOWplaceholder; in a plainfind()filter, replace it withnew Date(). InINSERTandUPDATEvalues it is already written asnew Date(). - Column names are kept as-is, so an SQL
idcolumn staysid. If your documents use_id, rename the field in the filter. - Only plain columns and aggregates are allowed in the
SELECTlist; computed expressions such asprice * qtyneed a$projectstage you write yourself.
Also keep types in mind: '42' in SQL is a string, and MongoDB will not match it against a stored number.
Privacy
Parsing and conversion run in your browser, so your queries and table names are never uploaded. To tidy the SQL first, run it through the SQL formatter, or check it with the SQL validator. If you are designing collections, JSON to Mongoose schema generates a schema from a sample document.