/**
* 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.cassandra.db.secindex;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.NoSuchElementException;
import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.*;
import org.apache.cassandra.dht.AbstractBounds;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.utils.CloseableIterator;
import org.apache.cassandra.utils.FBUtilities;
/**
* Iterates over keys matched by a particular index expression for an IndexType.KEYS index.
*/
public class KeysIterator implements CloseableIterator<DecoratedKey>
{
private static Logger logger = LoggerFactory.getLogger(KeysIterator.class);
public static final int BUFFER_COUNT = 128;
private final ColumnFamilyStore indexCFS;
private final IPartitioner basep;
private final AbstractBounds range;
private final DecoratedKey indexKey;
private final QueryPath path;
// beginning of the current index block
private ByteBuffer startKey;
// current index block, or null
private ArrayDeque<DecoratedKey> buffer;
public KeysIterator(ColumnFamilyStore indexCFS, IPartitioner basep, AbstractBounds range, DecoratedKey indexKey, ByteBuffer startKey)
{
this.indexCFS = indexCFS;
this.basep = basep;
this.path = new QueryPath(indexCFS.columnFamily);
this.range = range;
this.indexKey = indexKey;
this.startKey = startKey;
this.buffer = new ArrayDeque<DecoratedKey>();
}
public boolean hasNext()
{
if (!buffer.isEmpty())
// we have a buffered block
return true;
if (startKey == null)
// nothing left to scan
return false;
while (startKey != null)
{
// query for the next block
if (logger.isDebugEnabled())
logger.debug(String.format("Scanning index row %s:%s starting with %s",
indexCFS.columnFamily, indexKey, indexCFS.getComparator().getString(startKey)));
QueryFilter indexFilter = QueryFilter.getSliceFilter(indexKey,
path,
startKey,
FBUtilities.EMPTY_BYTE_BUFFER,
false,
BUFFER_COUNT);
ColumnFamily block = indexCFS.getColumnFamily(indexFilter);
logger.debug("fetched {}", block);
if (block == null)
return false;
int colsInBlock = 0;
for (IColumn column : block.getSortedColumns())
{
startKey = column.name();
if (++colsInBlock == BUFFER_COUNT)
// exclude this key, and perform another slice
break;
if (column.isMarkedForDelete())
continue;
DecoratedKey dk = basep.decorateKey(column.name());
/* we don't have a way to get the key back from the range -- we just have a token --
* so, we need to loop after starting with start_key, until we get to keys in the given `range`.
* But, if the calling StorageProxy is doing a good job estimating data from each range, the range
* should be pretty close to `start_key`. */
if (!range.right.equals(basep.getMinimumToken()) && range.right.compareTo(dk.token) < 0)
// after the end of the range
break;
if (!range.contains(dk.token))
// before the beginning of the range
continue;
// matched a key: add to the buffer
buffer.add(dk);
}
if (colsInBlock < BUFFER_COUNT)
// no more slices
startKey = null;
if (!buffer.isEmpty())
// found some keys
break;
}
return !buffer.isEmpty();
}
public DecoratedKey next()
{
if (!hasNext()) throw new NoSuchElementException();
return buffer.pollFirst();
}
public void close() { /* pass */ }
public void remove() { throw new UnsupportedOperationException(); }
}