I did a quick successful test with the data and query below.

It wasn't sure whether the service enhancer's substitution approach would work with FILTER (NOT) EXISTS because IIRC I didn't write tests for this case, but it seems to work as expected:


Data - One product 'a' with 2 categories, of which cat1 has the feature  f2 which should cause it to be excluded:

```sparql

INSERT DATA {
  <urn:a> <urn:hasCategory> <urn:cat1> .
  <urn:cat1> <urn:hasFeature> <urn:f1> .
  <urn:cat1> <urn:hasFeature> <urn:f2> .

  <urn:a> <urn:hasCategory> <urn:cat2> .
  <urn:cat2> <urn:hasFeature> <urn:f3> .
  <urn:cat2> <urn:hasFeature> <urn:f4> .
}

```


Query using SERVICE <loop:>:

```sparql

SELECT  *
WHERE
{
  VALUES (?product ?feature) { (<urn:a> <urn:f2>) }
  SERVICE <loop:> {
    SELECT ?category {
      ?product <urn:hasCategory> ?category .
      FILTER NOT EXISTS { ?category <urn:hasFeature> ?feature }
    }
  }
}

```


Note, that you can use SERVICE <loop:bulk+N:> to batch N bindings into a single request.


Output is the 'cat2' which does not have feature 'f2':


```

(?product = <urn:a>, ?feature = <urn:f2>, ?category = <urn:cat2>)
```


The substituted queries ignores the standard scoping rules and thus looks like:

```sparql

SELECT  *
WHERE
  {   { { SELECT  ?category
          WHERE
            { <urn:a>  <urn:hasCategory>  ?category
              FILTER NOT EXISTS { ?category <urn:hasFeature>  <urn:f2> }
            }
        }
        BIND(0 AS ?__idx__)
      }
    UNION
      { BIND(1000000000 AS ?__idx__) }
  }

```


As a side note, I have an maintenance update of the service enhancer plugin in the works with a some smaller fixes and improvements (e.g. improved cancellation handling based on recent changes I contributed to ARQ) of the existing system and and hope to have the PR ready during December.

I will also add FILTER (NOT) EXISTS this test case to the test suite.


Cheers,

Claus



On 27.11.24 20:13, Andy Seaborne wrote:


On 26/11/2024 08:56, Martynas Jusevičius wrote:
Hi,

I have two Fuseki 4.6.1 instances in a Docker network (fuseki-admin
and fuseki-end-user) that federate SPARQL queries between each other.

If you want to provide detailed control of SERVICE, then there is

https://jena.apache.org/documentation/query/service_enhancer.html



When I execute this query on fuseki-admin:

PREFIX  rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX  acl:  <http://www.w3.org/ns/auth/acl#>
PREFIX  sioc: <http://rdfs.org/sioc/ns#>

SELECT  *
FROM <urn:x-arq:UnionGraph>
WHERE
   { VALUES ( ?this ?Container ) {
       ( <https://localhost:4443/whateverest/> <https://localhost:4443/> )
     }
     SERVICE <http://fuseki-end-user:3030/ds/>
       { GRAPH ?Container
           { ?Container  a  ?Type }
       }
   }

then I can see the ?Container binding was injected into the query
string executed on fuseki-end-user:

Query = SELECT  * WHERE   { GRAPH <https://localhost:4443/>       {
<https://localhost:4443/>                   a  ?Type }   }

However when I add FILTER NOT EXISTS to the federated part

PREFIX  rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX  acl:  <http://www.w3.org/ns/auth/acl#>
PREFIX  sioc: <http://rdfs.org/sioc/ns#>

SELECT  *
FROM <urn:x-arq:UnionGraph>
WHERE
   { VALUES (?this ?Container) { (<https://localhost:4443/whateverest/>
<https://localhost:4443/>) }
     SERVICE <http://fuseki-end-user:3030/ds/>
       {  GRAPH ?Container
               {
                 ?Container  a  ?Type
               }
             FILTER NOT EXISTS { GRAPH ?this
                                   { ?this
sioc:has_parent|sioc:has_container  ?Container }
                               }
       }
   }

Without the service enhancer, there is no rewrite and it is a has join:

(join
   VALUES
   SERVICE
)

otherwise it can become a denial of service vector!

With control such as correlated joins:
https://jena.apache.org/documentation/query/service_enhancer.html#correlated-joins

you may be able to get the effect you want.

The ?this case is changing the meaning of the query -- ?this is not in-scope in the NOT EXISTS so it is undefined.

    Andy


I see a single request in fuseki-end-user but none of the variables
are bound to anything:

Query =  Query = SELECT  * WHERE   { GRAPH ?Container       {
?Container  a  ?Type }     FILTER NOT EXISTS { GRAPH ?this
               { ?this
<http://rdfs.org/sioc/ns#has_parent>|<http://rdfs.org/sioc/ns#has_container>
?Container }

This looks plain wrong because without bindings it eliminates too many
(positive) results, and the final result on fuseki-end-user comes out
empty.

Is this a bug or am I misunderstanding something about VALUES and/or SERVICE?

Martynas

Reply via email to