F.50. rum
F.50.1. Введение
Модуль rum предоставляет метод доступа RUM
для работы с индексами. Он основан на коде методов доступа GIN
.
Индекс GIN
позволяет выполнять быстрый полнотекстовый поиск, используя типы tsvector
и tsquery
. Однако при этом применении он имеет следующие недостатки:
Медленное ранжирование. Для ранжирования необходима информация о позициях лексем, но в индексе
GIN
эта информация не сохраняется. Поэтому после сканирования индекса необходимо провести ещё одно сканирование собственно данных, чтобы получить позиции лексем.Медленный поиск фраз. Эта проблема связана с предыдущей, так как и для поиска фраз необходима информация о позициях.
Медленное упорядочивание по меткам времени. Индекс
GIN
не может сохранять вместе с лексемами никакую дополнительную информацию, поэтому это требует сканирования кучи.
RUM
решает эти проблемы, сохраняя дополнительную информацию в дереве идентификаторов. В частности, он сохраняет информацию о позиции лексем или метки времени.
Недостаток RUM
состоит в том, что он строится и изменяется медленнее, чем GIN
, потому что RUM
хранит помимо ключей дополнительную информацию и использует унифицированные записи WAL.
F.50.2. Установка
rum
— это обычное расширение Postgres Pro Standard без каких-либо особых предварительных требований.
Процедура установки выглядит следующим образом:
$ psql имя_бд
-c "CREATE EXTENSION rum"
F.50.3. Общие операторы
Реализованные в модуле rum
операторы перечислены в Таблице F.104:
Таблица F.104. Операторы rum
Оператор | Возвращает | Описание |
---|---|---|
tsvector <=> tsquery | float4 | Возвращает расстояние между значениями tsvector и tsquery . |
timestamp <=> timestamp | float8 | Возвращает расстояние между двумя значениями timestamp . |
timestamp <=| timestamp | float8 | Возвращает расстояние только для возрастающих значений timestamp . |
timestamp |=> timestamp | float8 | Возвращает расстояние только для убывающих значений timestamp . |
F.50.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.50.5. Примеры
F.50.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.50.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.50.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.50.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.50.6. Авторы
Александр Коротков
Олег Бартунов
Фёдор Сигаев