diff --git a/examples/sample.ttl b/examples/sample.ttl new file mode 100644 index 000000000..9596b083b --- /dev/null +++ b/examples/sample.ttl @@ -0,0 +1,78 @@ +@prefix rdf: . +@prefix owl: . +@prefix prop: . +@prefix xsd: . +@prefix rdfs: . +@prefix netflix: . +@prefix class: . + +prop:isListedIn rdfs:domain class:ShowId ; + rdfs:range class:Genre . + +prop:isOfType rdfs:domain class:ShowId ; + rdfs:range class:Movie , class:TvShow . + +class:TvShow a rdfs:Class . + +class:Year a rdfs:Class ; + rdfs:subClassOf class:Date . + +class:Movie a rdfs:Class . + +prop:hasTitle rdfs:domain class:ShowId ; + rdfs:range xsd:String . + +prop:hasCast rdfs:domain class:ShowId ; + rdfs:range class:Cast . + +class:Duration a rdfs:Class . + +class:ShowId a rdfs:Class . + +class:Person a rdfs:Class . + +class:Rating a rdfs:Class . + +prop:hasDirector rdfs:domain class:ShowId ; + rdfs:range class:Director . + +class:Director a rdfs:Class ; + rdfs:subClassOf class:Person . + +class:Genre a rdfs:Class . + +class:Country a rdfs:Class . + +class:ReleaseYr a rdfs:Class ; + rdfs:subClassOf class:Year . + +prop:releasedIn rdfs:domain class:ShowId ; + rdfs:range class:ReleaseYr . + +class:Cast a rdfs:Class ; + rdfs:subClassOf class:Person . + +prop:hasDescription rdfs:domain class:ShowId ; + rdfs:range xsd:String . + +class:Date a rdfs:Class . + +prop:madeInCountry rdfs:domain class:ShowId ; + rdfs:range class:Country . + +<1> prop:hasTitle . + +prop:wasAddedOn rdfs:domain class:ShowId ; + rdfs:range class:Date . + +owl:st rdf:type rdf:Statement; + rdf:subject owl:Par ; + rdf:predicate prop:livesInCountry ; + rdf:object . +owl:st prop:releasedIn <2016> . + +owl:Par rdf:type rdf:Statement; + rdf:subject ; + rdf:predicate prop:about ; + rdf:object . + diff --git a/examples/sparql_star_recursive.py b/examples/sparql_star_recursive.py new file mode 100644 index 000000000..864f24d9d --- /dev/null +++ b/examples/sparql_star_recursive.py @@ -0,0 +1,19 @@ +""" + +A simple example showing how to process RDFa from the web + +""" + +from rdflib import Graph + +if __name__ == '__main__': + g = Graph() + + g.parse('sample.ttl', format='turtle') + + print("Books found:") + + res = g.query("SELECT ?s1 WHERE{<<<> ?p2 ?o2>> ?p3 ?o3.}") + print(list(res)) + + diff --git a/rdflib/plugins/sparql/evaluate.py b/rdflib/plugins/sparql/evaluate.py index 589296a3b..f75319d5f 100644 --- a/rdflib/plugins/sparql/evaluate.py +++ b/rdflib/plugins/sparql/evaluate.py @@ -118,29 +118,38 @@ def evalUnion(ctx, union): for x in evalPart(ctx, union.p2): yield x -def _convert_embedded_triple(reif, t, v): - if t[0] == reif and t[2] == reif: - return v, t[1], v - elif t[0] == reif: - return v, t[1], t[2] - elif t[2] == reif: - return t[0], t[1], v - else: - return t +def _convert_embedded_triple(t): + + if isinstance(t[0], EmbeddedTriple) and isinstance(t[2], EmbeddedTriple): + v1 = Variable('__' + t[0].toPython()) + v2 = Variable('__' + t[2].toPython()) + return v1, t[1], v2 + + if isinstance(t[0], EmbeddedTriple): + v1 = Variable('__' + t[0].toPython()) #Replacing subject by a variable if Embedded Triple + return v1, t[1], t[2] + + if isinstance(t[2], EmbeddedTriple): + v3 = Variable('__' + t[2].toPython()) #Replacing object by a variable if Embedded Triple + return t[0], t[1], v3 # Embedded Triple Pattern -def reifyEmbTP(ctx, reif, part): +def reifyEmbTP(ctx, reif, triples): from rdflib import RDF v = Variable('__' + reif.toPython()) - triples = list() - for t in part.triples: - triples.append(_convert_embedded_triple(reif, t, v)) triples.append([v, RDF.type, RDF.Statement]) - triples.append([v, RDF.subject,reif.subject()]) triples.append([v, RDF.predicate, reif.predicate()]) - triples.append([v, RDF.object, reif.object()]) - part.triples = triples + if(isinstance(reif.subject() , EmbeddedTriple)): + triples.append([v, RDF.subject, Variable('__' +reif.subject().toPython())]) + else: + triples.append([v, RDF.subject,reif.subject()]) + + if (isinstance(reif.object(), EmbeddedTriple)): + triples.append([v, RDF.object, Variable('__' + reif.object().toPython())]) + else: + triples.append([v, RDF.object, reif.object()]) + def evalMinus(ctx, minus): a = evalPart(ctx, minus.p1) @@ -228,14 +237,25 @@ def evalMultiset(ctx, part): return evalPart(ctx, part.p) -def findSetOfEmbeddedTriples(ctx, part): - setOfEmbededTriples=set() - for t in part.triples: +def findSetOfEmbeddedTriples(ctx, triples,setOfEmbededTriples): + subTriples = [] + for t in triples: if isinstance(t[0], EmbeddedTriple) and t[0] not in setOfEmbededTriples: setOfEmbededTriples.add(t[0]) + + embTpl = [] + embTpl.extend([t[0].subject(),t[0].predicate(),t[0].object()]) + subTriples.append(embTpl) + if isinstance(t[2], EmbeddedTriple) and t[2] not in setOfEmbededTriples: setOfEmbededTriples.add(t[2]) - return setOfEmbededTriples + + embTpl = [] + embTpl.extend([t[2].subject(), t[2].predicate(), t[2].object()]) + subTriples.append(embTpl) + + if(len(subTriples)>0) : + findSetOfEmbeddedTriples(ctx, subTriples, setOfEmbededTriples) def evalPart(ctx, part): @@ -247,14 +267,19 @@ def evalPart(ctx, part): pass # the given custome-function did not handle this part if part.name == 'BGP': - set_of_embeded_triples = findSetOfEmbeddedTriples(ctx, part) + set_of_embeded_triples = set() + findSetOfEmbeddedTriples(ctx, part.triples, set_of_embeded_triples) # Reorder triples patterns by number of bound nodes in the current ctx # Do patterns with more bound nodes first + triples = list() + for t in part.triples: + triples.append(_convert_embedded_triple(t)) if len(set_of_embeded_triples) != 0: for embTp in set_of_embeded_triples: - reifyEmbTP(ctx, embTp, part) + reifyEmbTP(ctx, embTp, triples) + part.triples = triples triples = sorted(part.triples, key=lambda t: len([n for n in t if ctx[n] is None])) return evalBGP(ctx, triples) elif part.name == 'Filter': @@ -488,4 +513,4 @@ def evalQuery(graph, query, initBindings, base=None): g = d.named ctx.load(g, default=False) - return evalPart(ctx, main) + return evalPart(ctx, main) \ No newline at end of file diff --git a/rdflib/plugins/sparql/parser.py b/rdflib/plugins/sparql/parser.py index 2900f7b28..721024210 100644 --- a/rdflib/plugins/sparql/parser.py +++ b/rdflib/plugins/sparql/parser.py @@ -411,13 +411,13 @@ def _hexExpand(match): 'DEFAULT')) | Optional(Keyword('GRAPH')) + ParamList('graph', iri) # Inside a values clause the EmbeddedTriple must be fully resolvable. -KnownEmbTP = Suppress('<<') + iri + (iri | A) + (iri | RDFLiteral | NumericLiteral | BooleanLiteral) + Suppress('>>') +KnownEmbTP = Forward() +KnownEmbTP <<= Suppress('<<') + (iri | KnownEmbTP) + (iri | A) + (iri | RDFLiteral | NumericLiteral | BooleanLiteral | KnownEmbTP) + Suppress('>>') KnownEmbTP.setParseAction(lambda x: rdflib.EmbeddedTriple(subject=x[0], predicate=x[1], object=x[2])) - # [65] DataBlockValue ::= iri | RDFLiteral | NumericLiteral | BooleanLiteral | 'UNDEF' DataBlockValue = iri | RDFLiteral | NumericLiteral | BooleanLiteral | Keyword('UNDEF') | KnownEmbTP @@ -439,16 +439,16 @@ def _hexExpand(match): GraphNode = VarOrTerm | TriplesNode #Should be recursive but it is not yet so -#VarOrBlankNodeOrIriOrLitOrEmbTP = Forward() -#VarOrBlankNodeOrIriOrLitOrEmbTP <<= Var | BlankNode | iri | RDFLiteral | NumericLiteral | BooleanLiteral | VarOrBlankNodeOrIriOrLitOrEmbTP +# VarOrBlankNodeOrIriOrLitOrEmbTP = Forward() +# VarOrBlankNodeOrIriOrLitOrEmbTP <<= Var | BlankNode | iri | RDFLiteral | NumericLiteral | BooleanLiteral | VarOrBlankNodeOrIriOrLitOrEmbTP VarOrBlankNodeOrIriOrLitOrEmbTP = Var | BlankNode | iri | RDFLiteral | NumericLiteral | BooleanLiteral -EmbTP = Suppress('<<') + VarOrBlankNodeOrIriOrLitOrEmbTP + Verb + VarOrBlankNodeOrIriOrLitOrEmbTP + Suppress('>>') +EmbTP = Forward() +EmbTP <<= Suppress('<<') + (VarOrBlankNodeOrIriOrLitOrEmbTP|EmbTP) + Verb + (VarOrBlankNodeOrIriOrLitOrEmbTP|EmbTP) + Suppress('>>') EmbTP.setParseAction(lambda x: rdflib.EmbeddedTriple(subject=x[0], predicate=x[1], - object=x[2]) - ) + object=x[2])) VarOrTermOrEmbTP = Var | GraphTerm | EmbTP