F.57. rum — метод доступа RUM для работы с индексами #
F.57.1. Введение #
Модуль rum предоставляет метод доступа RUM для работы с индексами. Он основан на коде методов доступа GIN.
Индексы GIN позволяют выполнять быстрый полнотекстовый поиск, используя типы tsvector и tsquery. Однако могут возникнуть некоторые проблемы с производительностью из-за того, что информация о позициях лексем и другая дополнительная информация не сохраняются.
Индекс RUM решает эти проблемы, сохраняя дополнительную информацию в дереве идентификаторов и обладает следующими преимуществами перед GIN: 
- Ускорение ранжирования. Для ранжирования необходима информация о позициях лексем. После сканирования индекса - RUMнет необходимости в сканировании собственно данных, так как индекс сохраняет позиции лексем.
- Ускорение поиска фраз. Это преимущество связано с предыдущим, так как для поиска фраз также необходима информация о позициях лексем. 
- Ускорение упорядочивания по меткам времени. Вместе с лексемами индекс - RUMсохраняет дополнительную информацию, поэтому не требуется сканирование собственно данных.
- Возможность производить поиск «сначала в глубину», а значит сразу выдавать первые результаты. 
Недостаток RUM состоит в том, что он строится и изменяется медленнее, чем GIN, потому что RUM хранит помимо ключей дополнительную информацию и использует унифицированные записи WAL.
F.57.2. Установка #
rum — это обычное расширение Postgres Pro Standard без каких-либо особых предварительных требований.
Процедура установки выглядит следующим образом:
$ psql имя_бд -c "CREATE EXTENSION rum"
F.57.3. Общие операторы #
Реализованные в модуле rum операторы перечислены в Таблице F.35:
Таблица F.35. Операторы rum
| Оператор | Возвращает | Описание | 
|---|---|---|
| tsvector<=>tsquery | float4 | Возвращает расстояние между значениями tsvectorиtsquery. | 
| timestamp<=>timestamp | float8 | Возвращает расстояние между двумя значениями timestamp. | 
| timestamp<=|timestamp | float8 | Возвращает расстояние только для возрастающих значений timestamp. | 
| timestamp|=>timestamp | float8 | Возвращает расстояние только для убывающих значений timestamp. | 
Примечание
rum вводит собственную функцию ранжирования, которая выполняется внутри оператора <=> и вычисляет оценку (инвертированное расстояние), используя заданный метод реализации. Она представляет собой сочетание функций ts_rank и ts_rank_cd (за подробностями обратитесь к Разделу 9.13). В то время, как ts_rank не поддерживает логические операции, а ts_rank_cd плохо работает с запросами OR, доступная в rum функция ранжирования лишена этих недостатков.
F.57.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_- type_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.57.5. Примеры #
F.57.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.57.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.57.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.57.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)