Package org.kiji.schema.impl.hbase

Source Code of org.kiji.schema.impl.hbase.HBaseQualifierPager

/**
* (c) Copyright 2013 WibiData, Inc.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* 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.kiji.schema.impl.hbase;

import java.io.Closeable;
import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;

import com.google.common.base.Preconditions;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.client.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.kiji.annotations.ApiAudience;
import org.kiji.schema.EntityId;
import org.kiji.schema.KijiColumnName;
import org.kiji.schema.KijiColumnPagingNotEnabledException;
import org.kiji.schema.KijiDataRequest;
import org.kiji.schema.KijiDataRequestBuilder.ColumnsDef;
import org.kiji.schema.KijiIOException;
import org.kiji.schema.filter.Filters;
import org.kiji.schema.filter.KijiColumnFilter;
import org.kiji.schema.filter.KijiColumnRangeFilter;
import org.kiji.schema.filter.StripValueColumnFilter;
import org.kiji.schema.hbase.HBaseColumnName;
import org.kiji.schema.impl.KijiPaginationFilter;
import org.kiji.schema.layout.HBaseColumnNameTranslator;
import org.kiji.schema.layout.KijiTableLayout;
import org.kiji.schema.util.Debug;

/**
* Pages through the many qualifiers of a map-type family.
*
* <p>
*   The max-versions parameter on a map-type family applies on a per-qualifier basis.
*   This does not limit the total number of versions returned for the entire map-type family.
* </p>
*/
@ApiAudience.Private
public final class HBaseQualifierPager implements Iterator<String[]>, Closeable {
  private static final Logger LOG = LoggerFactory.getLogger(HBaseQualifierPager.class);

  /** Entity ID of the row being paged through. */
  private final EntityId mEntityId;

  /** HBase KijiTable to read from. */
  private final HBaseKijiTable mTable;

  /** Name of the map-type family being paged through. */
  private final KijiColumnName mFamily;

  /** Full data request. */
  private final KijiDataRequest mDataRequest;

  /** Column data request for the map-type family to page through. */
  private final KijiDataRequest.Column mColumnRequest;

  /** True only if there is another page of data to read through {@link #next()}. */
  private boolean mHasNext;

  /**
   * Highest qualifier (according to the HBase bytes comparator) returned so far.
   * This is the low bound (exclusive) for qualifiers to retrieve next.
   */
  private String mMinQualifier = null;

  /**
   * Initializes a qualifier pager.
   *
   * @param entityId The entityId of the row.
   * @param dataRequest The requested data.
   * @param table The Kiji table that this row belongs to.
   * @param family Iterate through the qualifiers from this map-type family.
   * @throws KijiColumnPagingNotEnabledException If paging is not enabled for the specified family.
   */
  public HBaseQualifierPager(
      EntityId entityId,
      KijiDataRequest dataRequest,
      HBaseKijiTable table,
      KijiColumnName family)
      throws KijiColumnPagingNotEnabledException {

    Preconditions.checkArgument(!family.isFullyQualified(),
        "Must use HBaseQualifierPager on a map-type family, but got '{}'.", family);
    mFamily = family;

    mDataRequest = dataRequest;
    mColumnRequest = mDataRequest.getColumn(family.getFamily(), null);
    if (!mColumnRequest.isPagingEnabled()) {
      throw new KijiColumnPagingNotEnabledException(
        String.format("Paging is not enabled for column [%s].", family));
    }

    mEntityId = entityId;
    mTable = table;
    mHasNext = true// there might be no page to read, but we don't know until we issue an RPC

    // Only retain the table if everything else ran fine:
    mTable.retain();
  }

  /** {@inheritDoc} */
  @Override
  public boolean hasNext() {
    return mHasNext;
  }

  /** {@inheritDoc} */
  @Override
  public String[] next() {
    return next(mColumnRequest.getPageSize());
  }

  /**
   * Fetches another page of qualifiers.
   *
   * @param pageSize Maximum number of qualifiers to retrieve in the page.
   * @return the next page of qualifiers.
   */
  public String[] next(int pageSize) {
    if (!mHasNext) {
      throw new NoSuchElementException();
    }
    Preconditions.checkArgument(pageSize > 0, "Page size must be >= 1, got %s", pageSize);

    // Clone the column data request template, adjusting the filters to restrict the range
    // and the number of qualifiers to fetch.

    final KijiColumnFilter filter = Filters.and(
        new KijiColumnRangeFilter(mMinQualifier, false, null, false)// qualifier > mMinQualifier
        mColumnRequest.getFilter()// user filter
        new KijiPaginationFilter(pageSize)// Select at most 1 version per qualifier
        new StripValueColumnFilter())// discard the cell content, we just need the qualifiers

    final KijiDataRequest nextPageDataRequest = KijiDataRequest.builder()
        .withTimeRange(mDataRequest.getMinTimestamp(), mDataRequest.getMaxTimestamp())
        .addColumns(ColumnsDef.create()
            .withFilter(filter)
            .withMaxVersions(1// HBase pagination filter forces max-versions to 1
            .add(mFamily))
        .build();

    LOG.debug("HBaseMapPager data request: {} and page size {}", nextPageDataRequest, pageSize);

    final KijiTableLayout layout = mTable.getLayout();
    final HBaseColumnNameTranslator translator = HBaseColumnNameTranslator.from(layout);
    final HBaseDataRequestAdapter adapter =
        new HBaseDataRequestAdapter(nextPageDataRequest, translator);
    try {
      final Get hbaseGet = adapter.toGet(mEntityId, layout);
      if (LOG.isDebugEnabled()) {
        LOG.debug("Sending HBase Get: {} with filter {}",
            hbaseGet, Debug.toDebugString(hbaseGet.getFilter()));
      }
      final Result result = doHBaseGet(hbaseGet);
      LOG.debug("Got {} cells over {} requested", result.size(), pageSize);

      final KeyValue[] kvs = result.raw();
      final String[] qualifiers = new String[kvs.length];
      for (int i = 0; i < kvs.length; ++i) {
        final HBaseColumnName hbaseColumn =
            new HBaseColumnName(kvs[i].getFamily(), kvs[i].getQualifier());
        final KijiColumnName kijiColumn = translator.toKijiColumnName(hbaseColumn);
        qualifiers[i] = kijiColumn.getQualifier();
      }

      // There is an HBase bug that leads to less KeyValue being returned than expected.
      // An empty result appears to be a reliable way to detect the end of the iteration.
      if (result.isEmpty()) {
        mHasNext = false;
      } else {
        // Update the low qualifier bound for the next iteration:
        mMinQualifier = qualifiers[qualifiers.length - 1];
      }

      return qualifiers;

    } catch (IOException ioe) {
      throw new KijiIOException(ioe);
    }
  }

  /**
   * Sends an HBase Get request.
   *
   * @param get HBase Get request.
   * @return the HBase Result.
   * @throws IOException on I/O error.
   */
  private Result doHBaseGet(Get get) throws IOException {
    final HTableInterface htable = mTable.openHTableConnection();
    try {
      return htable.get(get);
    } finally {
      htable.close();
    }
  }

  /** {@inheritDoc} */
  @Override
  public void remove() {
    throw new UnsupportedOperationException("KijiPager.remove() is not supported.");
  }

  /** {@inheritDoc} */
  @Override
  public void close() throws IOException {
    mTable.release();
  }
}
TOP

Related Classes of org.kiji.schema.impl.hbase.HBaseQualifierPager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.