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? :)

27
demandé sur Alve 2011-10-11 21:29:07

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;
39
répondu Paul Sasik 2014-10-16 17:14:52

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 fonction similarity() 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 avec pg_tgrm d'en haut.

vue d'ensemble de pattern-matching techniques:

35
répondu Erwin Brandstetter 2017-05-23 10:31:00