Wikidata SPARQL如何实现单值取原值多值取指定限定词值
问题
是否可以在Wikidata SPARQL中实现如下逻辑:
# 伪代码 if 某属性仅有1个值: 直接返回该值 else: 返回带子属性x限定的值
背景与现有尝试
需求场景为通过Wikidata获取瑞典议会议员的given name,用于选举相关数据可视化,目标是为每位议员返回单个given name:
- 单名示例:人物条目下P735(given name)属性仅挂载1个名字值,无额外角色标记
- 多名示例:人物条目下P735属性挂载3个名字,其中1个名字携带
object has role属性,属性值为usual first name(常用名),其余名字无该标记
现有两段查询均无法满足需求:
- 直接查询P735属性的版本会为每个given name返回一行结果,多名字人物会返回多行重复的人物记录:
SELECT ?personLabel ?givenNamesLabel WHERE { ?person wdt:P39 wd:Q10655178 . # 匹配担任瑞典议会议员职位的人物 ?person wdt:P735 ?givenNames . # 匹配人物的所有given name SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". } }
- 仅筛选带
usual first name角色标记名字的版本,会遗漏只有单个given name、无角色标记的人物:
SELECT ?personLabel ?givenNameSingularLabel WHERE { ?person wdt:P39 wd:Q10655178 . # 匹配担任瑞典议会议员职位的人物 ?person p:P735 ?givenNames . # 定位P735属性的声明节点 ?givenNames ps:P735 ?givenNameSingular . # 取声明对应的given name值 ?givenNames pq:P3831 wd:Q3409033 . # 筛选声明带"常用名"角色限定 SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". } }
实现方案
可以通过计数聚合+优先级匹配的逻辑实现需求,核心思路:
- 先统计每个人物挂载的given name总数
- 为两类名字分配优先级:带
usual first name标记的名字优先级为1,无标记的普通名字优先级为2 - 单名人物直接返回唯一的名字值,多名人物优先返回带常用名标记的值
- 可按需增加兜底逻辑,覆盖多名人物未标注常用名的边缘场景,确保无空值
可直接运行的基础版本查询代码如下:
SELECT ?personLabel ?finalGivenNameLabel WHERE { # 匹配所有瑞典议会议员 ?person wdt:P39 wd:Q10655178 . # 统计每个人物的given name总数 { SELECT ?person (COUNT(?allGiven) AS ?givenNameCount) WHERE { ?person wdt:P735 ?allGiven . } GROUP BY ?person } # 匹配所有given name,计算优先级 ?person p:P735 ?nameStmt . ?nameStmt ps:P735 ?finalGivenName . OPTIONAL { ?nameStmt pq:P3831 wd:Q3409033 . BIND(1 AS ?priority) } BIND(COALESCE(?priority, 2) AS ?namePriority) # 按规则筛选符合要求的名字 FILTER( ?givenNameCount = 1 || (?givenNameCount > 1 && ?namePriority = 1) ) SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],sv,en". } }
若需要覆盖「多名字但未标注常用名」的边缘场景,可将上述筛选逻辑替换为按人物分组取最高优先级名字的逻辑,确保每个人物一定返回1个结果,调整后的核心逻辑段如下:
# 替换原代码中的优先级匹配+筛选段 { SELECT ?person ?finalGivenName (MIN(?namePriority) AS ?topPriority) WHERE { ?person p:P735 ?nameStmt . ?nameStmt ps:P735 ?finalGivenName . OPTIONAL { ?nameStmt pq:P3831 wd:Q3409033 . BIND(1 AS ?priority) } BIND(COALESCE(?priority, 2) AS ?namePriority) } GROUP BY ?person ?finalGivenName } FILTER(?namePriority = ?topPriority)
内容的提问来源于stack exchange,提问作者David Van Dorsten
相关产品推荐
相关产品推荐

