Pull to refresh

Comments 19

UFO just landed and posted this here
UFO just landed and posted this here

RUM позволяет использовать для сортировки другой столбец таблицы. Сделать напрямую, чтобы сортировка была по какому-то свойству проиндексированного массива, не получится.


Но если очень нужно, то всегда можно пойти в обход.


Вот у нас таблица и какие-то строчки в ней:


posts=# CREATE TABLE posts(id serial, tags text[]);
CREATE TABLE
posts=# INSERT INTO posts(tags) VALUES 
  (ARRAY['a']), (ARRAY['a','d']), 
  (ARRAY['b']), (ARRAY['b','d']),
  (ARRAY['c']), (ARRAY['c','d']), 
  (ARRAY['x']), (ARRAY['x','d']); 
INSERT 0 8

Фактически мы хотим получить вот такой результат (только с помощью индекса):


posts=# SELECT id, tags, tags @> ARRAY['d'] has_d 
FROM posts 
WHERE tags <@ ARRAY['a','b','c','d'] 
ORDER BY 3 DESC;
 id | tags  | has_d 
----+-------+-------
  2 | {a,d} | t
  4 | {b,d} | t
  6 | {c,d} | t
  1 | {a}   | f
  3 | {b}   | f
  5 | {c}   | f
(6 rows)

Ок, создаем дополнительный столбец под нужное для сортировки свойство и триггер. Столбец делаем целочисленным, потому что с boolean RUM не научили работать.


posts=# ALTER TABLE posts ADD has_d integer;
ALTER TABLE

posts=# CREATE FUNCTION set_has_d() RETURNS TRIGGER AS $$
BEGIN
  new.has_d := CASE WHEN new.tags @> ARRAY['d'] THEN 1 ELSE 0 END;
  RETURN new;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION

posts=# CREATE TRIGGER posts_has_d_trigger
BEFORE INSERT OR UPDATE ON posts
FOR EACH ROW
EXECUTE PROCEDURE set_has_d();
CREATE TRIGGER

Можно обновить столбец, заодно проверим, что триггер отрабатывает корректно:


cards=# UPDATE posts SET tags = tags;
UPDATE 8
cards=# SELECT * FROM posts;
 id | tags  | has_d 
----+-------+-------
  1 | {a}   |     0
  2 | {a,d} |     1
  3 | {b}   |     0
  4 | {b,d} |     1
  5 | {c}   |     0
  6 | {c,d} |     1
  7 | {x}   |     0
  8 | {x,d} |     1
(8 rows)

Ну а теперь уже индекс:


posts=# CREATE INDEX ON posts USING rum(tags rum_anyarray_addon_ops, has_d) WITH (attach='has_d', to='tags');
CREATE INDEX

posts=# SELECT * FROM posts
WHERE tags <@ ARRAY['a','b','c','d'] 
ORDER BY has_d <=> 1;
 id | tags  | has_d 
----+-------+-------
  4 | {b,d} |     1
  2 | {a,d} |     1
  6 | {c,d} |     1
  5 | {c}   |     0
  3 | {b}   |     0
  1 | {a}   |     0
(6 rows)

Результат правильный, и в плане — индексный доступ:


posts=# EXPLAIN (costs off)
SELECT * FROM posts 
WHERE tags <@ ARRAY['a','b','c','d']
ORDER BY has_d <=> 1;
                   QUERY PLAN                   
------------------------------------------------
 Index Scan using posts_tags_has_d_idx on posts
   Index Cond: (tags <@ '{a,b,c,d}'::text[])
   Order By: (has_d <=> 1)
(3 rows)

Оно?

UFO just landed and posted this here

Увы, сейчас в параметре attach ожидается имя столбца таблицы, ничего другого туда не подсунуть. Но я поговорю с разработчиками, возможно это получится изменить.

UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here

Да, так и есть. Но есть еще операторы |=> и <=|. Например, has_d <=| 27 выдаст 27, 26, 25, ..., 0, 28, 29, ...

linuxover, если я правильно понял вопрос, то можно поступить чуть проще.
=# create index on posts using rum(tags rum_anyarray_ops);
=# select * from posts where tags <@ '{a,b,c,d}' order by tags <=> '{d}';
 id | tags
----+-------
  2 | {a,d}
  4 | {b,d}
  6 | {c,d}
  1 | {a}
  3 | {b}
  5 | {c}

При этом способе сортировка также выполняется в индексе:
=# set enable_sort to off;
=# explain (costs off) select * from posts where tags <@ '{a,b,c,d}' order by tags <=> '{d}';
                 QUERY PLAN                  
---------------------------------------------
 Index Scan using posts_tags_idx on posts
   Index Cond: (tags <@ '{a,b,c,d}'::text[])
   Order By: (tags <=> '{d}'::text[])

А ведь точно! В этом конкретном случае так, конечно, значительно проще и правильней.


Артур, раз уж ты сюда пришел (: расскажи, реально ли для attach/to запилить поддержку выражений, а не только столбцов таблицы? Чтобы можно было функциональные индексы делать.

Такую поддержку реально сделать ) Но ее пока нет.
UFO just landed and posted this here

На свежую голову — и у меня, и у Артура на самом деле должно быть условие where tags && '{a,b,c}', а не where tags <@ '{a,b,c,d}'.

UFO just landed and posted this here

Ну да, лишь бы условие в WHERE поддерживалось индексом, а в ORDER BY был бы <=>.

UFO just landed and posted this here

Версии, отличающиеся только последней цифрой, всегда совместимы, так что при переходе с 9.5.5 и 9.5.6 точно ничего не надо. Вот если с 9.5 на 9.6, то скорее всего придется пересобрать.

Интересно было бы узнать, какова судьба этого расширения. Забросили ли? В ванильном PostgreSQL как был только GIN, так и остался, несмотря на то, что RUM имеет очевидные преимущества. Также этого расширения нет в списке AWS Aurora, а там есть практически все (даже wal2json какой-нибудь).

Sign up to leave a comment.