Indici fulltext Mysql/MariaDB su Django

Venerdì, 11 Marzo 2022 | Django |

La feature di Django per la ricerca fulltext su campi di DB, almeno sino alla versione 4.0, viene supportata direttamente solo su PostgreSQL.

Per quanto riguarda MySQL/MariaDB è necessario procedere con alcuni step manuali per definire l'indice in fase di migration e per implementare il metodo di search attraverso una Lookup custom.

Vediamo con ordine:

Creazione dell'ìndice fulltext

E' opportuno creare una migrazione vuota sulla quale lavorare. Ipotizziamo di avere un'app nomeapp e procediamo con:

./manage.py makemigrations --empty nomeapp

A questo punto andremo ad editare il file di migration generato, inserendo nelle operations la creazione/rimozione dell'indice fulltext sui campi/tabella desiderati

...
operations = [
        migrations.RunSQL(
            f"CREATE FULLTEXT INDEX nomeindice ON nometabella(nomecolonna)",
            f"DROP INDEX nomeindice ON nometabella",
        ),
    ]
...

Andiamo quindi ad applicare la migrazione

./manage.py migrate nomeapp

e l'indice fulltext verrà creato.

Implementazione della search fulltext

A questo punto è necessario fornire a Django la possibilità di utilizzare questo indice, con la sintassi SQL appropriata, per le ricerche.

Per fare ciò definiamo una Lookup custom chiamata search che andremo a registrare sul modello TextField e/o sugli altri modelli per i quali abbiamo definito indici fulltext.  Possiamo aggiungere direttamente in models.py:

class Search(models.Lookup):
    lookup_name = "search"

    def as_mysql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        params = lhs_params + rhs_params
        return f"MATCH (%s) AGAINST (%s IN BOOLEAN MODE)" % (lhs, rhs), params

models.TextField.register_lookup(Search)

Nulla ci vieta di estendere o definire altre tipologie di search nello stesso modo.

Utilizzo della search fulltext

Per effettuare una search fulltext è sufficiente utilizzare la lookup search sul campo per il quale è stato definito l'indice fulltext:

Documento.objects.filter(nomecolonna__search='+Paroladaricercare')