org.apache.jena.sparql.engine.index.HashIndexTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jena-arq Show documentation
Show all versions of jena-arq Show documentation
ARQ is a SPARQL 1.1 query engine for Apache Jena
/**
* 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.index;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.jena.graph.Node ;
import org.apache.jena.sparql.core.Var ;
import org.apache.jena.sparql.engine.QueryIterator ;
import org.apache.jena.sparql.engine.binding.Binding ;
/**
* Indexes bindings so that they can be search for quickly when a binding to all the
* variables is provided. If a binding to only some of the known variables is provided
* then the index still works, but will search linearly.
*/
public class HashIndexTable implements IndexTable {
// Contribution from P Gearon (@quoll)
final private Set table ;
final private Map varColumns ;
private boolean missingValue ;
public HashIndexTable(Set commonVars, QueryIterator data) throws MissingBindingException
{
varColumns = initColumnMappings(commonVars) ;
if ( commonVars.size() == 0 )
{
table = null ;
return ;
}
table = new HashSet<>() ;
missingValue = false ;
while ( data.hasNext() )
{
Binding binding = data.nextBinding() ;
addBindingToTable(binding) ;
}
data.close() ;
}
@Override
public String toString() {
return "HashIndexTable: "+varColumns+" "+table+ "("+missingValue+")";
}
@Override
public boolean containsCompatibleWithSharedDomain(Binding binding)
{
// no shared variables means no shared domain, and should be ignored
if ( table == null )
return false ;
Key indexKey ;
indexKey = convertToKey(binding) ;
if ( table.contains(indexKey) )
return true ;
if ( anyUnbound(indexKey) )
return matchAnyCompatible(indexKey) ;
return false ;
}
private boolean anyUnbound(Key mappedBinding)
{
for ( Node n: mappedBinding.getNodes() )
{
if ( n == null )
return true ;
}
return false ;
}
private static Map initColumnMappings(Set commonVars)
{
Map varColumns = new HashMap<>() ;
int c = 0 ;
for ( Var var: commonVars )
varColumns.put(var, c++) ;
return varColumns;
}
private void addBindingToTable(Binding binding) throws MissingBindingException
{
Key key = convertToKey(binding) ;
table.add(key) ;
if ( missingValue )
throw new MissingBindingException(table, varColumns) ;
}
private Key convertToKey(Binding binding)
{
Node[] indexKey = new Node[varColumns.size()] ;
for ( Map.Entry varCol : varColumns.entrySet() )
{
Node value = binding.get(varCol.getKey()) ;
if ( value == null )
missingValue = true ;
indexKey[varCol.getValue()] = value ;
}
return new Key(indexKey) ;
}
private boolean matchAnyCompatible(Key mappedBindingLeft)
{
for ( Key mappedBindingRight: table )
{
if ( mappedBindingLeft.compatibleAndSharedDomain(mappedBindingRight) )
return true ;
}
return false ;
}
static class MissingBindingException extends Exception {
private final Set data ;
private final Map varMappings ;
public MissingBindingException(Set data, Map varMappings)
{
this.data = data ;
this.varMappings = varMappings ;
}
public Set getData() { return data ; }
public Map getMap() { return varMappings ; }
}
static class Key
{
final Node[] nodes;
Key(Node[] nodes)
{
this.nodes = nodes ;
}
public Node[] getNodes()
{
return nodes;
}
@Override
public String toString()
{
return Arrays.asList(nodes).toString() ;
}
@Override
public int hashCode()
{
int result = 0 ;
for ( Node n: nodes )
result ^= (n == null) ? 0 : n.hashCode() ;
return result ;
}
@Override
public boolean equals(Object o)
{
if ( ! (o instanceof Key) )
return false ;
Node[] other = ((Key)o).nodes ;
for ( int i = 0 ; i < nodes.length ; i++ )
{
if ( nodes[i] == null)
{
if ( other[i] != null )
return false ;
}
else
{
if ( ! nodes[i].equals(other[i]) )
return false ;
}
}
return true ;
}
public boolean compatibleAndSharedDomain(Key mappedBindingR)
{
Node[] nodesRight = mappedBindingR.getNodes() ;
for ( int c = 0 ; c < nodes.length ; c++ )
{
Node nLeft = nodes[c] ;
Node nRight = nodesRight[c] ;
if ( nLeft != null && nRight != null ) {
// Shared domain - both left and right have a value.
// Compatible?
if ( nLeft.equals(nRight) )
return true ;
}
}
// No compatible slot or didn't find shared domain.
return false ;
}
}
}