使用空白节点时,Sparql 查询返回不需要的结果 (rdflib)

Sparql query returns undesired results when using blank nodes (rdflib)

提问人:GabrielGodefroy 提问时间:11/3/2023 最后编辑:GabrielGodefroy 更新时间:11/4/2023 访问量:58

问:

我使用 python 库对联系人图进行建模,并执行查询以检索谁知道谁。当人们添加为 时,这工作正常,但在使用 时则不行。rdflibsparqlURIRefBNode

示例图可以表示如下:

bob   - knows -> linda
alice - knows -> linda
tom   - knows -> linda
        knows -> bob

只有汤姆认识鲍勃,没有人认识汤姆。

我执行以下 2 个查询:

  1. 第一个找回汤姆的人;它按预期工作。
  2. 在第二个查询中,我使用 Tom 节点 ID 来检索谁认识他。我期待一个空列表。当 Tom 添加为 时,它会按预期工作。但是,当 Tom 添加为 时,此查询将返回 3 个名称!URIRefBNode

use_blank_node = True # switch to see the undesired behavior happens only with blank node

pred_knows = URIRef("http://example.org/knows")
pred_named = URIRef("http://example.org/named")

def create_graph() -> Graph:
    graph = Graph()

    bob = URIRef("http://example.org/people/Bob")
    linda = BNode()  # a GUID is generated
    alice = BNode()
    tom = BNode() if use_blank_node else URIRef("http://example.org/people/Tom")
    print(f"{str(tom)=}")
    remy = BNode()

    graph.add((bob, pred_named, Literal("Bob")))
    graph.add((alice, pred_named, Literal("Alice")))
    graph.add((tom, pred_named, Literal("Tom")))
    graph.add((linda, pred_named, Literal("Linda")))
    graph.add((remy, pred_named, Literal("Remy")))

    graph.add((bob, pred_knows, linda))
    graph.add((alice, pred_knows, linda))
    graph.add((tom, pred_knows, linda))
    graph.add((tom, pred_knows, bob))

    return graph


find_tom_who_knows_bob_query = f"""SELECT DISTINCT ?knowsbob ?nameofwhoknowsbob
WHERE 
{{ ?knowsbob <{pred_knows}> <http://example.org/people/Bob> ;
             <{pred_named}> ?nameofwhoknowsbob . 
 }}"""


def find_who_know_tom(tom_id) -> str:
    tom_query = f"_:{tom_id}" if type(tom_id) is BNode else f"<{tom_id}>"

    return f"""SELECT DISTINCT ?nameOfWhoKnowsTom
    WHERE 
    {{ ?iriOfWhoKnowsTom  <{pred_knows}> {tom_query} ;
                          <{pred_named}> ?nameOfWhoKnowsTom}}"""


def main():
    graph = create_graph()
    print("=" * 60, "\n", graph.serialize(), "\n", "=" * 60)

    result = list(graph.query(find_tom_who_knows_bob_query))
    assert len(result) == 1 and len(result[0]) == 2
    tom_id = result[0][0]
    print(f"{str(tom_id)=}")
    assert (type(tom_id) == BNode and use_blank_node) or (type(tom_id) == URIRef and use_blank_node is False)
    assert str(result[0][1]) == "Tom"

    query = find_who_know_tom(tom_id)
    print(query)
    result = list(graph.query(query))
    print(
        "They know Tom:", ", ".join([str(r[0]) for r in result])
    )  # why is it not empty when use_blank_node = True
    # prints: "They know Tom: Bob, Alice, Tom"


if __name__ == "__main__":
    main()


我的问题:如何正确使用sparql,以便查询也适用于空白节点?

python sparql rdf rdflib 空白节点

评论


答:

2赞 Stanislav Kralin 11/3/2023 #1

空白节点类似于自由变量。从 SPARQL 1.1 查询语言

应用程序编写者不应期望查询中有空白节点标签 以引用数据中的特定空白节点。

您的第二个查询,而不是

SELECT ?nameOfWhoKnowsTom WHERE {
   ?iriOfWhoKnowsTom ex:knows _:N6fb3b031995c43cfbf3e257ec0c0eac0 ;
                     ex:named ?nameOfWhoKnowsTom .
}

应该是这样的:

SELECT ?nameOfWhoKnowsTom WHERE {
   ?iriOfWhoKnowsTom ex:knows / ex:named "Tom" ;
                     ex:named ?nameOfWhoKnowsTom .
}

另请参阅 标记信息。