| /** |
| * Copyright 2010 The Apache Software Foundation |
| * |
| * 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.hadoop.hbase.filter; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.hadoop.hbase.KeyValue; |
| import org.apache.hadoop.hbase.util.Bytes; |
| |
| /** |
| * A filter for adding inter-column timestamp matching |
| * Only cells with a correspondingly timestamped entry in |
| * the target column will be retained |
| * Not compatible with Scan.setBatch as operations need |
| * full rows for correct filtering |
| */ |
| public class DependentColumnFilter extends CompareFilter { |
| |
| protected byte[] columnFamily; |
| protected byte[] columnQualifier; |
| protected boolean dropDependentColumn; |
| |
| protected Set<Long> stampSet = new HashSet<Long>(); |
| |
| /** |
| * Should only be used for writable |
| */ |
| public DependentColumnFilter() { |
| } |
| |
| /** |
| * Build a dependent column filter with value checking |
| * dependent column varies will be compared using the supplied |
| * compareOp and comparator, for usage of which |
| * refer to {@link CompareFilter} |
| * |
| * @param family dependent column family |
| * @param qualifier dependent column qualifier |
| * @param dropDependentColumn whether the column should be discarded after |
| * @param valueCompareOp comparison op |
| * @param valueComparator comparator |
| */ |
| public DependentColumnFilter(final byte [] family, final byte[] qualifier, |
| final boolean dropDependentColumn, final CompareOp valueCompareOp, |
| final WritableByteArrayComparable valueComparator) { |
| // set up the comparator |
| super(valueCompareOp, valueComparator); |
| this.columnFamily = family; |
| this.columnQualifier = qualifier; |
| this.dropDependentColumn = dropDependentColumn; |
| } |
| |
| /** |
| * Constructor for DependentColumn filter. |
| * Keyvalues where a keyvalue from target column |
| * with the same timestamp do not exist will be dropped. |
| * |
| * @param family name of target column family |
| * @param qualifier name of column qualifier |
| */ |
| public DependentColumnFilter(final byte [] family, final byte [] qualifier) { |
| this(family, qualifier, false); |
| } |
| |
| /** |
| * Constructor for DependentColumn filter. |
| * Keyvalues where a keyvalue from target column |
| * with the same timestamp do not exist will be dropped. |
| * |
| * @param family name of dependent column family |
| * @param qualifier name of dependent qualifier |
| * @param dropDependentColumn whether the dependent columns keyvalues should be discarded |
| */ |
| public DependentColumnFilter(final byte [] family, final byte [] qualifier, |
| final boolean dropDependentColumn) { |
| this(family, qualifier, dropDependentColumn, CompareOp.NO_OP, null); |
| } |
| |
| |
| @Override |
| public boolean filterAllRemaining() { |
| return false; |
| } |
| |
| @Override |
| public ReturnCode filterKeyValue(KeyValue v) { |
| // Check if the column and qualifier match |
| if (!v.matchingColumn(this.columnFamily, this.columnQualifier)) { |
| // include non-matches for the time being, they'll be discarded afterwards |
| return ReturnCode.INCLUDE; |
| } |
| // If it doesn't pass the op, skip it |
| if(comparator != null && doCompare(compareOp, comparator, v.getValue(), 0, v.getValueLength())) |
| return ReturnCode.SKIP; |
| |
| stampSet.add(v.getTimestamp()); |
| if(dropDependentColumn) { |
| return ReturnCode.SKIP; |
| } |
| return ReturnCode.INCLUDE; |
| } |
| |
| @Override |
| public void filterRow(List<KeyValue> kvs) { |
| Iterator<KeyValue> it = kvs.iterator(); |
| KeyValue kv; |
| while(it.hasNext()) { |
| kv = it.next(); |
| if(!stampSet.contains(kv.getTimestamp())) { |
| it.remove(); |
| } |
| } |
| } |
| |
| @Override |
| public boolean hasFilterRow() { |
| return true; |
| } |
| |
| @Override |
| public boolean filterRow() { |
| return false; |
| } |
| |
| @Override |
| public boolean filterRowKey(byte[] buffer, int offset, int length) { |
| return false; |
| } |
| |
| @Override |
| public void reset() { |
| stampSet.clear(); |
| } |
| |
| @Override |
| public void readFields(DataInput in) throws IOException { |
| super.readFields(in); |
| this.columnFamily = Bytes.readByteArray(in); |
| if(this.columnFamily.length == 0) { |
| this.columnFamily = null; |
| } |
| |
| this.columnQualifier = Bytes.readByteArray(in); |
| if(this.columnQualifier.length == 0) { |
| this.columnQualifier = null; |
| } |
| |
| this.dropDependentColumn = in.readBoolean(); |
| } |
| |
| @Override |
| public void write(DataOutput out) throws IOException { |
| super.write(out); |
| Bytes.writeByteArray(out, this.columnFamily); |
| Bytes.writeByteArray(out, this.columnQualifier); |
| out.writeBoolean(this.dropDependentColumn); |
| } |
| |
| } |