sql: new FTS mechanism with fuzzy match #1201
Loading…
Reference in New Issue
No description provided.
Delete Branch "wip/86527-better-fts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
3 parts in this work:
wcs_tsquery
function to replaceplainto_tsquery
and do a fuzzy-match of the requested words agains wcs_search_tokens (with a priority on full match obviously)@ -1679,0 +1728,4 @@
$function$;""")
# Second part : insert and update triggers
cur.execute("CREATE OR REPLACE TRIGGER wcs_all_forms_fts_trg_ins AFTER INSERT ON wcs_all_forms FOR EACH ROW WHEN (NEW.fts IS NOT NULL) EXECUTE PROCEDURE wcs_search_tokens_trigger_fn();")
J'ai bien noté que c'était encore WIP mais point d'attention quand même, le ticket en question (/api/formdefs) concerne les démarches (formdef, indexées via SearchableFormDef), pas les demandes (formdata).
Mais il sera bien d'avoir quelque chose de générique, qui pourra s'appliquer à l'ensemble des demandes (wcs_all_forms) mais aussi sur les tables de demandes par formulaire (
formdata_*
) et les tables des fiches (carddata_*
).Merci pour ce point, c'est bien noté.
C'était beaucoup plus simple pour moi de commencer par les demandes que par les démarches. La grosse partie est surtout dans le commit 3, et elle est indépendante. Une fois que cette base fonctionnera, j'y ajouterai les démarches.
c84d2c2e65
to768174fb85
8a7301f810
tod40f540a00
d40f540a00
to0807aaa1ca
0807aaa1ca
tod3a1dd3a6f
d3a1dd3a6f
tob6fbb639ec
b7fdbd4c32
to01f74d5c4b
88c718424b
tof80aa275b5
f80aa275b5
to5ac8da9b4e
5ac8da9b4e
to900ebf1d06
WIP: wip/86527-better-ftsto sql: new FTS mechanism with fuzzy matchIl faudrait quand même un peu de tests pour montrer ce que ça change / que ça marche mieux.
a9f810689b
to17ac728f78
J'ai intégré un test pour SearchableFormdef déjà (en reconstruisant l'historique pour qu'on voit bien le test arriver en échec, puis le reste du code qui corrige l'échec).
Ça correspond donc au test des triggers et de la fonction de recherche. Je dois voir encore pour tester la méthode du cron
45a7ccf97b
tofc6e252770
fc6e252770
to627f5a6f12
Et maintenant avec en plus un test de la purge et de l'indexation des formdata.
@ -703,2 +708,4 @@
CronJob(cls.clean_loggederrors, hours=[3], minutes=[0], name='clean_loggederrors')
)
cls.register_cronjob(
CronJob(cls.clean_search_tokens, weekdays=[0], hours=[1], minutes=[0], name='clean_tokens')
name='clean_search_tokens' donnera un peu de cohérence.
Corrigé, merci
627f5a6f12
to56fb3fb2fc
56fb3fb2fc
to0843793ac3
@ -97,2 +97,4 @@
def _table_exists(cur, table_name):
cur.execute('SELECT 1 FROM pg_class WHERE relname = %s;', (table_name,))
Je profite de l'occasion pour demander le sens des point-virgules en fin de ligne; ça n'est pas quelque chose que je mettais, est-ce important ?
Vieille habitude de ma part, ça ne change rien normalement, je peux le virer si tu préfères.
Non ok pour moi, j'étais juste curieux de peut-être un effet.
@ -1679,3 +1696,4 @@
cur.close()
def init_search_tokens(conn=None, cur=None):
Ça m'irait bien d'avoir ici un récapitulatif du fonctionnement, les tables/triggers/fonctions créées, comment les choses s'agencent et pour quel résultat.
Fait, je te laisse juger, avec la tête dans le guidon c'est plus dur de réaliser si la documentation est complète ou non.
@ -1682,0 +1730,4 @@
tokenized as (select unnest(regexp_split_to_array($1, '\s+')) w),
super_tokenized as (
select w,
coalesce((select plainto_tsquery(perfect.token) from wcs_search_tokens perfect where perfect.token = plainto_tsquery(w)::text),
Je suis toujours perdu par la lecture ici, la forme raccourcie pour renommer la table, ça marcherait pareil (je pense) et plus clair pour moi si c'était wcs_search_tokens AS perfect.
Peut-être plus généralement décrire la requête, et ça rejoint peut-être le point plus haut, si je comprends bien c'est "perfect" pour "perfect match" (ce que je capte en voyant plus bas "partial"), mais ça ne m'éclaire pas totalement sur ce qui est fait.
Fait, idem, n'hésite pas si y'a besoin de plus d'éléments.
@ -5121,3 +5281,3 @@
# programmaticaly but will make sure git conflicts if two migrations are
# separately added with the same number)
SQL_LEVEL = (106, 'add context column to logged_errors table')
SQL_LEVEL = (107, 'improved fts method')
j'aime bien garder le même commentaire ici et plus bas dans le migrate(), donc plutôt "new fts mechanism with tokens table". (et numéro à incrémenter).
0843793ac3
to171ade9840
171ade9840
toaf28be9910
@ -1682,0 +1740,4 @@
# And last: functions to use this brand new table
# These two aggregates make the search query far simpler to write
cur.execute('CREATE OR REPLACE AGGREGATE tsquery_agg_or (tsquery) (sfunc=tsquery_or, stype=tsquery);')
cur.execute('CREATE OR REPLACE AGGREGATE tsquery_agg_and (tsquery) (sfunc=tsquery_and, stype=tsquery);')
Je crois totalement que ça rend l'affaire plus simple à écrire mais je ne trouve pas de référence à tsquery_or dans la documentation de postgresql. Peut-être que ça serait utile d'expliciter ici en commentaire ce que font tsquery_agg_or et tsquery_agg_and.
Documentation améliorée avec info sur l'origine des fonctions dans PG, dis moi si c'est mieux
@ -1682,0 +1758,4 @@
tsquery_agg_or(plainto_tsquery(partial.token) order by partial.token <-> w desc),
plainto_tsquery(w)) tokens
FROM tokenized
LEFT JOIN wcs_search_tokens AS partial ON partial.token % w AND w not similar to '%[0-9]{2,}%'
Ok, donc sur un formdef ("Intervention du service hygiène, salubrité et environnement lorem ipsum plop plop plop") qui a été indexé avec :
Il y a un match sur hien j'imagine parce que hygien, parce que match "partial", qui se voit ainsi :
Je n'ai pas trouvé de description de l'opérateur % dans la documentation de postgresql. (pas le caractère le plus facile à chercher, certes).
Sinon dans le code je n'arrive pas à suivre ce qu'est w, je n'arrive même pas à dire si c'est tout le temps la même chose. J'encouragerais à allonger un peu le nom de cette variable, par exemple si c'était "token" tout le temps (si ça a du sens) ça pourrait être plus clair, modulo confusion avec le nom de la colonne, donc allonger encore, searched_token.
À reprendre pour essayer de comprendre comment tout ça fonctionne, je n'explique pas ceci, toujours ma démarche "Intervention du service hygiène, salubrité et environnement ..."
mais
vs
(l'indexation et la recherche lancée depuis wcs ramènent à de l'ascii, cf FtsMatch.get_fts_value())
En suivant, ça semble venir du dernier cas ("otherwise..."), plainto_tsquery(w), qui serait appelé avec "salubrit", ce qui le raccourcit à nouveau et tombe sur
Je ne sais que faire de tout ça mais j'espère que ça peut t'aider à voir quelque chose.
J'ai corrigé le w en word dans la requête, pour ne pas confondre avec un token de recherche justement (c'est avant la transformation finalement), et supprimé un order by inutile en l'état. J'ai ajouté un lien vers la documentation de pgtrgm pour l'opérateur %.
Concernant ton problème, je n'arrive pas à le reproduire, je cherche
@ -1682,0 +1798,4 @@
):
# Second part: insert and update triggers for wcs_all_forms
cur.execute(
"""CREATE TRIGGER wcs_all_forms_fts_trg_ins
À regarder le commit dans mon terminal je vois qu'il y a des lignes qui comme celle-ci se terminent par un caractère espace; il faudrait les retirer. (j'imagine qu'on pourrait trouver un hook pre-commit pour assurer ça).
af28be9910
toac48ebb70d
ac48ebb70d
tofcdc2581ca
fcdc2581ca
to5cdb00e496
Un truc a du changé, la recherche sur "tes" ne trouve plus :
(je notais juste l'erreur jenkins, je n'ai pas encore regardé les changements)
5cdb00e496
to614ff32a23
6bc9588111
to7baeb009d4
7baeb009d4
to89a165c975
2bc52f9ccd
to4bd581c32b
Il m'a fallu beaucoup de temps pour trouver ce qui causait cette régression pour jenkins et pas en local. Ce test est malheureux puisque "tes" est considéré comme un stop-word sur le dictionnaire français de postgresql. Donc un plainto_tsquery('tes') renvoie '', j'ai corrigé en passant sur le dictionnaire 'simple' pour la recherche de similarités.
Je viens de mettre à jour la branche locale et je tombe toujours sur le même comportement; alors qu'il y a un token "salubrit" dans wcs_search_tokens,
select wcs_tsquery('salubrite');
retourne 'salubr', ce qui fait qu'une recherche de "salubrité" ne trouve pas le formdef qui a salubrité dans son nom (puisque dans sa colonne fts de searchable_formdefs c'est 'salubrit' qui s'y trouve).Je ne sais pas ce qui pourrait aider dans comme description, pour reproduire, mais je viens d'ajouter un commit dans la branche, qui échoue aussi sur jenkins. (et qui fonctionne appliqué sur main).
Merci bien, en fait le problème est bien plus profond que ça, le code actuel casse complètement la mécanique de FTS donc je vais voir pour implémenter le même fonctionnement que l'existant, mais je ferai un autre ticket pour détailler le problème. En très court, le code actuel casse FTS en retirant tous les accents car le dictionnaire intégré au FTS s'effondre...
Exemple rapide et idiot : avec FTS, écolier et écolière sont le même mot normalement, mais sans les accents...
aa2c7b3fe8
to71a1489c88
J'ai mis à jour la branche en incorporant ton test, qui me semble important à garder pour l'avenir, directement dans les tests du FTS.
Dans mon développement, je partais du principe que plainto_tsquery(plainto_tsquery('bidule')::text) était égal à plainto_tsquery('bidule'), ce qui n'est pas le cas quand on sort du dictionnaire avec le unaccent.
Donc je n'ai eu qu'à retirer cette supposition et la remplacer par un cast direct pour régler le problème.
71a1489c88
tod5036ed141
02b9ecf665
to6b0108f527
6b0108f527
to9d8efbda51
5d8ceee958
to4c2278fdaa
4c2278fdaa
to7dc8fe49b7
7dc8fe49b7
to817875d6cb
817875d6cb
toaec7181a78
Passons ça ainsi.
aec7181a78
to9f23205882
C'est fait, merci !