Re: Filter scope and optionals

Seaborne, Andy wrote:
>
>
>
> Jorge Pérez wrote:
>> Hi Andy,
>>
>>> If the proposed filter scoping rule is taken naively, there isn't a way
>>> to
>>> get
>>> value testing conditions into the left join conditions for optional
>>> (unlike
>>> the join conditions for a group).
>>>
>>> Here's an example:
>>>
>>> ---- Data
>>> @prefix   :         <http://example/> .
>>>
>>> :x1 :p 1 .
>>> :x2 :p 2 .
>>>
>>> :x3 :q 3 .
>>> :x3 :q 4 .
>>> ---- Data
>>>
>>>
>>> ---- Query
>>> PREFIX :    <http://example/>
>>>
>>> SELECT *
>>> {
>>>    ?x :p ?v .
>>>    OPTIONAL
>>>    {
>>>      ?y :q ?w .
>>>      FILTER(?v=2)
>>>    }
>>> }
>>> ---- Query
>>
>> I personally think that this kind of queries are interesting from the
>> user
>> point of view, but because they need a *dynamic scope* evaluation (I
>> really don't know how to call it, but I think you understand) the
>> language
>> looses the compositionality. What I mean is that the *expression*
>>
>> {
>>   ?y :q ?w .
>>   FILTER(?v=2)
>> }
>>
>> has different meanings depending on where it appears on the query. Here
>> you are discussing the appearance of it in the right hand side of an
>> OPTIONAL, but it would appear in a parallel group, in a UNION, inside a
>> GRAPH, etc. Also here the expression in the filter is very simple but I
>> think that with bound\!bound things could be more complex.
>>
>>>
>>> ---- Results 1
>>> ---------------------
>>> | x   | v | y   | w |
>>> ====================>> | :x2 | 2 | :x3 | 4 |
>>> | :x2 | 2 | :x3 | 3 |
>>> | :x1 | 1 |     |   |
>>> ---------------------
>>>
>>> ---- Results 2
>>> -------------------
>>> | x   | v | y | w |
>>> ==================>> | :x2 | 2 |   |   |
>>> | :x1 | 1 |   |   |
>>> -------------------
>>>
>>> Results 1 is what the current doc (declarative form) gets - and would
>>> be
>>> the
>>> same if the FILTER were part of the left outer join conditions.
>>>
>>> Results 2 is what a purely filter-scoped-to-group gets because the
>>> optional
>>> part never matches because ?v is an unbound variable.
>>>
>>>
>>> I can't see any way of getting filter into the scope of the left outer
>>> join.
>>> The only way I can see of rewriting this query is to use a UNION and
>>> repeat
>>> the fixed pattern on each side of the union, one side including the
>>> filter
>>> and
>>>   optional triple pattern. That query is going to give different
>>> answers
>>> and
>>> the app will have to post-process the results to find out what went on.
>>
>> And the following query?
>>
>> SELECT *
>> {
>>    ?x :p ?v .
>>    OPTIONAL
>>    {
>>      ?x :p ?v .
>>      ?y :q ?w .
>>      FILTER(?v=2)
>>    }
>> }
>>
>> I think (but I do not have a proof of it), that giving a dinamic scope
>> for
>> filters in optionals does not increase the expressiveness of the
>> language
>> compared with the compositional-algebraic-operations approach, and one
>> can
>> always obtain the same result repeating the pattern inside the optional
>> (as in the above query).
>
> It may not change the expressivity but it isn't convenient for the
> application
> writer.  The example here has one triple pattern - in a realistic query,
> it
> would be 10's of triple patterns.  Repeating it would be a significant
> burden
> and potentially error prone, for example if the repeated pattern needs
> changing.
>
> It does suggest an approach though, which is to define optional, not
> directly
> as a left join operation but to do that repetition:
>
> OPT(A,B) = LeftJoin(A, Join(A, B))
>
> but filters need to be handled so that itself does not work.
>
> I haven't worked though properly but an idea:
>
> Write Group(P,F) where P a pattern, F is a filter restriction,
> scope filters over the evaluation of the pattern:
> then Group(P,F) = F(P) = [[ [[P]] FILTER F ]]
>
> so:
>
> OPT(Group(P1, F1), Group(P2,F2))      LeftJoin(F1(P1), F2( Join(F1(P1),
> P2) ))
>
> and F2 can mention variables in P1
>
> which is  F1(P1) LeftJoin (F1(P1) Join P2) ON (F2 and matching vars)
> (in a vaguely SQL-ish notation)
>
> which looks like it might be  F1(P1) LeftJoin P2 ON (F2 and matching vars)
>
> If F2 only mentions variables from P2 and not P1, then it can be moved in
> to
> apply directly to P2.
>
> If F2 mentions a variable x from P1 and P2, then x must have the same
> value in
> each so its the same as applying F2 to P2 alone.
>
> (Caveat - design-by-email.  Further work and testing needed!)
>
>
>>
>>> Intuitively, the scope rule needs to be modified to put the filter just
>>> outside the group, allowing it to be part of the left outer join, and
>>> not
>>> just
>>> inside the group.
>>
>> I think it is not so simple, because you can have patterns like
>>
>> { ?x :p ?v
>>   OPTIONAL {
>>      ?y :p ?w
>>      OPTIONAL {
>>         ?z :q ?u .
>>         FILTER (?v=2) } } }
>>
>> and then the problem affect the semantics of nested optionals too.
>
> I wasn't thinking of extending the scoping to allow this.  I don't see it
> happening for real nearly as much as the one level form above.
>
> By allowing the scope to be just outside the containing optional part, the
> FILTER in the innermost optional does not have access to the outermost "?x
> :p
> ?v" and ?v is unbound for the restriction.

That is why I think it would be strange, why the scope is only the
containing optional part, and not all the *path of optionals*? and what
happend with parallel optionals, with UNION, with groups? an example

{
  { ?x :p ?v .
      { ?y :p ?w . FILTER (?v = 2) }
  }
}

I think it would be a bad design to change the scope of filter depending
on where and how they are used. I think it would be strange for users and
for developers (even in the case that it just change for one level of
OPTIONAL).

What I've called *dynamic* scope for filter is interesting, intuitivelly
the scope of the filter is inherited from the containing groups. The
problem is that it is incompatible with a pure algebraic approach.

- jorge

>
>
>>
>>> Any alternatives?
>>> How do we formalize this?
>>
>> One approach is to formalize OPTIONAL + FILTER in a navigational way (as
>> the current spec).
>> Another approach to formalize the dinamic scoping (without loosing
>> compositionality) is to think that the filter is *generating bindings*,
>> for example from
>>
>> {
>>   ?y :q ?w .
>>   FILTER(?v=2)
>> }
>>
>> results the solutions for ?y :q ?w defined also in variable ?v but with
>> fixed value 2 for ?v. What I do not like of this approach is that FILTER
>> is not filtering, it is *forcing* some bindings, and formal definitions
>> would become complex in the case of restrictions like ?x = ?y, or
>> restrictions with negations.
>
> I don't like this either (FILTER(?v < 3) for example) but it does show the
> asymmetry here: variables mentioned in the pattern play a part in the
> left-join condition but variables mentioned in the filter don't.
>
>>
>> I'm personally in favor of the Results 2 above (filter-scoped-to-group).
>>
>> - jorge
>>
>>
>>> 	Andy
>
> Thanks for the prompt comments
>
> 	Andy
>
>>>
>>>
>>
>>
>
>
>

Received on Tuesday, 31 October 2006 15:15:31 UTC