In uno dei miei lavori precedenti ho fatto qualcosa di simile:ho ottenuto una query (non un sql, ma abbastanza simile) e l'ho tradotta in query mongo con antlr.
Non ho un codice da condividere, tuttavia posso condividere i miei pensieri:
-
Mongo non è conforme a SQL, quindi non puoi semplicemente prendere una grammatica sql. Che dire dei JOIN e di tutta l'algebra relazionale? Che dire delle aggregazioni che sono piuttosto complicate in mongo con il loro framework di aggregazione? Nella direzione opposta, come si genera SQL che viene tradotto nella clausola "esiste" in mongo. Ci sono molte cose come questa, alcune sono piccole, altre sono enormi, ma in conclusione devi parlare di una sorta di sottoinsieme di sql, di un DSL che può essere utilizzato come linguaggio di query e sembra "come" un sql perché le persone sono abituate a SQL.
-
Con questo in mente, dovresti creare la tua grammatica e Antlr genererà un lexer/parser per te. Avrai anche per scontato un controllo della sintassi della query in Runtime. Antlr non sarà in grado di analizzare la query se non è in un formato corretto ovviamente, alcune regole grammaticali falliranno. Questo è un altro motivo per non prendere SQL "così com'è".
-
Fin qui tutto bene, hai creato il tuo ascoltatore/visitatore. Nel mio caso ho optato per la creazione di una rappresentazione ad oggetto della query con stato interno e tutto il resto. Quindi la query
Select id,name
from employee
where age > 30
and department = 'IT'
limit 200
È stato tradotto in oggetti di tipo:
class Query {
private SelectClause select;
private FromClause from;
private WhereClause where;
private Limit limit;
}
class SelectClause {
private List<String> fields;
}
...
class WhereClause {
Condition root;
}
interface Condition {
...
}
class AndCondition implements Condition { // the same for Not, Or
}
Per questa particolare query è qualcosa del tipo:
Query q = new Query(new SelectClause(["id", "name"]), new FromClause("employee"), new WhereClause(new AndCondition(new SimpleLeafCondition("age", Operators.GT, 30), new SimpleLeafCondition("department", Operators.EQ, "IT" )), new Limit(30));
Quindi è possibile apportare alcune ottimizzazioni nella query (come incorporare le clausole where se necessario o, ad esempio, manipolare la parte "For" se si lavora in un ambiente multi-tenant e si hanno raccolte diverse per tenant diversi).
Dopotutto puoi usare l'"interprete" del modello di progettazione e analizzare ricorsivamente gli oggetti della query e "tradurli" in una query mongo valida. Ricordo che questo passaggio mi ha richiesto qualcosa come 1 giorno per essere realizzato (era 7 anni fa con mongo 2 I guess, ma comunque), data la struttura corretta degli oggetti che rappresentano la query, quindi non dovrebbe essere così complicato. Ne parlo, perché sembra che sia la tua preoccupazione principale nella domanda.