Я создаю пользовательский токенизатор
CREATE OR REPLACE FUNCTION edge_gram_tsvector(text text) RETURNS tsvector AS
$BODY$
BEGIN
RETURN (select array_to_tsvector((select array_agg(distinct substring(lexeme for len)) from unnest(to_tsvector(text)), generate_series(1,length(lexeme)) len)));
END;
$BODY$
IMMUTABLE
language plpgsql;
Эта функция создает все граничные нграммы, подобные этой
postgres=# select edge_gram_tsvector('general electric');
edge_gram_tsvector
-----------------------------------------------------------------------------------------
'e' 'el' 'ele' 'elec' 'elect' 'electr' 'g' 'ge' 'gen' 'gene' 'gener' 'genera' 'general'
(1 row)
Затем я создаю GIN
индекс для tsquery
create index on company using gin(edge_gram_tsvector(name));
Поисковый запрос будет выглядеть следующим образом
b2bdb_master=# select name from company where edge_gram_tsvector(name) @@ 'electric'::tsquery limit 3;
name
--------------------------------------------
General electric
Electriciantalk
Galesburg Electric Industrial Supply
(3 rows)
Производительность решения довольно высока
explain analyse select * from company where edge_gram_tsvector(name) @@ 'electric'::tsquery;
Bitmap Heap Scan on company (cost=175.13..27450.31 rows=20752 width=2247) (actual time=0.224..1.019 rows=343 loops=1)
Recheck Cond: (edge_gram_tsvector((name)::text) @@ '''electric'''::tsquery)
Heap Blocks: exact=342
-> Bitmap Index Scan on company_edge_gram_tsvector_idx (cost=0.00..169.94 rows=20752 width=0) (actual time=0.138..0.138 rows=343 loops=1)
Index Cond: (edge_gram_tsvector((name)::text) @@ '''electric'''::tsquery)
Planning Time: 0.216 ms
Execution Time: 1.100 ms