在 PostgreSQL 中搜索 XML 个文档
Searching inside XML documents in PostgreSQL
所以我目前有一个非常简单的 table 使用 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”。如果是这样,我想 return 满足该条件的一个或多个文件的名称。
我尝试使用 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。
我使用此命令创建了 table:
create table test(id serial primary key, blah xml);
并且我使用这些命令填充了 table:
\set test = 'juego_Tipo1.xml'
INSERT INTO test(blah) values (:'test');
注意:“juego_Tipo1.xml”应该是当前目录下存在的具有上述内容的文件。
这个问题并不像乍看起来那么简单,至少对我来说是这样。以下是我理解的复制和解决它的过程,尽管可能是错误的。如果不正确,至少我希望这将是朝着正确方向迈出的第一步,或者为您提供一些解决方法的线索,因为在这个问题中您甚至没有尝试过。
1.- 在临时目录中创建输入数据。
mkdir -p /tmp/Whosebug/{initdb,xmlfiles} && cd /tmp/Whosebug
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/Whosebug/xmlfiles:/xmlfiles \
-v /tmp/Whosebug/initdb:/docker-entrypoint-initdb.d \
postgres
5.- 使用psql进入容器:
docker run -it --rm --link stack-overflow:postgres postgres \
psql -h postgres -U postgres
6.- 使用 psql
客户端执行查询(blah
字段应该是 text
类型):
select file_with_correct_answer(cast(blah as text)) from test;
产生:
file_with_correct_answer
---------------------------
/xmlfiles/juego_Tipo1.xml
(1 row)
所以我目前有一个非常简单的 table 使用 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”。如果是这样,我想 return 满足该条件的一个或多个文件的名称。
我尝试使用 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。
我使用此命令创建了 table:
create table test(id serial primary key, blah xml);
并且我使用这些命令填充了 table:
\set test = 'juego_Tipo1.xml'
INSERT INTO test(blah) values (:'test');
注意:“juego_Tipo1.xml”应该是当前目录下存在的具有上述内容的文件。
这个问题并不像乍看起来那么简单,至少对我来说是这样。以下是我理解的复制和解决它的过程,尽管可能是错误的。如果不正确,至少我希望这将是朝着正确方向迈出的第一步,或者为您提供一些解决方法的线索,因为在这个问题中您甚至没有尝试过。
1.- 在临时目录中创建输入数据。
mkdir -p /tmp/Whosebug/{initdb,xmlfiles} && cd /tmp/Whosebug
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/Whosebug/xmlfiles:/xmlfiles \
-v /tmp/Whosebug/initdb:/docker-entrypoint-initdb.d \
postgres
5.- 使用psql进入容器:
docker run -it --rm --link stack-overflow:postgres postgres \
psql -h postgres -U postgres
6.- 使用 psql
客户端执行查询(blah
字段应该是 text
类型):
select file_with_correct_answer(cast(blah as text)) from test;
产生:
file_with_correct_answer
---------------------------
/xmlfiles/juego_Tipo1.xml
(1 row)