AbstractIntersectionQueryExecutor.java
/*
* AbstractIntersectionQueryExecutor.java
*
* Copyright (c) 2007-2011, The University of Sheffield.
*
* This file is part of GATE MÃmir (see http://gate.ac.uk/family/mimir.html),
* and is free software, licenced under the GNU Lesser General Public License,
* Version 3, June 2007 (also included with this distribution as file
* LICENCE-LGPL3.html).
*
* Valentin Tablan, 8 Jul 2009
*
* $Id: AbstractIntersectionQueryExecutor.java 16667 2013-04-29 16:35:35Z valyt $
*/
package gate.mimir.search.query;
import gate.mimir.search.QueryEngine;
import it.unimi.dsi.fastutil.objects.ReferenceArraySet;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
import it.unimi.di.big.mg4j.index.Index;
import it.unimi.di.big.mg4j.search.visitor.DocumentIteratorVisitor;
import java.io.IOException;
import java.util.Arrays;
/**
* An abstract query executor that implements the nextDocument() functionality
* shared between all query executors that combine a set of sub-executors and
* only need to return results on common documents.
*/
public abstract class AbstractIntersectionQueryExecutor extends AbstractQueryExecutor {
/**
* The sub-queries
*/
protected QueryNode[] nodes;
/**
* An array of current nextDocumentID values, for all of the sub nodes.
*/
protected long[] nextDocIDs;
protected ReferenceSet<Index> indices;
/**
* The {@link QueryExecutor}s for the contained nodes.
*/
protected QueryExecutor[] executors;
/**
* Constructor from {@link QueryEngine}.
* @throws IOException if the index files cannot be accessed.
*/
public AbstractIntersectionQueryExecutor(QueryEngine engine, QueryNode query,
QueryNode... subNodes) throws IOException {
super(engine, query);
this.nodes = subNodes;
// prepare all the executors
this.executors = new QueryExecutor[subNodes.length];
this.nextDocIDs = new long[executors.length];
for(int i = 0; i < subNodes.length; i++) {
executors[i] = subNodes[i].getQueryExecutor(engine);
nextDocIDs[i] = executors[i].nextDocument(-1);
if(nextDocIDs[i] < 0) {
// no results!
latestDocument = -1;
// shorten the executors array, to avoid NPEs.
executors = Arrays.copyOfRange(executors, 0, i + 1);
break;
}
}
}
@Override
public long nextDocument(long greaterThan) throws IOException {
if(closed) return latestDocument = -1;
if(latestDocument == -1) return latestDocument;
// we want all documentIDs to converge to max, which should be at least
// greaterThan + 1
// the max value will only move up!
// Note that the greterThan value can be anything (e.g.-100), so we need to
// force the advance by comparing to latestDocument.
long max = Math.max(latestDocument, greaterThan) + 1;
// move all documentIDs to at or over current max,
// until they all have the same ID
boolean doneAdvancing = false;
while(!doneAdvancing) {
doneAdvancing = true;
for(int i = 0; i < executors.length; i++) {
if(nextDocIDs[i] < max) {
// this needs to move forward to at least max
nextDocIDs[i] = executors[i].nextDocument(max - 1);
if(nextDocIDs[i] == -1) {
// one executor has run out of documents -> we're done here!
return latestDocument = -1;
}
doneAdvancing = false;
}
if(nextDocIDs[i] > max) {
max = nextDocIDs[i];
//we need to move all others to the same value
doneAdvancing = false;
}
}
}
// If we reached this point, all executors are pointing to the same
// document (max).
return latestDocument = max;
}
public <T> T accept( final DocumentIteratorVisitor<T> visitor ) throws IOException {
if ( ! visitor.visitPre( this ) ) return null;
int n = executors.length;
final T[] a = visitor.newArray( n );
if ( a == null ) {
for( int i = 0; i < n; i++ ) if ( executors[ i ] != null && executors[ i ].accept( visitor ) == null ) return null;
}
else {
for( int i = 0; i < n; i++ ) if ( executors[ i ] != null && ( a[ i ] = executors[ i ].accept( visitor ) ) == null ) return null;
}
return visitor.visitPost( this, a );
}
public <T> T acceptOnTruePaths( final DocumentIteratorVisitor<T> visitor ) throws IOException {
if ( ! visitor.visitPre( this ) ) return null;
int n = executors.length;
final T[] a = visitor.newArray( n );
if ( a == null ) {
for( int i = 0; i < n; i++ ) if ( executors[ i ] != null && executors[ i ].acceptOnTruePaths( visitor ) == null ) return null;
}
else {
for( int i = 0; i < n; i++ ) if ( executors[ i ] != null && ( a[ i ] = executors[ i ].acceptOnTruePaths( visitor ) ) == null ) return null;
}
return visitor.visitPost( this, a );
}
@Override
public ReferenceSet<Index> indices() {
if(indices == null) {
indices = new ReferenceArraySet<Index>();
for(QueryExecutor qExec : executors) {
indices.addAll(qExec.indices());
}
}
return indices;
}
/* (non-Javadoc)
* @see gate.mimir.search.query.AbstractQueryExecutor#close()
*/
@Override
public void close() throws IOException {
if(closed) return;
super.close();
for(QueryExecutor anExecutor : executors) {
if(anExecutor != null) anExecutor.close();
}
executors = null;
nodes = null;
nextDocIDs = null;
indices = null;
}
}