Package org.kiji.schema.impl.cassandra

Source Code of org.kiji.schema.impl.cassandra.CassandraKijiResult

/**
* (c) Copyright 2014 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.cassandra;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.ExecutionException;

import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.driver.core.exceptions.DriverInternalError;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Uninterruptibles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.kiji.annotations.ApiAudience;
import org.kiji.schema.EntityId;
import org.kiji.schema.KijiCell;
import org.kiji.schema.KijiColumnName;
import org.kiji.schema.KijiDataRequest;
import org.kiji.schema.KijiDataRequest.Column;
import org.kiji.schema.KijiDataRequestBuilder;
import org.kiji.schema.KijiResult;
import org.kiji.schema.KijiURI;
import org.kiji.schema.NoSuchColumnException;
import org.kiji.schema.cassandra.CassandraColumnName;
import org.kiji.schema.cassandra.CassandraTableName;
import org.kiji.schema.impl.DefaultKijiResult;
import org.kiji.schema.impl.EmptyKijiResult;
import org.kiji.schema.impl.MaterializedKijiResult;
import org.kiji.schema.layout.CassandraColumnNameTranslator;
import org.kiji.schema.layout.KijiTableLayout;
import org.kiji.schema.layout.impl.CellDecoderProvider;
import org.kiji.schema.layout.impl.ColumnId;

/**
* A utility class which can create a {@link KijiResult} view on a Cassandra Kiji table.
*/
@ApiAudience.Private
public final class CassandraKijiResult {
  private static final Logger LOG = LoggerFactory.getLogger(CassandraKijiResult.class);

  /**
   * Create a new {@link KijiResult} backed by Cassandra.
   *
   * @param entityId EntityId of the row from which to read cells.
   * @param dataRequest KijiDataRequest defining the values to retrieve.
   * @param table The table being viewed.
   * @param layout The layout of the table.
   * @param columnTranslator A column name translator for the table.
   * @param decoderProvider A cell decoder provider for the table.
   * @param <T> The type of value in the {@code KijiCell} of this view.
   * @return an {@code KijiResult}.
   * @throws IOException On error while decoding cells.
   */
  public static <T> KijiResult<T> create(
      final EntityId entityId,
      final KijiDataRequest dataRequest,
      final CassandraKijiTable table,
      final KijiTableLayout layout,
      final CassandraColumnNameTranslator columnTranslator,
      final CellDecoderProvider decoderProvider
  ) throws IOException {
    final KijiDataRequestBuilder unpagedRequestBuilder = KijiDataRequest.builder();
    final KijiDataRequestBuilder pagedRequestBuilder = KijiDataRequest.builder();
    unpagedRequestBuilder.withTimeRange(
        dataRequest.getMinTimestamp(),
        dataRequest.getMaxTimestamp());
    pagedRequestBuilder.withTimeRange(dataRequest.getMinTimestamp(), dataRequest.getMaxTimestamp());

    for (final Column columnRequest : dataRequest.getColumns()) {
      if (columnRequest.getFilter() != null) {
        throw new UnsupportedOperationException(
            String.format("Cassandra Kiji does not support filters on column requests: %s.",
                columnRequest));
      }
      if (columnRequest.isPagingEnabled()) {
        pagedRequestBuilder.newColumnsDef(columnRequest);
      } else {
        unpagedRequestBuilder.newColumnsDef(columnRequest);
      }
    }

    final CellDecoderProvider requestDecoderProvider =
        decoderProvider.getDecoderProviderForRequest(dataRequest);

    final KijiDataRequest unpagedRequest = unpagedRequestBuilder.build();
    final KijiDataRequest pagedRequest = pagedRequestBuilder.build();

    if (unpagedRequest.isEmpty() && pagedRequest.isEmpty()) {
      return new EmptyKijiResult<T>(entityId, dataRequest);
    }

    final MaterializedKijiResult<T> materializedKijiResult;
    if (!unpagedRequest.isEmpty()) {
      materializedKijiResult =
          createMaterialized(
              table.getURI(),
              entityId,
              unpagedRequest,
              layout,
              columnTranslator,
              requestDecoderProvider,
              table.getAdmin());
    } else {
      materializedKijiResult = null;
    }

    final CassandraPagedKijiResult<T> pagedKijiResult;
    if (!pagedRequest.isEmpty()) {
      pagedKijiResult =
          new CassandraPagedKijiResult<T>(
              entityId,
              pagedRequest,
              table,
              layout,
              columnTranslator,
              requestDecoderProvider);
    } else {
      pagedKijiResult = null;
    }

    if (unpagedRequest.isEmpty()) {
      return pagedKijiResult;
    } else if (pagedRequest.isEmpty()) {
      return materializedKijiResult;
    } else {
      return DefaultKijiResult.create(dataRequest, materializedKijiResult, pagedKijiResult);
    }
  }

  /**
   * Create a materialized {@code KijiResult} for a get on a Cassandra Kiji table.
   *
   * @param tableURI The table URI.
   * @param entityId The entity ID of the row to get.
   * @param dataRequest The data request defining the columns to get. All columns must be non-paged.
   * @param layout The layout of the table.
   * @param translator A column name translator for the table.
   * @param decoderProvider A decoder provider for the table.
   * @param admin The Cassandra connection.
   * @param <T> The value type of cells in the result.
   * @return A materialized {@code KijiResult} for the row.
   */
  public static <T> MaterializedKijiResult<T> createMaterialized(
      final KijiURI tableURI,
      final EntityId entityId,
      final KijiDataRequest dataRequest,
      final KijiTableLayout layout,
      final CassandraColumnNameTranslator translator,
      final CellDecoderProvider decoderProvider,
      final CassandraAdmin admin
  ) {

    SortedMap<KijiColumnName, ListenableFuture<Iterator<KijiCell<T>>>> resultFutures =
        Maps.newTreeMap();

    for (final Column columnRequest : dataRequest.getColumns()) {
      Preconditions.checkArgument(
          !columnRequest.isPagingEnabled(),
          "CassandraMaterializedKijiResult can not be created with a paged data request: %s.",
          dataRequest);

      resultFutures.put(
          columnRequest.getColumnName(),
          CassandraKijiResult.<T>getColumn(
              tableURI,
              entityId,
              columnRequest,
              dataRequest,
              layout,
              translator,
              decoderProvider,
              admin));
    }

    SortedMap<KijiColumnName, List<KijiCell<T>>> results = Maps.newTreeMap();
    for (Map.Entry<KijiColumnName, ListenableFuture<Iterator<KijiCell<T>>>> entry
        : resultFutures.entrySet()) {

      results.put(
          entry.getKey(),
          Lists.newArrayList(CassandraKijiResult.unwrapFuture(entry.getValue())));
    }

    return MaterializedKijiResult.create(entityId, dataRequest, layout, results);
  }

  // CSOFF: ParameterNumber
  /**
   * Query Cassandra for a Kiji qualified-column or column-family in a Kiji row. The result is a
   * future containing an iterator over the result cells.
   *
   * @param tableURI The table URI.
   * @param entityId The entity ID of the row in the Kiji table.
   * @param columnRequest The requested column.
   * @param dataRequest The data request defining the request options.
   * @param layout The table's layout.
   * @param translator A column name translator for the table.
   * @param decoderProvider A decoder provider for the table.
   * @param admin The Cassandra connection to use for querying.
   * @param <T> The value type of the column.
   * @return A future containing an iterator of cells in the column.
   */
  public static <T> ListenableFuture<Iterator<KijiCell<T>>> getColumn(
      final KijiURI tableURI,
      final EntityId entityId,
      final Column columnRequest,
      final KijiDataRequest dataRequest,
      final KijiTableLayout layout,
      final CassandraColumnNameTranslator translator,
      final CellDecoderProvider decoderProvider,
      final CassandraAdmin admin
  ) {
    final KijiColumnName column = columnRequest.getColumnName();
    final CassandraColumnName cassandraColumn;
    try {
      cassandraColumn = translator.toCassandraColumnName(column);
    } catch (NoSuchColumnException e) {
      throw new IllegalArgumentException(
          String.format("No such column '%s' in table %s.", column, tableURI));
    }

    final ColumnId localityGroupId =
        layout.getFamilyMap().get(column.getFamily()).getLocalityGroup().getId();
    final CassandraTableName table =
        CassandraTableName.getLocalityGroupTableName(tableURI, localityGroupId);

    if (column.isFullyQualified()) {

      final Statement statement =
          CQLUtils.getQualifiedColumnGetStatement(
              layout,
              table,
              entityId,
              cassandraColumn,
              dataRequest,
              columnRequest);

      return Futures.transform(
          admin.executeAsync(statement),
          RowDecoders.<T>getQualifiedColumnDecoderFunction(column, decoderProvider));
    } else {

      if (columnRequest.getMaxVersions() != 0) {
        LOG.warn("Cassandra Kiji can not efficiently get a column family with max versions"
                + " (column family: {}, max version: {}). Filtering versions on the client.",
            column, columnRequest.getMaxVersions());
      }

      if (dataRequest.getMaxTimestamp() != Long.MAX_VALUE
          || dataRequest.getMinTimestamp() != Long.MIN_VALUE) {
        LOG.warn("Cassandra Kiji can not efficiently restrict a timestamp on a column family: "
                + " (column family: {}, data request: {}). Filtering timestamps on the client.",
            column, dataRequest);
      }

      final Statement statement =
          CQLUtils.getColumnFamilyGetStatement(
              layout,
              table,
              entityId,
              cassandraColumn,
              columnRequest);

      return Futures.transform(
          admin.executeAsync(statement),
          RowDecoders.<T>getColumnFamilyDecoderFunction(
              table,
              column,
              columnRequest,
              dataRequest,
              layout,
              translator,
              decoderProvider));
    }
  }
  // CSON: ParameterNumber

  /**
   * Unwrap a Cassandra listenable future.
   *
   * @param future The future to unwrap.
   * @param <T> The value type of the future.
   * @return The future's value.
   */
  public static <T> T unwrapFuture(final ListenableFuture<T> future) {
    // See DefaultResultSetFuture#getUninterruptibly
    try {
      return Uninterruptibles.getUninterruptibly(future);
    } catch (ExecutionException e) {
      final Throwable cause = e.getCause();
      if (cause instanceof DriverException) {
        throw (DriverException) cause;
      } else {
        throw new DriverInternalError("Unexpected exception thrown", cause);
      }
    }
  }

  /**
   * Constructor for non-instantiable helper class.
   */
  private CassandraKijiResult() { }
}
TOP

Related Classes of org.kiji.schema.impl.cassandra.CassandraKijiResult

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.