@Override public List<Row> search( List<IndexExpression> clause, AbstractBounds<RowPosition> range, int maxResults, IFilter dataFilter, boolean maxIsColumns) { assert clause != null && !clause.isEmpty(); ExtendedFilter filter = ExtendedFilter.create(baseCfs, dataFilter, clause, maxResults, maxIsColumns, false); return baseCfs.filter(getIndexedIterator(range, filter), filter); }
public ColumnFamilyStore.AbstractScanIterator getIndexedIterator( final AbstractBounds<RowPosition> range, final ExtendedFilter filter) { // Start with the most-restrictive indexed clause, then apply remaining clauses // to each row matching that clause. // TODO: allow merge join instead of just one index + loop final IndexExpression primary = highestSelectivityPredicate(filter.getClause()); final SecondaryIndex index = indexManager.getIndexForColumn(primary.column_name); if (logger.isDebugEnabled()) logger.debug( "Primary scan clause is " + baseCfs.getComparator().getString(primary.column_name)); assert index != null; final DecoratedKey indexKey = indexManager.getIndexKeyFor(primary.column_name, primary.value); /* * XXX: If the range requested is a token range, we'll have to start at the beginning (and stop at the end) of * the indexed row unfortunately (which will be inefficient), because we have not way to intuit the small * possible key having a given token. A fix would be to actually store the token along the key in the * indexed row. */ final ByteBuffer startKey = range.left instanceof DecoratedKey ? ((DecoratedKey) range.left).key : ByteBufferUtil.EMPTY_BYTE_BUFFER; final ByteBuffer endKey = range.right instanceof DecoratedKey ? ((DecoratedKey) range.right).key : ByteBufferUtil.EMPTY_BYTE_BUFFER; return new ColumnFamilyStore.AbstractScanIterator() { private ByteBuffer lastSeenKey = startKey; private Iterator<IColumn> indexColumns; private final QueryPath path = new QueryPath(baseCfs.columnFamily); private int columnsRead = Integer.MAX_VALUE; protected Row computeNext() { int meanColumns = Math.max(index.getIndexCfs().getMeanColumns(), 1); // We shouldn't fetch only 1 row as this provides buggy paging in case the first row doesn't // satisfy all clauses int rowsPerQuery = Math.max(Math.min(filter.maxRows(), filter.maxColumns() / meanColumns), 2); while (true) { if (indexColumns == null || !indexColumns.hasNext()) { if (columnsRead < rowsPerQuery) { logger.debug( "Read only {} (< {}) last page through, must be done", columnsRead, rowsPerQuery); return endOfData(); } if (logger.isDebugEnabled()) logger.debug( String.format( "Scanning index %s starting with %s", expressionString(primary), index.getBaseCfs().metadata.getKeyValidator().getString(startKey))); QueryFilter indexFilter = QueryFilter.getSliceFilter( indexKey, new QueryPath(index.getIndexCfs().getColumnFamilyName()), lastSeenKey, endKey, false, rowsPerQuery); ColumnFamily indexRow = index.getIndexCfs().getColumnFamily(indexFilter); logger.debug("fetched {}", indexRow); if (indexRow == null) { logger.debug("no data, all done"); return endOfData(); } Collection<IColumn> sortedColumns = indexRow.getSortedColumns(); columnsRead = sortedColumns.size(); indexColumns = sortedColumns.iterator(); IColumn firstColumn = sortedColumns.iterator().next(); // Paging is racy, so it is possible the first column of a page is not the last seen // one. if (lastSeenKey != startKey && lastSeenKey.equals(firstColumn.name())) { // skip the row we already saw w/ the last page of results indexColumns.next(); logger.debug( "Skipping {}", baseCfs.metadata.getKeyValidator().getString(firstColumn.name())); } else if (range instanceof Range && indexColumns.hasNext() && firstColumn.name().equals(startKey)) { // skip key excluded by range indexColumns.next(); logger.debug("Skipping first key as range excludes it"); } } while (indexColumns.hasNext()) { IColumn column = indexColumns.next(); lastSeenKey = column.name(); if (column.isMarkedForDelete()) { logger.debug("skipping {}", column.name()); continue; } DecoratedKey dk = baseCfs.partitioner.decorateKey(lastSeenKey); if (!range.right.isMinimum(baseCfs.partitioner) && range.right.compareTo(dk) < 0) { logger.debug("Reached end of assigned scan range"); return endOfData(); } if (!range.contains(dk)) { logger.debug("Skipping entry {} outside of assigned scan range", dk.token); continue; } logger.debug("Returning index hit for {}", dk); ColumnFamily data = baseCfs.getColumnFamily(new QueryFilter(dk, path, filter.initialFilter())); // While the column family we'll get in the end should contains the primary clause // column, the initialFilter may not have found it and can thus be null if (data == null) data = ColumnFamily.create(baseCfs.metadata); return new Row(dk, data); } } } public void close() throws IOException {} }; }