Example #1
0
 @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);
 }
Example #2
0
  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 {}
    };
  }