首页 > 解决方案 > 在 PostgreSQL 中搜索 XML 文档

问题描述

所以我目前有一个使用 psql 的非常简单的表,如下所示:

Table "public.test"
 Column |  Type   | Collation | Nullable |             Default              
--------+---------+-----------+----------+----------------------------------
 id     | integer |           | not null | nextval('test_id_seq'::regclass)
 blah   | xml     |           |          | 
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)

目前有这个数据:

id |       blah       
----+------------------
  1 | =juego_Tipo1.xml
(1 row)

文件“juego_Tipo1.xml”如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Gamificacion Id="1">
<titulo>NomTitulo</titulo>
<autor>NomAutor</autor>
<preguntas tipo="Tipo1">
<pregunta EmpiezaPor="" comodin50="" id="preg_0" sol="A">
<temaPregunta/>
<enunciado numLineas="3">Este es el enunciado 1</enunciado>
<respuesta num="1" numLetras="5" numLineas="1">Respuesta A</respuesta>
<respuesta num="2" numLetras="5" numLineas="1">Respuesta B</respuesta>
<respuesta num="3" numLetras="5" numLineas="1">Respuesta C</respuesta>
<respuesta num="4" numLetras="5" numLineas="1">Respuesta D</respuesta>
<pista id="1" numLineas="numLineas"/>
</pregunta>
<pregunta EmpiezaPor="" comodin50="" id="preg_1" sol="B">
<temaPregunta/>
<enunciado numLineas="3">Enunciado 2</enunciado>
<respuesta num="1" numLetras="5" numLineas="1">Respuesta A 2 </respuesta>
<respuesta num="2" numLetras="5" numLineas="1">Respuesta B 2</respuesta>
<respuesta num="3" numLetras="5" numLineas="1">Respuesta C 2</respuesta>
<respuesta num="4" numLetras="5" numLineas="1">Respuesta D 2</respuesta>
<pista id="1" numLineas="numLineas"/></pregunta></preguntas></Gamificacion>

我要做的是检查“respuesta”中的任何元素是否是“Respuesta A”。如果是这样,我想返回满足该条件的文件(或文件)的名称。

我尝试使用该xpath(xpath, xml [, nsarray])函数,但它会引发此错误:

ERROR:  could not parse XML document
DETAIL:  line 1: Start tag expected, '<' not found
=juego_Tipo1.xml

我假设这是因为该函数仅在“blah”中的内容是 xml 而不是 xml 文件时才有效。

这可能吗?如果没有,我应该如何解决这个问题?

我目前使用的 Postgresql 是 10.15。

我使用以下命令创建了表:

create table test(id serial primary key, blah xml);

我已经使用这些命令填充了表格:

\set test = 'juego_Tipo1.xml'
INSERT INTO test(blah) values (:'test');

注意:“juego_Tipo1.xml”应该是当前目录中存在的如上所示内容的文件。

标签: xmlpostgresqlxpathpsql

解决方案


这个问题并不像乍看起来那么简单,至少对我来说是这样。以下是我理解的复制和解决它的过程,尽管可能是错误的。如果不正确,至少我希望这将是朝着正确方向迈出的第一步,或者为您提供一些解决方法的线索,因为在这个问题中您甚至没有尝试过。

1.- 在临时目录中创建输入数据。

mkdir -p /tmp/stackoverflow/{initdb,xmlfiles} && cd /tmp/stackoverflow

2.- 使用您喜欢的编辑器编辑 sql 文件:

vim initdb/setup.sql

并添加:

create table test(id serial primary key, blah xml);

insert into test(blah)
values
('/xmlfiles/juego_Tipo1.xml'),
('/xmlfiles/juego_Tipo2.xml');

create function file_with_correct_answer(file text)
returns table(filename text) as $$
begin
    file := quote_literal(file);

    create temp table xmldata (data text);
    create temp table t (filename text);
    execute 'insert into t (filename) values (' || file || ')';
    execute 'copy xmldata from ' || file;
    return query
       with t2(fn, linexml) as (select t.filename, string_agg(data, '')::xml from xmldata cross join t group by t.filename)
       select fn from t2 where xpath_exists('//respuesta[text() = "Respuesta A"]', linexml);
    drop table xmldata;
    drop table t;

end;
$$ language plpgsql;

3.- 创建两个xml文件来检查成功和失败的条件:

vim xmlfiles/juego_Tipo1.xml

有数据:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Gamificacion Id="1">
<titulo>NomTitulo</titulo>
<autor>NomAutor</autor>
<preguntas tipo="Tipo1">
<pregunta EmpiezaPor="" comodin50="" id="preg_0" sol="A">
<temaPregunta/>
<enunciado numLineas="3">Este es el enunciado 1</enunciado>
<respuesta num="1" numLetras="5" numLineas="1">Respuesta A</respuesta>
<respuesta num="2" numLetras="5" numLineas="1">Respuesta B</respuesta>
<respuesta num="3" numLetras="5" numLineas="1">Respuesta C</respuesta>
<respuesta num="4" numLetras="5" numLineas="1">Respuesta D</respuesta>
<pista id="1" numLineas="numLineas"/>
</pregunta>
<pregunta EmpiezaPor="" comodin50="" id="preg_1" sol="B">
<temaPregunta/>
<enunciado numLineas="3">Enunciado 2</enunciado>
<respuesta num="1" numLetras="5" numLineas="1">Respuesta A 2 </respuesta>
<respuesta num="2" numLetras="5" numLineas="1">Respuesta B 2</respuesta>
<respuesta num="3" numLetras="5" numLineas="1">Respuesta C 2</respuesta>
<respuesta num="4" numLetras="5" numLineas="1">Respuesta D 2</respuesta>
<pista id="1" numLineas="numLineas"/></pregunta></preguntas></Gamificacion>

vim xmlfiles/juego_Tipo2.xml

有数据:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Gamificacion Id="1">
<titulo>NomTitulo</titulo>
<autor>NomAutor</autor>
<preguntas tipo="Tipo1">
<pregunta EmpiezaPor="" comodin50="" id="preg_0" sol="A">
<temaPregunta/>
<enunciado numLineas="3">Este es el enunciado 1</enunciado>
<respuesta num="1" numLetras="5" numLineas="1">Respuesta E</respuesta>
<respuesta num="2" numLetras="5" numLineas="1">Respuesta B</respuesta>
<respuesta num="3" numLetras="5" numLineas="1">Respuesta C</respuesta>
<respuesta num="4" numLetras="5" numLineas="1">Respuesta D</respuesta>
<pista id="1" numLineas="numLineas"/>
</pregunta>
<pregunta EmpiezaPor="" comodin50="" id="preg_1" sol="B">
<temaPregunta/>
<enunciado numLineas="3">Enunciado 2</enunciado>
<respuesta num="1" numLetras="5" numLineas="1">Respuesta A 2 </respuesta>
<respuesta num="2" numLetras="5" numLineas="1">Respuesta B 2</respuesta>
<respuesta num="3" numLetras="5" numLineas="1">Respuesta C 2</respuesta>
<respuesta num="4" numLetras="5" numLineas="1">Respuesta D 2</respuesta>
<pista id="1" numLineas="numLineas"/></pregunta></preguntas></Gamificacion>

4.- 为 Postgresql 使用 docker 容器:

docker container run --rm -it \
--name stack-overflow \
-p 5432:5432 \
-e POSTGRES_PASSWORD=black \
-v /tmp/stackoverflow/xmlfiles:/xmlfiles \
-v /tmp/stackoverflow/initdb:/docker-entrypoint-initdb.d \
postgres

5.- 在容器中使用 psql:

docker run -it --rm --link stack-overflow:postgres postgres \
psql -h postgres -U postgres

6.- 与客户端执行查询(blah字段应该是text类型)psql

select file_with_correct_answer(cast(blah as text)) from test;

产生:

 file_with_correct_answer  
---------------------------
 /xmlfiles/juego_Tipo1.xml
(1 row)

推荐阅读