F.16. fuzzystrmatch — determina la similitud y distancia entre cadenas #

F.16.1. Soundex
F.16.2. Soundex Daitch-Mokotoff
F.16.3. Levenshtein
F.16.4. Metaphone
F.16.5. Double Metaphone

El módulo fuzzystrmatch proporciona varias funciones para determinar similitudes y distancia entre cadenas.

Caution

En la actualidad, las funciones soundex, metaphone, dmetaphone y dmetaphone_alt no funcionan bien con codificaciones multibyte (como UTF-8). Usa daitch_mokotoff o levenshtein con ese tipo de datos.

Este módulo se considera trusted (confiable), es decir, puede ser instalado por no superusuarios que tengan el privilegio CREATE en la base de datos actual.

F.16.1. Soundex #

El sistema Soundex es un método para emparejar nombres que suenan de forma similar convirtiéndolos al mismo código. Fue utilizado inicialmente por el censo de los Estados Unidos en 1880, 1900 y 1910. Ten en cuenta que Soundex no es muy útil para nombres que no estén en inglés.

El módulo fuzzystrmatch proporciona dos funciones para trabajar con códigos Soundex:

soundex(text) returns text
difference(text, text) returns int

La función soundex convierte una cadena a su código Soundex. La función difference convierte dos cadenas a sus códigos Soundex y luego informa el número de posiciones de código que coinciden. Dado que los códigos Soundex tienen cuatro caracteres, el resultado varía de cero a cuatro, donde cero indica que no hay coincidencia y cuatro es una coincidencia exacta. (Por lo tanto, la función tiene un nombre incorrecto — similarity habría sido un mejor nombre).

Aquí tienes algunos ejemplos de uso:

SELECT soundex('hello world!');

SELECT soundex('Anne'), soundex('Ann'), difference('Anne', 'Ann');
SELECT soundex('Anne'), soundex('Andrew'), difference('Anne', 'Andrew');
SELECT soundex('Anne'), soundex('Margaret'), difference('Anne', 'Margaret');

CREATE TABLE s (nm text);

INSERT INTO s VALUES ('john');
INSERT INTO s VALUES ('joan');
INSERT INTO s VALUES ('wobbly');
INSERT INTO s VALUES ('jack');

SELECT * FROM s WHERE soundex(nm) = soundex('john');

SELECT * FROM s WHERE difference(s.nm, 'john') > 2;

F.16.2. Soundex Daitch-Mokotoff #

Al igual que el sistema Soundex original, Daitch-Mokotoff Soundex empareja nombres de sonido similar convirtiéndolos al mismo código. Sin embargo, Daitch-Mokotoff Soundex es significativamente más útil para nombres que no están en inglés que el sistema original. Las mejoras principales sobre el sistema original incluyen:

  • El código se basa en las primeras seis letras significativas en lugar de cuatro.

  • Una letra o combinación de letras se mapea en diez códigos posibles en lugar de siete.

  • Cuando dos letras consecutivas tienen un único sonido, se codifican como un solo número.

  • Cuando una letra o combinación de letras puede tener diferentes sonidos, se emiten múltiples códigos para cubrir todas las posibilidades.

Esta función genera los códigos Daitch-Mokotoff soundex para su entrada:

daitch_mokotoff(source text) returns text[]

El resultado puede contener uno o más códigos según cuántas pronunciaciones plausibles existan, por lo que se representa como un array.

Dado que un código Daitch-Mokotoff soundex consiste de solo 6 dígitos, el parámetro source debería ser preferiblemente una sola palabra o nombre.

Aquí tienes algunos ejemplos:

SELECT daitch_mokotoff('George');
 daitch_mokotoff
-----------------
 {595000}

SELECT daitch_mokotoff('John');
 daitch_mokotoff
-----------------
 {160000,460000}

SELECT daitch_mokotoff('Bierschbach');
                      daitch_mokotoff
-----------------------------------------------------------
 {794575,794574,794750,794740,745750,745740,747500,747400}

SELECT daitch_mokotoff('Schwartzenegger');
 daitch_mokotoff
-----------------
 {479465}

Para emparejar nombres individuales, los arrays de texto devueltos se pueden comparar directamente utilizando el operador &&: cualquier superposición puede considerarse una coincidencia. Se puede utilizar un índice GIN para mayor eficiencia; consulta Section 65.4 y este ejemplo:

CREATE TABLE s (nm text);
CREATE INDEX ix_s_dm ON s USING gin (daitch_mokotoff(nm)) WITH (fastupdate = off);

INSERT INTO s (nm) VALUES
  ('Schwartzenegger'),
  ('John'),
  ('James'),
  ('Steinman'),
  ('Steinmetz');

SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Swartzenegger');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Jane');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Jens');

Para indexar y emparejar cualquier cantidad de nombres en cualquier orden, se pueden utilizar las características de Búsqueda de Texto Completo (Full Text Search). Consulta Chapter 12 y este ejemplo:

CREATE FUNCTION soundex_tsvector(v_name text) RETURNS tsvector
BEGIN ATOMIC
  SELECT to_tsvector('simple',
                     string_agg(array_to_string(daitch_mokotoff(n), ' '), ' '))
  FROM regexp_split_to_table(v_name, '\s+') AS n;
END;

CREATE FUNCTION soundex_tsquery(v_name text) RETURNS tsquery
BEGIN ATOMIC
  SELECT string_agg('(' || array_to_string(daitch_mokotoff(n), '|') || ')', '&')::tsquery
  FROM regexp_split_to_table(v_name, '\s+') AS n;
END;

CREATE TABLE s (nm text);
CREATE INDEX ix_s_txt ON s USING gin (soundex_tsvector(nm)) WITH (fastupdate = off);

INSERT INTO s (nm) VALUES
  ('John Doe'),
  ('Jane Roe'),
  ('Public John Q.'),
  ('George Best'),
  ('John Yamson');

SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('john');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('jane doe');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('john public');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('besst, giorgio');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('Jameson John');

Si se desea evitar el recálculo de los códigos soundex durante las comprobaciones del índice, se puede utilizar un índice en una columna separada en lugar de un índice en una expresión. Para esto se puede usar una columna generada almacenada; consulta Section 5.4.

F.16.3. Levenshtein #

Esta función calcula la distancia de Levenshtein entre dos cadenas:

levenshtein(source text, target text, ins_cost int, del_cost int, sub_cost int) returns int
levenshtein(source text, target text) returns int
levenshtein_less_equal(source text, target text, ins_cost int, del_cost int, sub_cost int, max_d int) returns int
levenshtein_less_equal(source text, target text, max_d int) returns int

Tanto source como target pueden ser cualquier cadena no nula, con un máximo de 255 caracteres. Los parámetros de costo especifican cuánto cobrar por una inserción, eliminación o sustitución de caracteres, respectivamente. Puedes omitir los parámetros de costo, como en la segunda versión de la función; en ese caso, todos tienen un valor predeterminado de 1.

levenshtein_less_equal es una versión acelerada de la función de Levenshtein para su uso cuando solo interesan distancias pequeñas. Si la distancia real es menor o igual a max_d, entonces levenshtein_less_equal devuelve la correcta distancia; de lo contrario, devuelve algún valor mayor que max_d. Si max_d es negativo, entonces el comportamiento es el mismo que levenshtein.

Ejemplos:

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

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

test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 2);
 levenshtein_less_equal
------------------------
                      3
(1 row)

test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 4);
 levenshtein_less_equal
------------------------
                      4
(1 row)

F.16.4. Metaphone #

Metaphone, al igual que Soundex, se basa en la idea de construir un código representativo para una cadena de entrada. Luego, se considera que dos cadenas son similares si tienen los mismos códigos.

Esta función calcula el código metaphone de una cadena de entrada:

metaphone(source text, max_output_length int) returns text

source debe ser una cadena no nula con un máximo de 255 caracteres. max_output_length establece la longitud máxima del código metaphone de salida; si es más largo, la salida se trunca a esta longitud.

Ejemplo:

test=# SELECT metaphone('GUMBO', 4);
 metaphone
-----------
 KM
(1 row)

F.16.5. Double Metaphone #

El sistema Double Metaphone calcula dos cadenas sounds like (suena como) para una cadena de entrada dada — una primaria y una alternativa. En la mayoría de los casos son iguales, pero especialmente para nombres que no están en inglés pueden ser un poco diferentes, dependiendo de la pronunciación. Estas funciones calculan los códigos primarios y alternativos:

dmetaphone(source text) returns text
dmetaphone_alt(source text) returns text

No hay límite de longitud para las cadenas de entrada.

Ejemplo:

test=# SELECT dmetaphone('gumbo');
 dmetaphone
------------
 KMP
(1 row)