/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.sparql.engine.join;
import java.util.* ;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.atlas.logging.Log ;
import org.apache.jena.sparql.engine.ExecutionContext ;
import org.apache.jena.sparql.engine.QueryIterator ;
import org.apache.jena.sparql.engine.binding.Binding ;
import org.apache.jena.sparql.engine.iterator.QueryIterNullIterator ;
import org.apache.jena.sparql.engine.iterator.QueryIterPlainWrapper ;
import org.apache.jena.sparql.engine.join.JoinKey ;
import org.apache.jena.sparql.expr.ExprList ;
/**
* Hash left join.
*
* This code materializes the left hand side into a probe table then hash joins
* from the right.
*
* See {@link QueryIterHashLeftJoin_Right} for one that uses the right hand side
* to make the probe table.
*/
public class QueryIterHashLeftJoin_Left extends AbstractIterHashJoin {
// Left join conditions
private final ExprList conditions;
/**
* Create a hashjoin QueryIterator.
* @param joinKey Join key - if null, one is guessed by snooping the input QueryIterators
* @param left
* @param right
* @param conditions
* @param execCxt
* @return QueryIterator
*/
public static QueryIterator create(JoinKey joinKey, QueryIterator left, QueryIterator right, ExprList conditions, ExecutionContext execCxt) {
// Easy cases.
if ( ! left.hasNext() ) {
left.close() ;
right.close() ;
return QueryIterNullIterator.create(execCxt) ;
}
if ( ! right.hasNext() ) {
right.close() ;
return left ;
}
if ( joinKey != null && joinKey.length() > 1 )
Log.warn(QueryIterHashLeftJoin_Left.class, "Multivariable join key") ;
return new QueryIterHashLeftJoin_Left(joinKey, left, right, conditions, execCxt) ;
}
/**
* Create a hashjoin QueryIterator.
* @param left
* @param right
* @param execCxt
* @return QueryIterator
*/
public static QueryIterator create(QueryIterator left, QueryIterator right, ExprList conditions, ExecutionContext execCxt) {
return create(null, left, right, conditions, execCxt) ;
}
private QueryIterHashLeftJoin_Left(JoinKey joinKey, QueryIterator left, QueryIterator right, ExprList conditions, ExecutionContext execCxt) {
super(joinKey, left, right, execCxt) ;
this.conditions = conditions ;
}
private Set<Binding> leftHits = new HashSet<>() ;
@Override
protected Binding yieldOneResult(Binding rowCurrentProbe, Binding rowStream, Binding rowResult) {
if ( conditions != null && ! conditions.isSatisfied(rowResult, getExecContext()) )
return null ;
leftHits.add(rowCurrentProbe) ;
return rowResult ;
}
// Right is stream, left is the probe table.
@Override
protected Binding noYieldedRows(Binding rowCurrentProbe) {
return null;
}
@Override
protected QueryIterator joinFinished() {
Iterator<Binding> iter = Iter.filter(hashTable.values(), b-> ! leftHits.contains(b) ) ;
return new QueryIterPlainWrapper(iter, getExecContext()) ;
}
}