/** * 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; } }