F.51. rum
F.51.1. Введение
Модуль rum предоставляет метод доступа RUM для работы с индексами. Он основан на коде методов доступа GIN.
Индекс GIN позволяет выполнять быстрый полнотекстовый поиск, используя типы tsvector и tsquery. Однако при этом применении он имеет следующие недостатки:
Медленное ранжирование. Для ранжирования необходима информация о позициях лексем, но в индексе
GINэта информация не сохраняется. Поэтому после сканирования индекса необходимо провести ещё одно сканирование собственно данных, чтобы получить позиции лексем.Медленный поиск фраз. Эта проблема связана с предыдущей, так как и для поиска фраз необходима информация о позициях.
Медленное упорядочивание по меткам времени. Индекс
GINне может сохранять вместе с лексемами никакую дополнительную информацию, поэтому это требует сканирования кучи.
RUM решает эти проблемы, сохраняя дополнительную информацию в дереве идентификаторов. В частности, он сохраняет информацию о позиции лексем или метки времени.
Недостаток RUM состоит в том, что он строится и изменяется медленнее, чем GIN, потому что RUM хранит помимо ключей дополнительную информацию и использует унифицированные записи WAL.
F.51.2. Установка
rum — это обычное расширение Postgres Pro Standard без каких-либо особых предварительных требований.
Процедура установки выглядит следующим образом:
$ psql имя_бд -c "CREATE EXTENSION rum"
F.51.3. Общие операторы
Реализованные в модуле rum операторы перечислены в Таблице F.108:
Таблица F.108. Операторы rum
| Оператор | Возвращает | Описание |
|---|---|---|
tsvector <=> tsquery | float4 | Возвращает расстояние между значениями tsvector и tsquery. |
timestamp <=> timestamp | float8 | Возвращает расстояние между двумя значениями timestamp. |
timestamp <=| timestamp | float8 | Возвращает расстояние только для возрастающих значений timestamp. |
timestamp |=> timestamp | float8 | Возвращает расстояние только для убывающих значений timestamp. |
F.51.4. Классы операторов
Расширение rum предоставляет следующие классы операторов:
rum_tsvector_opsСохраняет лексемы
tsvectorс информацией о позициях. Поддерживает упорядочивание с оператором<=>и поиск по префиксу.rum_tsvector_hash_opsСохраняет хеш лексем
tsvectorс информацией о позициях. Поддерживает упорядочивание с оператором<=>, но не поддерживает поиск по префиксу.rum_tsvector_addon_opsСохраняет лексемы
tsvectorс дополнительными данными любых типов, которые принимаетRUM.rum_tsvector_hash_addon_opsСохраняет лексемы
tsvectorс дополнительными данными любых типов, которые принимаетRUM. Не поддерживает поиск по префиксу.rum_tsquery_opsСохраняет ветви дерева запроса в дополнительной информации.
rum_anyarray_opsСохраняет элементы массива
anyarrayи длину массива. Поддерживает упорядочивание с оператором <=>.Индексируемые операторы:
&& @> <@ = %rum_anyarray_addon_opsСохраняет элементы
anyarrayс дополнительными данными любых типов, которые принимаетRUM.rum_тип_opsСохраняет лексемы соответствующего типа с информацией о позициях. В качестве
типав имени класса должно подставляться одно из следующих имён типов:int2,int4,int8,float4,float8,money,oid,timestamp,timestamptz,time,timetz,date,interval,macaddr,inet,cidr,text,varchar,char,bytea,bit,varbit,numeric.Класс операторов
rum_поддерживает упорядочивание с операторамитип_ops<=>,<=|и|=>. Его можно использовать совместно с классами операторовrum_tsvector_addon_ops,rum_tsvector_hash_addon_opsиrum_anyarray_addon_ops.Поддержка индексируемых операторов зависит от типа данных:
Операторы
< <= = >= > <=> <=| |=>поддерживаются для типовint2,int4,int8,float4,float8,money,oid,timestamp,timestamptz.Операторы
< <= = >= >поддерживаются для типовtime,timetz,date,interval,macaddr,inet,cidr,text,varchar,char,bytea,bit,varbit,numeric.
Примечание
Следующие классы операторов теперь считаются устаревшими: rum_tsvector_timestamp_ops, rum_tsvector_timestamptz_ops, rum_tsvector_hash_timestamp_ops, rum_tsvector_hash_timestamptz_ops.
F.51.5. Примеры
F.51.5.1. Пример с rum_tsvector_ops
Предположим, что у нас есть таблица:
CREATE TABLE test_rum(t text, a tsvector);
CREATE TRIGGER tsvectorupdate
BEFORE UPDATE OR INSERT ON test_rum
FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('a', 'pg_catalog.english', 't');
INSERT INTO test_rum(t) VALUES ('The situation is most beautiful');
INSERT INTO test_rum(t) VALUES ('It is a beautiful');
INSERT INTO test_rum(t) VALUES ('It looks like a beautiful place');Затем мы можем создать новый индекс:
CREATE INDEX rumidx ON test_rum USING rum (a rum_tsvector_ops);
И выполнять следующие запросы:
SELECT t, a <=> to_tsquery('english', 'beautiful | place') AS rank
FROM test_rum
WHERE a @@ to_tsquery('english', 'beautiful | place')
ORDER BY a <=> to_tsquery('english', 'beautiful | place');
t | rank
---------------------------------+-----------
The situation is most beautiful | 0.0303964
It is a beautiful | 0.0303964
It looks like a beautiful place | 0.0607927
(3 rows)
SELECT t, a <=> to_tsquery('english', 'place | situation') AS rank
FROM test_rum
WHERE a @@ to_tsquery('english', 'place | situation')
ORDER BY a <=> to_tsquery('english', 'place | situation');
t | rank
---------------------------------+-----------
The situation is most beautiful | 0.0303964
It looks like a beautiful place | 0.0303964
(2 rows)F.51.5.2. Пример с rum_tsvector_addon_ops
Предположим, что у нас есть таблица:
CREATE TABLE tsts (id int, t tsvector, d timestamp);
\copy tsts from 'rum/data/tsts.data'
CREATE INDEX tsts_idx ON tsts USING rum (t rum_tsvector_addon_ops, d)
WITH (attach = 'd', to = 't');С ним мы можем выполнять подобные запросы:
EXPLAIN (costs off)
SELECT id, d, d <=> '2016-05-16 14:21:25' FROM tsts WHERE t @@ 'wr&qh' ORDER BY d <=> '2016-05-16 14:21:25' LIMIT 5;
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
-> Index Scan using tsts_idx on tsts
Index Cond: (t @@ '''wr'' & ''qh'''::tsquery)
Order By: (d <=> 'Mon May 16 14:21:25 2016'::timestamp without time zone)
(4 rows)
SELECT id, d, d <=> '2016-05-16 14:21:25' FROM tsts WHERE t @@ 'wr&qh' ORDER BY d <=> '2016-05-16 14:21:25' LIMIT 5;
id | d | ?column?
-----+---------------------------------+---------------
355 | Mon May 16 14:21:22.326724 2016 | 2.673276
354 | Mon May 16 13:21:22.326724 2016 | 3602.673276
371 | Tue May 17 06:21:22.326724 2016 | 57597.326724
406 | Wed May 18 17:21:22.326724 2016 | 183597.326724
415 | Thu May 19 02:21:22.326724 2016 | 215997.326724
(5 rows)F.51.5.3. Пример с rum_tsquery_ops
Предположим, что у нас таблица:
CREATE TABLE query (q tsquery, tag text);
INSERT INTO query VALUES ('supernova & star', 'sn'),
('black', 'color'),
('big & bang & black & hole', 'bang'),
('spiral & galaxy', 'shape'),
('black & hole', 'color');
CREATE INDEX query_idx ON query USING rum(q);Мы можем выполнить следующий быстрый запрос:
SELECT * FROM query
WHERE to_tsvector('black holes never exists before we think about them') @@ q;
q | tag
------------------+-------
'black' | color
'black' & 'hole' | color
(2 rows)F.51.5.4. Пример с rum_anyarray_ops
Предположим, что у нас есть таблица:
CREATE TABLE test_array (i int2[]);
INSERT INTO test_array VALUES ('{}'), ('{0}'), ('{1,2,3,4}'), ('{1,2,3}'), ('{1,2}'), ('{1}');
CREATE INDEX idx_array ON test_array USING rum (i rum_anyarray_ops);Теперь мы можем выполнить следующий запрос, используя сканирование индекса:
SET enable_seqscan TO off;
EXPLAIN (COSTS OFF) SELECT * FROM test_array WHERE i && '{1}' ORDER BY i <=> '{1}' ASC;
QUERY PLAN
------------------------------------------
Index Scan using idx_array on test_array
Index Cond: (i && '{1}'::smallint[])
Order By: (i <=> '{1}'::smallint[])
(3 rows)
SELECT * FROM test_array WHERE i && '{1}' ORDER BY i <=> '{1}' ASC;
i
-----------
{1}
{1,2}
{1,2,3}
{1,2,3,4}
(4 rows)F.51.6. Авторы
Александр Коротков
Олег Бартунов
Фёдор Сигаев