View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.filter;
21  
22  import java.io.DataInput;
23  import java.io.DataOutput;
24  import java.io.IOException;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Set;
29  import java.util.ArrayList;
30  
31  import org.apache.hadoop.hbase.KeyValue;
32  import org.apache.hadoop.hbase.util.Bytes;
33  
34  import com.google.common.base.Preconditions;
35  
36  /**
37   * A filter for adding inter-column timestamp matching
38   * Only cells with a correspondingly timestamped entry in
39   * the target column will be retained
40   * Not compatible with Scan.setBatch as operations need 
41   * full rows for correct filtering 
42   */
43  public class DependentColumnFilter extends CompareFilter {
44  
45    protected byte[] columnFamily;
46    protected byte[] columnQualifier;
47    protected boolean dropDependentColumn;
48  
49    protected Set<Long> stampSet = new HashSet<Long>();
50    
51    /**
52     * Should only be used for writable
53     */
54    public DependentColumnFilter() {
55    }
56    
57    /**
58     * Build a dependent column filter with value checking
59     * dependent column varies will be compared using the supplied
60     * compareOp and comparator, for usage of which
61     * refer to {@link CompareFilter}
62     * 
63     * @param family dependent column family
64     * @param qualifier dependent column qualifier
65     * @param dropDependentColumn whether the column should be discarded after
66     * @param valueCompareOp comparison op 
67     * @param valueComparator comparator
68     */
69    public DependentColumnFilter(final byte [] family, final byte[] qualifier,
70  		  final boolean dropDependentColumn, final CompareOp valueCompareOp,
71  	      final WritableByteArrayComparable valueComparator) {
72      // set up the comparator   
73      super(valueCompareOp, valueComparator);
74      this.columnFamily = family;
75      this.columnQualifier = qualifier;
76      this.dropDependentColumn = dropDependentColumn;
77    }
78    
79    /**
80     * Constructor for DependentColumn filter.
81     * Keyvalues where a keyvalue from target column 
82     * with the same timestamp do not exist will be dropped. 
83     * 
84     * @param family name of target column family
85     * @param qualifier name of column qualifier
86     */
87    public DependentColumnFilter(final byte [] family, final byte [] qualifier) {
88      this(family, qualifier, false);
89    }
90    
91    /**
92     * Constructor for DependentColumn filter.
93     * Keyvalues where a keyvalue from target column 
94     * with the same timestamp do not exist will be dropped. 
95     * 
96     * @param family name of dependent column family
97     * @param qualifier name of dependent qualifier
98     * @param dropDependentColumn whether the dependent columns keyvalues should be discarded
99     */
100   public DependentColumnFilter(final byte [] family, final byte [] qualifier,
101       final boolean dropDependentColumn) {
102     this(family, qualifier, dropDependentColumn, CompareOp.NO_OP, null);
103   }
104 
105   /**
106    * @return the column family
107    */
108   public byte[] getFamily() {
109     return this.columnFamily;
110   }
111 
112   /**
113    * @return the column qualifier
114    */
115   public byte[] getQualifier() {
116     return this.columnQualifier;
117   }
118 
119   /**
120    * @return true if we should drop the dependent column, false otherwise
121    */
122   public boolean dropDependentColumn() {
123     return this.dropDependentColumn;
124   }
125 
126   public boolean getDropDependentColumn() {
127     return this.dropDependentColumn;
128   }
129 
130   @Override
131   public boolean filterAllRemaining() {
132     return false;
133   }
134 
135   @Override
136   public ReturnCode filterKeyValue(KeyValue v) {
137     // Check if the column and qualifier match
138   	if (!v.matchingColumn(this.columnFamily, this.columnQualifier)) {
139         // include non-matches for the time being, they'll be discarded afterwards
140         return ReturnCode.INCLUDE;
141   	}
142     // If it doesn't pass the op, skip it
143     if (comparator != null
144         && doCompare(compareOp, comparator, v.getBuffer(), v.getValueOffset(),
145             v.getValueLength()))
146       return ReturnCode.SKIP;
147 	
148     stampSet.add(v.getTimestamp());
149     if(dropDependentColumn) {
150     	return ReturnCode.SKIP;
151     }
152     return ReturnCode.INCLUDE;
153   }
154 
155   @Override
156   public void filterRow(List<KeyValue> kvs) {
157     Iterator<KeyValue> it = kvs.iterator();
158     KeyValue kv;
159     while(it.hasNext()) {
160       kv = it.next();
161       if(!stampSet.contains(kv.getTimestamp())) {
162         it.remove();
163       }
164     }
165   }
166 
167   @Override
168   public boolean hasFilterRow() {
169     return true;
170   }
171   
172   @Override
173   public boolean filterRow() {
174     return false;
175   }
176 
177   @Override
178   public boolean filterRowKey(byte[] buffer, int offset, int length) {
179     return false;
180   }
181   @Override
182   public void reset() {
183     stampSet.clear();    
184   }
185 
186   public static Filter createFilterFromArguments(ArrayList<byte []> filterArguments) {
187     Preconditions.checkArgument(filterArguments.size() == 2 ||
188                                 filterArguments.size() == 3 ||
189                                 filterArguments.size() == 5,
190                                 "Expected 2, 3 or 5 but got: %s", filterArguments.size());
191     if (filterArguments.size() == 2) {
192       byte [] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
193       byte [] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1));
194       return new DependentColumnFilter(family, qualifier);
195 
196     } else if (filterArguments.size() == 3) {
197       byte [] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
198       byte [] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1));
199       boolean dropDependentColumn = ParseFilter.convertByteArrayToBoolean(filterArguments.get(2));
200       return new DependentColumnFilter(family, qualifier, dropDependentColumn);
201 
202     } else if (filterArguments.size() == 5) {
203       byte [] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
204       byte [] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1));
205       boolean dropDependentColumn = ParseFilter.convertByteArrayToBoolean(filterArguments.get(2));
206       CompareOp compareOp = ParseFilter.createCompareOp(filterArguments.get(3));
207       WritableByteArrayComparable comparator = ParseFilter.createComparator(
208         ParseFilter.removeQuotesFromByteArray(filterArguments.get(4)));
209       return new DependentColumnFilter(family, qualifier, dropDependentColumn,
210                                        compareOp, comparator);
211     } else {
212       throw new IllegalArgumentException("Expected 2, 3 or 5 but got: " + filterArguments.size());
213     }
214   }
215 
216   @Override
217   public void readFields(DataInput in) throws IOException {
218 	super.readFields(in);
219     this.columnFamily = Bytes.readByteArray(in);
220 	if(this.columnFamily.length == 0) {
221 	  this.columnFamily = null;
222 	}
223     
224     this.columnQualifier = Bytes.readByteArray(in);
225     if(this.columnQualifier.length == 0) {
226       this.columnQualifier = null;
227     }	
228     
229     this.dropDependentColumn = in.readBoolean();
230   }
231 
232   @Override
233   public void write(DataOutput out) throws IOException {
234     super.write(out);
235     Bytes.writeByteArray(out, this.columnFamily);
236     Bytes.writeByteArray(out, this.columnQualifier);
237     out.writeBoolean(this.dropDependentColumn);    
238   }
239 
240   @Override
241   public String toString() {
242     return String.format("%s (%s, %s, %s, %s, %s)",
243         this.getClass().getSimpleName(),
244         Bytes.toStringBinary(this.columnFamily),
245         Bytes.toStringBinary(this.columnQualifier),
246         this.dropDependentColumn,
247         this.compareOp.name(),
248         this.comparator != null ? Bytes.toStringBinary(this.comparator.getValue()) : "null");
249   }
250 }