MinusQuery.java
/**
* Copyright (c) 2007-2012, 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, 12 11 2012
*
* $Id*/
package gate.mimir.search.query;
import gate.mimir.search.QueryEngine;
import gate.mimir.search.query.AbstractOverlapQuery.SubQuery;
import it.unimi.di.big.mg4j.index.Index;
import it.unimi.di.big.mg4j.search.visitor.DocumentIteratorVisitor;
import it.unimi.dsi.fastutil.objects.ReferenceArraySet;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* A binary query operator that returns all the hits from the left operand that
* are not also returned by the right operand.
*/
public class MinusQuery implements QueryNode {
public static class MinusQueryExecutor extends AbstractQueryExecutor{
protected QueryExecutor left;
protected QueryExecutor right;
protected List<Binding> hitsOnCurrentDocument;
public MinusQueryExecutor(QueryEngine engine, MinusQuery qNode) throws IOException {
super(engine, qNode);
left = qNode.left.getQueryExecutor(engine);
right = qNode.right.getQueryExecutor(engine);
hitsOnCurrentDocument = new ArrayList<Binding>();
}
@Override
public void close() throws IOException {
if(closed) return;
super.close();
left.close();
right.close();
}
@Override
public long nextDocument(long greaterThan) throws IOException {
if(closed) return latestDocument = -1;
if(latestDocument == -1) return latestDocument;
hitsOnCurrentDocument.clear();
// any more input documents from left?
latestDocument = left.nextDocument(greaterThan);
while(hitsOnCurrentDocument.isEmpty() && latestDocument != -1){
// check if right needs advancing
long rightNext = right.getLatestDocument();
if(rightNext != -1 && rightNext < latestDocument){
rightNext = right.nextDocument(latestDocument - 1);
}
if(latestDocument == rightNext) {
// filter the hits
Binding leftHit = left.nextHit();
Binding rightHit = right.nextHit();
while(leftHit != null) {
int cmp = rightHit == null ? -1 : leftHit.compareTo(rightHit);
if(cmp < 0) {
hitsOnCurrentDocument.add(leftHit);
leftHit = left.nextHit();
} else if(cmp == 0) {
// skip this left hit
leftHit = left.nextHit();
} else {
rightHit = right.nextHit();
}
}
} else {
// all hits from left are good
Binding hit = left.nextHit();
while(hit != null){
hitsOnCurrentDocument.add(hit);
hit = left.nextHit();
}
}
// if all hits were vetoed, try the next doc
if(hitsOnCurrentDocument.isEmpty()){
latestDocument = left.nextDocument(greaterThan);
}
}
return latestDocument;
}
@Override
public Binding nextHit() throws IOException {
if(closed) return null;
return hitsOnCurrentDocument.isEmpty() ? null : hitsOnCurrentDocument.remove(0);
}
@Override
public ReferenceSet<Index> indices() {
return left.indices();
}
@Override
public <T> T accept(DocumentIteratorVisitor<T> visitor) throws IOException {
if ( ! visitor.visitPre( this ) ) return null;
final T[] a = visitor.newArray( 2 );
if ( a == null ) {
if ( left.accept( visitor ) == null ) return null;
if ( right.accept( visitor ) == null ) return null;
} else {
if ( ( a[ 0 ] = left.accept( visitor ) ) == null ) return null;
if ( ( a[ 1 ] = right.accept( visitor ) ) == null ) return null;
}
return visitor.visitPost( this, a );
}
@Override
public <T> T acceptOnTruePaths(DocumentIteratorVisitor<T> visitor)
throws IOException {
if ( ! visitor.visitPre( this ) ) return null;
final T[] a = visitor.newArray( 1 );
if ( a == null ) {
if ( left.acceptOnTruePaths( visitor ) == null ) return null;
}
else {
if ( ( a[ 0 ] = left.acceptOnTruePaths( visitor ) ) == null ) return null;
}
return visitor.visitPost( this, a );
}
}
/**
* The left operand.
*/
protected QueryNode left;
/**
* The right operand.
*/
protected QueryNode right;
/**
* Construct a new MINUS query.
* @param left the left operand
* @param right the right operand
*/
public MinusQuery(QueryNode left, QueryNode right) {
this.left = left;
this.right = right;
}
/* (non-Javadoc)
* @see gate.mimir.search.query.QueryNode#getQueryExecutor(gate.mimir.search.QueryEngine)
*/
@Override
public QueryExecutor getQueryExecutor(QueryEngine engine) throws IOException {
return new MinusQueryExecutor(engine, this);
}
public String toString(){
return "MINUS (\nLEFT:" + left.toString() + ",\nRIGHT:" +
right.toString() +"\n)";
}
public QueryNode getLeft() {
return left;
}
public QueryNode getRight() {
return right;
}
}