/*
* Copyright 2010 Outerthought bvba
*
* Licensed 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.lilyproject.hbaseindex;
import java.io.IOException;
import org.apache.hadoop.hbase.util.Bytes;
/**
* Merge-joins two QueryResults into one, in other words: an AND
* operation on two indices.
*
* <p>This only works if the individual QueryResults return their rows
* sorted in increasing identifier order, and return each identifier at most
* once. This will not be the case for queries that only search
* on a subset of the fields in the index, or when using range queries
* on multi-valued fields.
*
* <p>A Conjunction itself also returns its results in increasing identifier
* order, and can hence serve as input to other Conjunctions.
*
* <p>TODO the implementation is currently not optimal if lots of rows need
* to be skipped to move to the next common result, since this is done by
* iterating one result at a time using next() calls. It would be better to
* directly skip to the next appropriate result. HBase scanners don't support
* this natively, so to skip we would rather need to open a new scanner. But
* we can't exactly know on beforehand if this will be beneficial or not. Maybe
* we could have some heuristic for this, e.g. after 10 next() calls open a new
* scanner to jump directly to the next relevant result.
*/
public class Conjunction extends BaseQueryResult {
private QueryResult result1;
private QueryResult result2;
public Conjunction(QueryResult result1, QueryResult result2) {
super(null);
this.result1 = result1;
this.result2 = result2;
}
@Override
public byte[] next() throws IOException {
byte[] key1 = result1.next();
byte[] key2 = result2.next();
if (key1 == null || key2 == null) {
return null;
}
int cmp = Bytes.compareTo(key1, key2);
while (cmp != 0) {
if (cmp < 0) {
while (cmp < 0) {
key1 = result1.next();
if (key1 == null) {
return null;
}
cmp = Bytes.compareTo(key1, key2);
}
} else if (cmp > 0) {
while (cmp > 0) {
key2 = result2.next();
if (key2 == null) {
return null;
}
cmp = Bytes.compareTo(key1, key2);
}
}
}
currentQResult = result1;
return key1;
}
@Override
public void close() {
result1.close();
result2.close();
}
}