Как стать автором
Обновить

Комментарии 19

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

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)

Оно?

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Да, так и есть. Но есть еще операторы |=> и <=|. Например, 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 запилить поддержку выражений, а не только столбцов таблицы? Чтобы можно было функциональные индексы делать.

Такую поддержку реально сделать ) Но ее пока нет.
НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

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

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий