如何使用 xpath 表达式在 PostgreSQL 中的 XML 列上创建索引?

Posted

技术标签:

【中文标题】如何使用 xpath 表达式在 PostgreSQL 中的 XML 列上创建索引?【英文标题】:How to create an index on an XML column in PostgreSQL with an xpath expression? 【发布时间】:2019-02-09 22:30:05 【问题描述】:

尝试在使用 AuroraDB - PostgreSQL 9.6 上的 xpath 表达式的 XML 数据类型列上创建 btree 索引时遇到此错误:

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

这个 2009 年没有明确解决方案的线程是我发现的唯一一个讨论此错误消息的线程,该错误消息是关于为更早版本的 PostgreSQL 创建基于 xpath 的索引: https://www.postgresql-archive.org/Slow-select-times-on-select-with-xpath-td2074839.html

在我的情况下,我还需要指定命名空间,并且该线程中的原始海报将 xpath 表达式的结果转换为 text[] ,这对我来说也是错误的 - 但为什么甚至需要呢?即使我有数千行要处理,我也没有看到 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 data type 没有提供任何comparison operators,因此您无法在xpath() 的结果上创建索引,因为它返回an array of XML values。

因此您需要在创建索引时将 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;

【讨论】:

如果能够在 text() 上创建索引,例如 say (cast(xpath('/book/title/text()', xml_data) as text));但这会导致错误:索引表达式中的函数必须标记为 IMMUTABLE 这是否意味着 text() 被认为是可变的并且没有办法解决? @Khorkrak 我使用text()添加了一个示例。

以上是关于如何使用 xpath 表达式在 PostgreSQL 中的 XML 列上创建索引?的主要内容,如果未能解决你的问题,请参考以下文章

使用webdriver查找元素时如何在xpath中使用撇号(')?

如何使用 xpath 表达式在 PostgreSQL 中的 XML 列上创建索引?

如何在 xpath 表达式中实现具有名称的用户定义函数?

如何在 Node 中使用 saxon-js 处理 XPath 表达式

如何在 Chrome 开发者工具或 Firefox 的 Firebug 中验证 XPath 表达式?

XPath如何定位dom节点