Andy,
I've run into a thorny problem to do with applying transformations to custom
algebra operators. Essentially we have a number of custom algebra operators
defined (which derived from OpExt) and in order to try and allow
optimisations to work correctly for these we override the apply() method and
create a new instance of the custom operator with the transformer applied to
the sub operators using the Transformer.transform() helper method.
This works great for bottom up transformations but for top down
transformations e.g transformation to quad form we've found this can result
in incorrect transformations. The following code is a trivial demonstration
of this problem:
package org.apache.jena.playground;
import org.apache.jena.atlas.io.IndentedWriter;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.NodeFactory;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.sparql.algebra.AlgebraQuad;
import com.hp.hpl.jena.sparql.algebra.Op;
import com.hp.hpl.jena.sparql.algebra.Transform;
import com.hp.hpl.jena.sparql.algebra.Transformer;
import com.hp.hpl.jena.sparql.algebra.op.OpBGP;
import com.hp.hpl.jena.sparql.algebra.op.OpExt;
import com.hp.hpl.jena.sparql.algebra.op.OpGraph;
import com.hp.hpl.jena.sparql.core.BasicPattern;
import com.hp.hpl.jena.sparql.engine.ExecutionContext;
import com.hp.hpl.jena.sparql.engine.QueryIterator;
import com.hp.hpl.jena.sparql.serializer.SerializationContext;
import com.hp.hpl.jena.sparql.util.NodeIsomorphismMap;
public class CustomOperatorTransformPassing {
public static void main(String[] args) {
Node customGraph = NodeFactory.createURI("http://graph");
Node s = NodeFactory.createVariable("s");
Node p = NodeFactory.createVariable("p");
Node o = NodeFactory.createVariable("o");
BasicPattern bp = new BasicPattern();
bp.add(new Triple(s, p, o));
OpBGP bgp = new OpBGP(bp);
OpGraph graph = new OpGraph(customGraph, bgp);
// Transform normal algebra to quads
Op quads = AlgebraQuad.quadize(graph);
System.out.println("Original Algebra:");
System.out.println(graph.toString());
System.out.println();
System.out.println("Quad Form Algebra:");
System.out.println(quads.toString());
System.out.println();
// Now wrap in custom algebra and repeat
Op foo = new OpFoo(graph);
quads = AlgebraQuad.quadize(foo);
System.out.println("Original Algebra:");
System.out.println(foo.toString());
System.out.println();
System.out.println("Quad Form Algebra:");
System.out.println(quads.toString());
System.out.println();
}
/**
* Trivial custom algebra operator which tries to allow transforms to
pass
* through it
*
*/
private static class OpFoo extends OpExt {
private Op subOp;
public OpFoo(Op subOp) {
super("foo");
this.subOp = subOp;
}
public Op getSubOp() {
return this.subOp;
}
@Override
public Op effectiveOp() {
return null;
}
@Override
public QueryIterator eval(QueryIterator input, ExecutionContext
execCxt) {
throw new UnsupportedOperationException();
}
@Override
public Op apply(Transform transform) {
// Apply transforms on inner operator so we don't block
transformations
return new OpFoo(Transformer.transform(transform, this.subOp));
}
@Override
public void outputArgs(IndentedWriter out, SerializationContext
sCxt) {
// Not needed as we override output directly
}
@Override
public void output(IndentedWriter out, SerializationContext sCxt) {
out.println("(foo");
out.incIndent();
subOp.output(out, sCxt);
out.decIndent();
out.write(")");
}
@Override
public int hashCode() {
return subOp.hashCode();
}
@Override
public boolean equalTo(Op other, NodeIsomorphismMap labelMap) {
if (other instanceof OpFoo) {
return this.subOp.equalTo(((OpFoo) other).getSubOp(),
labelMap);
}
return false;
}
}
}
This can also be found at
https://gist.github.com/rvesse/85aeac225b7907db1155 for when the mailing
list mangles the code
It produces the following output:
Original Algebra:
(graph <http://graph>
(bgp (triple ?s ?p ?o)))
Quad Form Algebra:
(quadpattern (quad <http://graph> ?s ?p ?o))
Original Algebra:
(foo
(graph <http://graph>
(bgp (triple ?s ?p ?o)))
)
Quad Form Algebra:
(foo
(quadpattern (quad <urn:x-arq:DefaultGraphNode> ?s ?p ?o))
)
Also included with the gist.
As can be seen in the above example output the transformation causes the
graph field in the resulting quadpattern operator to be incorrectly
overwritten
Under a debugger I can see that this is because when invoked via
Transformer.Transform() the quad form transform gets applied bottom up
instead of top down so the bgp is visited prior to the graph and so the
correct graph node is not honoured
I'm assuming this is essentially a user error on my part because of the way
I am blindly using Transform.transform() to apply the transform given. Part
of the problem is that in every other case bottom up application is going to
be the desired behaviour. Is there an alternative way to apply the
transform or to detect when a transform wants to be top down?
Any thoughts would be most appreciated,
Cheers,
Rob