Как создать индекс для столбца XML в PostgreSQL с выражением xpath?

я сталкиваюсь с этой ошибкой при попытке создать индекс btree в столбце типа данных XML, который использует выражение xpath в AuroraDB-PostgreSQL 9.6:

ERROR:  could not identify a comparison function for type xml
SQL state: 42883

этот поток 2009 без четкого разрешения-единственный, который я нашел, обсуждая это сообщение об ошибке в отношении создания индекса на основе xpath для гораздо более ранней версии PostgreSQL: https://www.postgresql-archive.org/Slow-select-times-on-select-with-xpath-td2074839.html

в моем случае мне также нужно указать пространства имен, а также исходный плакат в этом потоке привел результат выражения xpath к тексту [], который тоже получает ошибку для меня - но почему это даже необходимо? Я также не вижу, чтобы PostgreSQL когда-либо использовал мой индекс, даже если у меня есть тысячи строк.

поэтому я попробовал более простой случай и ошибку все еще происходит - пожалуйста, пролить свет на то, почему, если бы вы могли:

CREATE TABLE test
(
    id integer NOT NULL,
    xml_data xml NOT NULL,
    CONSTRAINT test_pkey PRIMARY KEY (id)
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;



CREATE INDEX test_idx
    ON test USING btree 
    (xpath('/book/title', xml_data))

и полученное сообщение:

ERROR:  could not identify a comparison function for type xml
SQL state: 42883

кодировка базы данных UTF8. Параметры сортировки и тип символов-en_US.UTF-8.

некоторые примеры инструкций вставки тоже:

insert into source_data.test(id, xml_data) 
values(1, XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</title><chapter>1</chapter><chapter>2</chapter></book>'))

insert into source_data.test(id, xml_data) 
values(2, XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Apropos</title><chapter>1</chapter><chapter>2</chapter></book>'))

1 ответов


вы получаете эту ошибку, потому что тип данных XML не предоставляет никаких операторы сравнения, следовательно, вы не можете создать индекс на результат xpath(), потому что он возвращает массив значений XML.

поэтому вам нужно привести выражение XPath к текстовому массиву при создании индекса:

CREATE INDEX test_idx
ON test USING BTREE 
    (cast(xpath('/book/title', xml_data) as text[])) ;

этот индекс затем используется при запросе таблица:

EXPLAIN ANALYZE
SELECT * FROM test where
cast(xpath('/book/title', xml_data) as text[]) = '{<title>Apropos</title>}';

дает

                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
Index Scan using test_idx on test  (cost=0.13..8.15 rows=1 width=36) (actual time=0.034..0.038 rows=1 loops=1)
    Index Cond: ((xpath('/book/title'::text, xml_data, '{}'::text[]))::text[] = '{<title>Apropos</title>}'::text[])
Planning time: 0.168 ms
Execution time: 0.073 ms (4 rows)

это работает так же при использовании text():

CREATE INDEX test_idx
ON test USING BTREE 
    (cast(xpath('/book/title/text()', xml_data) as text[])) ;

explain analyze select * from test where
cast(xpath('/book/title/text()', xml_data) as text[]) = '{Apropos}';

дает

                                                   QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
 Index Scan using test_idx on test  (cost=0.13..8.15 rows=1 width=36) (actual time=0.034..0.038 rows=1 loops=1)
   Index Cond: ((xpath('/book/title/text()'::text, xml_data, '{}'::text[]))::text[] = '{Apropos}'::text[])
 Planning time: 0.166 ms
 Execution time: 0.076 ms
(4 rows)

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

SET enable_seqscan TO off;