1.
首先还是先放出本例中的查询语句:
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX people: <http://org.semwebprogramming/chapter2/people#>
SELECT DISTINCT ?name
WHERE
{ people:me foaf:name ?name }
前几天一直在寻找的最终处理类应该就是AlgebraGenerator这个类,这个类的解释是“ Class used to compile SPARQL queries into SPARQL algebra”,也即是说将sparql查询语句编译成sparql代数。这个类中有许多的编译方法,我们使用的编译方法是protected Op compileModifiers(Query query, Op pattern),这个方法的具体内容如下:
protected Op compileModifiers(Query query, Op pattern)
{
/* The modifier order in algebra is:
*
* Limit/Offset
* Distinct/reduce
* project
* OrderBy
* Bindings
* having
* select expressions
* group
*/
// Preparation: sort SELECT clause into assignments and projects.
VarExprList projectVars = query.getProject() ;
VarExprList exprs = new VarExprList() ; // Assignments to be done.
List<Var> vars = new ArrayList<Var>() ; // projection variables
Op op = pattern ;
// ---- GROUP BY
if ( query.hasGroupBy() )
{
// When there is no GroupBy but there are some aggregates, it's a group of no variables.
op = new OpGroup(op, query.getGroupBy(), query.getAggregators()) ;
}
//---- Assignments from SELECT and other places (so available to ORDER and HAVING)
// Now do assignments from expressions
// Must be after "group by" has introduced it's variables.
// Look for assignments in SELECT expressions.
if ( ! projectVars.isEmpty() && ! query.isQueryResultStar())
{
// Don't project for QueryResultStar so initial bindings show
// through in SELECT *
if ( projectVars.size() == 0 && query.isSelectType() )
Log.warn(this,"No project variables") ;
// Separate assignments and variable projection.
for ( Var v : query.getProject().getVars() )
{
Expr e = query.getProject().getExpr(v) ;
if ( e != null )
{
Expr e2 = ExprLib.replaceAggregateByVariable(e) ;
exprs.add(v, e2) ;
}
// Include in project
vars.add(v) ;
}
}
// ---- Assignments from SELECT and other places (so available to ORDER and HAVING)
if ( ! exprs.isEmpty() )
// Potential rewrites based of assign introducing aliases.
op = OpExtend.extend(op, exprs) ;
// ---- HAVING
if ( query.hasHaving() )
{
for (Expr expr : query.getHavingExprs())
{
// HAVING expression to refer to the aggregate via the variable.
Expr expr2 = ExprLib.replaceAggregateByVariable(expr) ;
op = OpFilter.filter(expr2 , op) ;
}
}
// ---- VALUES
if ( query.hasValues() )
{
Table table = TableFactory.create(query.getValuesVariables()) ;
for ( Binding binding : query.getValuesData() )
table.addBinding(binding) ;
OpTable opTable = OpTable.create(table) ;
op = OpJoin.create(op, opTable) ;
}
// ---- ToList
if ( context.isTrue(ARQ.generateToList) )
// Listify it.
op = new OpList(op) ;
// ---- ORDER BY
if ( query.getOrderBy() != null )
{
List<SortCondition> scList = new ArrayList<SortCondition>() ;
// Aggregates in ORDER BY
for ( SortCondition sc : query.getOrderBy() )
{
Expr e = sc.getExpression() ;
e = ExprLib.replaceAggregateByVariable(e) ;
scList.add(new SortCondition(e, sc.getDirection())) ;
}
op = new OpOrder(op, scList) ;
}
// ---- PROJECT
// No projection => initial variables are exposed.
// Needed for CONSTRUCT and initial bindings + SELECT *
if ( vars.size() > 0 )
op = new OpProject(op, vars) ;
// ---- DISTINCT
if ( query.isDistinct() )
op = OpDistinct.create(op) ;
// ---- REDUCED
if ( query.isReduced() )
op = OpReduced.create(op) ;
// ---- LIMIT/OFFSET
if ( query.hasLimit() || query.hasOffset() )
op = new OpSlice(op, query.getOffset() /*start*/, query.getLimit()/*length*/) ;
return op ;
}
由方法里的注释可以看到modifier其实就是sparql中的功能关键字,前面一直在搞的是将sparql中的查询WHERE中的模式进行各种变换,但始终没有涉及对其功能性关键字的处理,而这里就是对其的处理。
来分析这个函数的处理过程:
VarExprList projectVars = query.getProject() ;
这里是将query中的查询值数组放在VarExprList类中,在本例中,查询值只有一个?name。
下面是: VarExprList exprs = new VarExprList() ; // Assignments to be done.
List<Var> vars = new ArrayList<Var>() ; // projection variables
Op op = pattern ;
这两句话创造了两个计算中需要用到的中间量,以及一个用来承载传入pattern的变量op。
下面就是一堆对query中之前手贱创造的标记位的判断了:
if ( query.hasGroupBy() )
if ( ! projectVars.isEmpty() && ! query.isQueryResultStar())
if ( ! exprs.isEmpty() )
if ( query.hasHaving() )
if ( query.hasValues() )
if ( context.isTrue(ARQ.generateToList) )
if ( query.getOrderBy() != null )
if ( vars.size() > 0 )
if ( query.isDistinct() )
if ( query.isReduced() )
if ( query.hasLimit() || query.hasOffset() )
这一坨判断,我只打算分析本例中判断成功的,其它的,就让它自生自灭吧。
首先第一个进入的判断是:
if ( ! projectVars.isEmpty() && ! query.isQueryResultStar())
{
// Don't project for QueryResultStar so initial bindings show
// through in SELECT *
if ( projectVars.size() == 0 && query.isSelectType() )
Log.warn(this,"No project variables") ;
// Separate assignments and variable projection.
for ( Var v : query.getProject().getVars() )
{
Expr e = query.getProject().getExpr(v) ;
if ( e != null )
{
Expr e2 = ExprLib.replaceAggregateByVariable(e) ;
exprs.add(v, e2) ;
}
// Include in project
vars.add(v) ;
}
}
这个判断很强势,首先判断有没有查询返回值,本例中,幸运地存在一个?name,然后查询query.queryResultStar这一标记为是不是为false,幸运的是,本例中枪,因此进入处理。接着使用下面这个语句一个个地取出需要的返回值。
for ( Var v : query.getProject().getVars() )
本例中,这里只取出了一个返回值?name。因为返回值只有一个,所以下面的
Expr e = query.getProject().getExpr(v) ;
这句其实就取到null值传给e了。这种最简单的情况下甚至连复杂一点的变换都不需要,直接将?name加入函数一开始时声明的vars值就行。至此,这个判断结束。
然后下面的判断略过一大堆,进入这个判断:
if ( vars.size() > 0 )
判断下语句就只有一个:
op = new OpProject(op, vars) ;
就是将pattern和返回的?name绑定到一起,实际跟踪后显示的就是new了一个op1类,然后将其的这两个值载入。
下一个进入的判断是:
if ( query.isDistinct() )
这个判断的执行语句也只有一个:
op = OpDistinct.create(op) ;
这个依然是new了一个继承op1的OpDistinct类然后赋给op,实际上,这两次为op赋值虽然都使用new方法创建了某种东西,但是看op的实际值就可以看到,这种两次创建并不是覆盖性的,而是顺序性的,不妨观察一下op值得变化,最早刚生成的时候,op的值为:
(bgp (triple <http://org.semwebprogramming/chapter2/people#me> <http://xmlns.com/foaf/0.1/name> ?name))
之后判断vars的size的实现语句之后,op的值为:
(project (?name)
(bgp (triple <http://org.semwebprogramming/chapter2/people#me> <http://xmlns.com/foaf/0.1/name> ?name)))
到现在对distinct的判断之后,op的值为:
(distinct
(project (?name)
(bgp (triple <http://org.semwebprogramming/chapter2/people#me> <http://xmlns.com/foaf/0.1/name> ?name))))
可见每次都是对op的表述进行了某种程度的扩展。
这个判断之后,op值修改完毕,将最后的那个op值返回。
2.这里要开始涉及Plan的操作了。
Plan是个重头戏,因为最后的一些值是从Plan中取出的,因此这个东西值得好好分析:
protected Plan createPlan()
{
// Decide the algebra to actually execute.
Op op = queryOp ;
if ( ! startBinding.isEmpty() ) {
op = Substitute.substitute(op, startBinding) ;
context.put(ARQConstants.sysCurrentAlgebra, op) ;
// Don't reset the startBinding because it also is
// needed in the output.
}
op = modifyOp(op) ;
QueryIterator queryIterator = null ;
if ( dataset != null )
// Null means setting up but not executing a query.
queryIterator = evaluate(op, dataset, startBinding, context) ;
else
// Bypass management interface
queryIterator = evaluateNoMgt(op, dataset, startBinding, context) ;
// This could be an automagic iterator to catch close.
return new PlanOp(getOp(), this, queryIterator) ;
}
首先,现将之前的Op查询语句传进来。
然后判断是否已经开始绑定,官方的解释是“Don't reset the startBinding because it also is needed in the output.”就是说不要重新进行绑定,因为这在输出的时候还需要用到,具体啥意思我没搞懂,先往下看。接着是对op进行修饰,具体的修饰是判断一些标记位然后进行一些优化,这个过程也比较长,因此先略过,本例中,这一优化后op并没有发生什么大的改变。
然后判断dataset是否为空,官方对此也有解释“Null means setting up but not executing a query.”就是说如果dataset是空就意味着dataset已经建立起来了,只是没有执行查询语句,和上面一样,官方的解释我还是不懂,官方能不能多说几句?感觉自己智商乏力。还是往下看吧,下一句的深一层执行代码是:
// Record the query operation as it goes pass and call the actual worker @Override final public QueryIterator evaluate(Op op, DatasetGraph dsg, Binding binding, Context context) {
Explain.explain("ALGEBRA", op, context) ; queryEngineInfo.incQueryCount() ; queryEngineInfo.setLastQueryExecAt() ; //queryEngineInfo.setLastQueryExecTime(-1) ; queryEngineInfo.setLastQueryString((Query)context.get(ARQConstants.sysCurrentQuery)) ; queryEngineInfo.setLastOp(op) ; return eval(op, dsg, binding, context) ; } 官方的解释是:“Record the query operation as it goes pass and call the actual worker”就是说记录查询操作当函数调用实际的处理单元的时候。if ( query != null ) Explain.explain("QUERY", query, context) ;
这两句是用来判断当查询存在的时候,这个查询是不是已经正在执行,如果是,看起来Jena想调用一些多线程机制来定义新的查询?不知道对不对,先把猜测不负责任地放这里好了,继续往下解读,下一句和这一句的意思是一样的,只是把query换成了op。if ( query != null ) Explain.explain("QUERY", query, context) ;
然后几句是设置queryEngineInfo的信息。后面return了一个QueryIterator类型的东西,这个东西里面的资料很奇怪,我一开始理解的是查询结果,可又不是,又是个变种的中间变量:
到现在为止,我已经对变种的sparql查询见怪不怪了,这一变种被提交到上一层之后交进PlanOp函数处理:QueryIterRoot QueryIterBlockTriples <http://org.semwebprogramming/chapter2/people#me> <http://xmlns.com/foaf/0.1/name> ?name . QueryIterProject ?name QueryIterDistinct
这个过程原本以为很高端,其实就是再包装一层,其实提交上去的plan内容是:return new PlanOp(getOp(), this, queryIterator) ;
plan至此已经生成完毕。(Plan QueryIteratorCloseable/QueryIteratorCheck QueryIterRoot QueryIterBlockTriples <http://org.semwebprogramming/chapter2/people#me> <http://xmlns.com/foaf/0.1/name> ?name . QueryIterProject ?name QueryIterDistinct )