Il 20 agosto è stato mergiato nel ramo 13.x di Laravel il PR #61250, che porta la ricerca vettoriale nativa del query builder — whereVectorSimilarTo e compagnia — anche su MariaDB. Fin qui era roba solo per PostgreSQL con pgvector. La notizia mi interessa per due ragioni: ho passato undici anni a costruire gestionali su Laravel e MySQL, e il modo in cui il PR è scritto è una piccola lezione di design dei driver che vale più della funzionalità stessa. C'è anche una terza ragione, meno allegra: se i vostri progetti girano su MySQL liscio, come la maggior parte dei miei storici, restate fuori. E non per pigrizia di qualcuno.
Cosa c'era prima: un controllo hard-coded
Laravel 13 ha quattro metodi per lavorare con gli embedding direttamente dal query builder: whereVectorSimilarTo, whereVectorDistanceLessThan, orderByVectorDistance e selectVectorDistance. L'idea è quella di sempre: scrivi la query in fluent PHP, il framework la compila nell'SQL giusto per il tuo database.
Solo che fino a metà agosto il "database giusto" era uno solo. Dentro Query\Builder c'era un controllo instanceof PostgresConnection scritto a mano, e l'SQL della distanza — l'operatore <=> di pgvector — era inlined direttamente nel builder invece che nella grammatica del driver. Funzionava, ma era il classico debito che non si vede finché qualcuno non prova ad aggiungere il secondo database.
Cosa fa il PR: sposta la domanda al posto giusto
Il refactoring è piccolo e pulito. La domanda "questo driver sa calcolare distanze vettoriali?" si sposta dalla connessione alla grammatica, con una coppia di metodi — supportsVectorDistance() e compileVectorDistanceExpression($column) — che ogni driver può sovrascrivere. È lo stesso pattern che Laravel usa da anni per compileRandom() o per il supporto ai savepoint: un default sulla grammatica base, override per driver.
Con la domanda al posto giusto, aggiungere MariaDB diventa quasi banale: la sua grammatica compila la distanza in vec_distance_cosine(), la funzione nativa che MariaDB ha dalla 11.7 Community (11.4.5-3 sulla Enterprise), insieme a un tipo colonna VECTOR vero. Il lato schema — typeVector() e l'indice vettoriale — esisteva già da un PR precedente; mancava solo il lato query. Cinque giorni dopo è arrivato anche il follow-up #61337 con una correzione all'SQL e un cast Eloquent AsVector.
Questo è il punto che mi porto a casa come progettista di API, prima ancora che come utente: quando ti accorgi che stai scrivendo instanceof QualcosaConnection fuori dal driver, la feature sta nel posto sbagliato. Il primo database lo aggiungi comunque; è il secondo che ti presenta il conto.
La parte scomoda: MySQL liscio resta fuori
E i miei gestionali su MySQL? Niente. whereVectorSimilarTo() su una connessione MySQL standard continua a lanciare una RuntimeException — il PR si è limitato ad aggiornare il messaggio per menzionare MariaDB. Il motivo non è Laravel: MySQL Community ed Enterprise, nei binari normali, non hanno una funzione di distanza vettoriale nativa. DISTANCE() e VECTOR_DISTANCE() esistono solo su HeatWave (quindi su OCI) e su MySQL AI. Se non siete su Oracle Cloud, non c'è SQL da compilare.
Interessante anche cosa il PR ha rifiutato di fare: un fallback lato PHP che scarica le righe e calcola la similarità in memoria. Sarebbe stato comodo da annunciare e disastroso da usare — avrebbe silenziosamente rotto la semantica di limit() e della paginazione, e cambiato il contratto di performance della query senza dirlo a nessuno. Lasciar lanciare l'eccezione è la scelta onesta: meglio un errore chiaro di una lentezza misteriosa in produzione sei mesi dopo.
Cosa farei, in pratica
Per un gestionale esistente su MySQL che vuole una ricerca semantica — trovare "trattamento anti-crespo" quando l'utente cerca "capelli gonfi", per dire — le strade oggi sono tre, in ordine di preferenza mia:
- Valutare il passaggio a MariaDB, se la vostra applicazione usa MySQL in modo abbastanza standard da rendere la migrazione un'operazione noiosa invece che eroica. Con questo PR, MariaDB è diventato il modo più economico di avere vettori nativi restando nella famiglia MySQL.
- Un servizio separato per la sola ricerca — un piccolo Postgres con pgvector accanto al MySQL principale, sincronizzato dagli eventi dell'applicazione. Più pezzi da gestire, ma non tocca il database su cui vive tutto il resto.
- Aspettare, che è una strategia legittima: la ricerca semantica in un gestionale è quasi sempre una feature "nice to have", e il costo di infrastruttura sbagliata si paga per anni.
Per i progetti nuovi il discorso è chiuso in partenza: quando ho scelto Postgres per Miraviso, i vettori non erano nemmeno nel mio radar, ma è esattamente il tipo di dividendo che una scelta conservativa sul database paga nel tempo. E vale la pena dire anche il rovescio: nessuna di queste query serve a qualcosa sui dati che cifrate lato client. Le note sensibili di Miraviso, quelle che il server conserva come buste che non può aprire, non potranno mai finire in un indice vettoriale lato server — non puoi calcolare l'embedding di un testo che non puoi leggere. È un promemoria utile: la ricerca semantica è un trattamento dei dati come un altro, e va deciso con la stessa cura.
Dove sta andando la cosa
Il refactoring apre una porta più larga della singola feature: ora che la compilazione della distanza vive nella grammatica, aggiungere un driver o una metrica diversa è un PR piccolo, non un intervento a cuore aperto sul builder. C'è già una proposta per rendere configurabile la metrica di distanza (coseno, euclidea) invece di assumerla. La direzione mi pare quella giusta: il query builder che tratta i vettori come una capacità del driver, dichiarata dalla grammatica, esattamente come tutto il resto dell'SQL. Chi sta su MySQL liscio, nel frattempo, ha almeno un messaggio d'errore che gli dice la verità.