Comment créer une recherche simple et floue avec Postgresql seulement?
j'ai un petit problème avec la fonctionnalité de recherche sur mon site basé sur RoR. J'ai beaucoup de produits avec des CODEs. Ce code peut être n'importe quelle chaîne de caractères comme "AB-123-lHdfj". Maintenant j'utilise L'opérateur ILIKE pour trouver des produits:
Product.where("code ILIKE ?", "%" + params[:search] + "%")
cela fonctionne bien, mais il ne peut pas trouver de produit avec des codes comme" AB123-lHdfj", ou"AB123lHdfj".
Que dois-je faire pour ça? Peut-être postgresql a-t-il une fonction de normalisation des chaînes, ou d'autres méthodes pour m'aider? :)
2 réponses
Postgres fournit un module avec plusieurs fonctions de comparsion de chaîne telles que soundex et métaphone. Mais vous voudrez utiliser la fonction levenshtein edit distance.
Example:
test=# SELECT levenshtein('GUMBO', 'GAMBOL');
levenshtein
-------------
2
(1 row)
Le 2
est la distance d'édition entre les deux mots. Quand vous appliquez ceci contre un certain nombre de mots et triez par le résultat de distance d'édition vous aurez le type de correspondances floues que vous recherchez.
essayez cette requête exemple: (avec vos propres noms d'objets et données bien sûr)
SELECT *
FROM some_table
WHERE levenshtein(code, 'AB123-lHdfj') <= 3
ORDER BY levenshtein(code, 'AB123-lHdfj')
LIMIT 10
cette requête dit:
Donnez-moi les 10 premiers résultats de toutes les données de some_table où la distance d'édition entre la valeur du code et L'entrée 'AB123-lHdfj' est inférieure à 3. Vous obtiendrez toutes les lignes où la valeur du code est inférieure de 3 caractères à 'AB123-lHdfj'...
Note: Si vous obtenez une erreur comme:
function levenshtein(character varying, unknown) does not exist
Installer l'extension fuzzystrmatch
en utilisant:
test=# CREATE EXTENSION fuzzystrmatch;
dont Paul vous a parlé levenshtein()
. C'est un outil très utile, mais il est également très lent, de grandes tables. Il doit calculer la distance levenshtein du terme de recherche pour chaque ligne simple, c'est cher.
d'Abord, si à vos exigences sont simples comme l'exemple indique, vous pouvez toujours utiliser LIKE
. Il suffit de remplacer -
dans votre terme de recherche par %
pour créer la clause WHERE
1519170920"
WHERE code LIKE "%AB%123%lHdfj%"
au lieu de
WHERE code LIKE "%AB-123-lHdfj%"
si votre vrai problème est plus complexe et que vous avez besoin de quelque chose de plus rapide alors - selon vos besoins - il ya plusieurs options.
-
Il y a "1519310920 recherche" plein texte , bien sûr. Mais c'est peut-être trop dans votre cas.
-
un candidat plus probable est pg_trgm . Notez que vous pouvez combiner cela avec
LIKE
dans PostgreSQL 9.1. Voir ce post de blog par Depesz .
Aussi très intéressant dans ce contexte: la fonctionsimilarity()
ou l'opérateur%
de ce module. Plus: -
enfin, vous pouvez mettre en œuvre une solution manuelle avec une fonction de normaliser les chaînes à rechercher. Par exemple , vous pouvez transformer
AB1-23-lHdfj
->ab123lhdfj
, le sauvegarder dans une colonne supplémentaire et le rechercher avec des termes de recherche qui ont été transformés de la même manière.ou utilisez un index sur une expression au lieu de la colonne redondante. (Impliquer les fonctions doivent être
IMMUTABLE
.) Et peut-être combiner cela avecpg_tgrm
d'en haut.
vue d'ensemble de pattern-matching techniques: