rdflib

时间:2017-05-15 10:05:51

标签: sparql rdflib

将自定义SPARQL函数挂钩到rdflib的好方法是什么?

我一直在rdflib中寻找自定义函数的入口点。我没有找到专门的入口点,但发现rdflib.plugins.sparql.CUSTOM_EVALS可能是添加自定义函数的地方。

到目前为止,我已尝试使用下面的代码。好像"脏"对我来说。我打电话给"隐藏"函数(_eval),我不确定我的所有参数更新是否正确。除了custom_eval.py示例代码(构成我的代码的基础)之外,我发现关于CUSTOM_EVALS的其他代码或文档很少。

import rdflib
from rdflib.plugins.sparql.evaluate import evalPart
from rdflib.plugins.sparql.sparql import SPARQLError
from rdflib.plugins.sparql.evalutils import _eval
from rdflib.namespace import Namespace
from rdflib.term import Literal

NAMESPACE = Namespace('//custom/')
LENGTH = rdflib.term.URIRef(NAMESPACE + 'length')

def customEval(ctx, part):
    """Evaluate custom function."""
    if part.name == 'Extend':
        cs = []
        for c in evalPart(ctx, part.p):
            if hasattr(part.expr, 'iri'):
                # A function
                argument = _eval(part.expr.expr[0], c.forget(ctx, _except=part.expr._vars))
                if part.expr.iri == LENGTH:
                    e = Literal(len(argument))
                else:
                    raise SPARQLError('Unhandled function {}'.format(part.expr.iri))
            else:
                e = _eval(part.expr, c.forget(ctx, _except=part._vars))
                if isinstance(e, SPARQLError):
                    raise e
            cs.append(c.merge({part.var: e}))
        return cs
    raise NotImplementedError()


QUERY = """
PREFIX custom: <%s>

SELECT ?s ?length WHERE {
  BIND("Hello, World" AS ?s)
  BIND(custom:length(?s) AS ?length)
}
""" % (NAMESPACE,)

rdflib.plugins.sparql.CUSTOM_EVALS['exampleEval'] = customEval
for row in rdflib.Graph().query(QUERY):
    print(row)

1 个答案:

答案 0 :(得分:1)

首先,我要感谢您展示了您如何实现新的 SPARQL 函数。

其次,通过使用您的代码,我能够创建一个 SPARQL 函数,该函数使用 Levenshtein 距离计算两个字符串。它非常有见地,我希望分享它,因为它包含可以帮助其他开发人员创建自己的自定义 SPARQL 函数的其他文档。

# Import needed to introduce new SPARQL function
import rdflib
from rdflib.plugins.sparql.evaluate import evalPart
from rdflib.plugins.sparql.sparql import SPARQLError
from rdflib.plugins.sparql.evalutils import _eval
from rdflib.namespace import Namespace
from rdflib.term import Literal

# Import for custom function calculation
from Levenshtein import distance as levenshtein_distance # python-Levenshtein==0.12.2



def SPARQL_levenshtein(ctx:object, part:object) -> object:
    """
    The first two variables retrieved from a SPARQL-query are compared using the Levenshtein distance.
    The distance value is then stored in Literal object and added to the query results.
    
    Example:

    Query:
        PREFIX custom: //custom/      # Note: this part refereces to the custom function

        SELECT ?label1 ?label2 ?levenshtein WHERE {
          BIND("Hello" AS ?label1)
          BIND("World" AS ?label2)
          BIND(custom:levenshtein(?label1, ?label2) AS ?levenshtein)
        }

    Retrieve:
        ?label1 ?label2

    Calculation:
        levenshtein_distance(?label1, ?label2) =  distance

    Output:
        Save distance in Literal object.

    :param ctx:     <class 'rdflib.plugins.sparql.sparql.QueryContext'>
    :param part:    <class 'rdflib.plugins.sparql.parserutils.CompValue'>
    :return:        <class 'rdflib.plugins.sparql.processor.SPARQLResult'>
    """

    # This part holds basic implementation for adding new functions
    if part.name == 'Extend':
        cs = []

        # Information is retrieved and stored and passed through a generator
        for c in evalPart(ctx, part.p):

            # Checks if the function holds an internationalized resource identifier
            # This will check if any custom functions are added.
            if hasattr(part.expr, 'iri'):

                # From here the real calculations begin.
                # First we get the variable arguments, for example ?label1 and ?label2
                argument1 = str(_eval(part.expr.expr[0], c.forget(ctx, _except=part.expr._vars)))
                argument2 = str(_eval(part.expr.expr[1], c.forget(ctx, _except=part.expr._vars)))

                # Here it checks if it can find our levenshtein IRI (example: //custom/levenshtein)
                # Please note that IRI and URI are almost the same.
                # Earlier this has been defined with the following:
                    # namespace = Namespace('//custom/')
                    # levenshtein = rdflib.term.URIRef(namespace + 'levenshtein')

                if part.expr.iri == levenshtein:

                    # After finding the correct path for the custom SPARQL function the evaluation can begin.
                    # Here the levenshtein distance is calculated using ?label1 and ?label2 and stored as an Literal object.
                    # This object is than stored as an output value of the SPARQL-query (example: ?levenshtein)
                    evaluation = Literal(levenshtein_distance(argument1, argument2))


    # Standard error handling and return statements
                else:
                    raise SPARQLError('Unhandled function {}'.format(part.expr.iri))
            else:
                evaluation = _eval(part.expr, c.forget(ctx, _except=part._vars))
                if isinstance(evaluation, SPARQLError):
                    raise evaluation
            cs.append(c.merge({part.var: evaluation}))
        return cs
    raise NotImplementedError()


namespace = Namespace('//custom/')
levenshtein = rdflib.term.URIRef(namespace + 'levenshtein')


query = """
PREFIX custom: <%s>

SELECT ?label1 ?label2 ?levenshtein WHERE {
  BIND("Hello" AS ?label1)
  BIND("World" AS ?label2)
  BIND(custom:levenshtein(?label1, ?label2) AS ?levenshtein)
}
""" % (namespace,)

# Save custom function in custom evaluation dictionary.
rdflib.plugins.sparql.CUSTOM_EVALS['SPARQL_levenshtein'] = SPARQL_levenshtein


for row in rdflib.Graph().query(query):
    print(row)

回答您的问题:“将自定义 SPARQL 函数挂接到 rdflib 的好方法是什么?

目前我正在开发一个处理 RDF 数据的类,我认为最好在 __init__ 函数中实现以下代码。

例如:

class ClassName():
    """DOCSTRING"""

    def __init__(self):
        """DOCSTRING"""
        # Save custom function in custom evaluation dictionary.
        rdflib.plugins.sparql.CUSTOM_EVALS['SPARQL_levenshtein'] = SPARQL_levenshtein 

请注意,此 SPARQL 函数仅适用于实现它的端点。即使查询中的 SPARQL 语法是正确的,也不可能在用于 DBPedia 等数据库的 SPARQL 查询中应用该函数。 DBPedia 端点(尚)不支持此自定义函数。