web-dev-qa-db-fra.com

Comment créer une recherche floue simple avec Postgresql uniquement?

J'ai un petit problème avec la fonctionnalité de recherche sur mon site basé sur RoR. J'ai beaucoup de Produts avec quelques CODE. Ce code peut être n'importe quelle chaîne 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 que postgresql a une fonction de normalisation de chaîne ou d'autres méthodes pour m'aider? :)

38
Alve

Postgres fournit un module avec plusieurs fonctions de comparaison de chaînes telles que soundex et metaphone. Mais vous voudrez utiliser la fonction d'édition de distance levenshtein .

Example:

test=# SELECT levenshtein('GUMBO', 'GAMBOL');
 levenshtein
-------------
           2
(1 row)

Le 2 est la distance d'édition entre les deux mots. Lorsque vous appliquez cela à un certain nombre de mots et que vous triez en fonction du résultat de la distance d'édition, vous obtiendrez le type de correspondances floues que vous recherchez.

Essayez cet exemple de requête: (avec vos propres noms d'objet 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 de code et l'entrée 'AB123-lHdfj' est inférieure à 3. Vous récupérerez toutes les lignes où la valeur du code est dans une différence de 3 caractères avec ' AB123-lHdfj '...

Remarque: si vous obtenez une erreur comme:

function levenshtein(character varying, unknown) does not exist

Installez l'extension fuzzystrmatch en utilisant:

test=# CREATE EXTENSION fuzzystrmatch;
54
Paul Sasik

Paul vous a parlé de levenshtein() . C'est un outil très utile, mais il est également très lent avec de grandes tables. Il doit calculer la distance de levenshtein à partir du terme de recherche pour chaque ligne, c'est cher.

Tout d'abord, si vos besoins sont aussi simples que l'indique l'exemple, vous pouvez toujours utiliser LIKE. Remplacez simplement - Dans votre terme de recherche par % Pour créer la clause WHERE

WHERE code LIKE "%AB%123%lHdfj%"

au lieu de

WHERE code LIKE "%AB-123-lHdfj%"

Si votre problème réel est plus complexe et que vous avez besoin de quelque chose de plus rapide, selon vos besoins, il existe plusieurs options.

  • Il y a recherche plein texte , bien sûr. Mais cela peut être exagéré dans votre cas.

  • Un candidat plus probable est pg_trgm . Notez que vous pouvez combiner cela avec LIKE dans PostgreSQL 9.1. Voir ceci article de blog de Depesz .
    Également très intéressant dans ce contexte: la fonction similarity() ou l'opérateur % De ce module. Plus:

  • Enfin, vous pouvez implémenter une solution tricotée à la main avec une fonction pour normaliser les chaînes à rechercher. Par exemple, vous pouvez transformer AB1-23-lHdfj -> ab123lhdfj, L'enregistrer 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. (Les fonctions impliquées doivent être IMMUTABLE.) Et éventuellement combiner cela avec pg_tgrm D'en haut.

Aperçu des techniques de correspondance de motifs:

44
Erwin Brandstetter