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 && doCompare(compareOp, comparator, v.getValue(), 0, v.getValueLength()))
144   	  return ReturnCode.SKIP;  	  
145 	
146     stampSet.add(v.getTimestamp());
147     if(dropDependentColumn) {
148     	return ReturnCode.SKIP;
149     }
150     return ReturnCode.INCLUDE;
151   }
152 
153   @Override
154   public void filterRow(List<KeyValue> kvs) {
155     Iterator<KeyValue> it = kvs.iterator();
156     KeyValue kv;
157     while(it.hasNext()) {
158       kv = it.next();
159       if(!stampSet.contains(kv.getTimestamp())) {
160         it.remove();
161       }
162     }
163   }
164 
165   @Override
166   public boolean hasFilterRow() {
167     return true;
168   }
169   
170   @Override
171   public boolean filterRow() {
172     return false;
173   }
174 
175   @Override
176   public boolean filterRowKey(byte[] buffer, int offset, int length) {
177     return false;
178   }
179   @Override
180   public void reset() {
181     stampSet.clear();    
182   }
183 
184   public static Filter createFilterFromArguments(ArrayList<byte []> filterArguments) {
185     Preconditions.checkArgument(filterArguments.size() == 2 ||
186                                 filterArguments.size() == 3 ||
187                                 filterArguments.size() == 5,
188                                 "Expected 2, 3 or 5 but got: %s", filterArguments.size());
189     if (filterArguments.size() == 2) {
190       byte [] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
191       byte [] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1));
192       return new DependentColumnFilter(family, qualifier);
193 
194     } else if (filterArguments.size() == 3) {
195       byte [] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
196       byte [] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1));
197       boolean dropDependentColumn = ParseFilter.convertByteArrayToBoolean(filterArguments.get(2));
198       return new DependentColumnFilter(family, qualifier, dropDependentColumn);
199 
200     } else if (filterArguments.size() == 5) {
201       byte [] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
202       byte [] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1));
203       boolean dropDependentColumn = ParseFilter.convertByteArrayToBoolean(filterArguments.get(2));
204       CompareOp compareOp = ParseFilter.createCompareOp(filterArguments.get(3));
205       WritableByteArrayComparable comparator = ParseFilter.createComparator(
206         ParseFilter.removeQuotesFromByteArray(filterArguments.get(4)));
207       return new DependentColumnFilter(family, qualifier, dropDependentColumn,
208                                        compareOp, comparator);
209     } else {
210       throw new IllegalArgumentException("Expected 2, 3 or 5 but got: " + filterArguments.size());
211     }
212   }
213 
214   @Override
215   public void readFields(DataInput in) throws IOException {
216 	super.readFields(in);
217     this.columnFamily = Bytes.readByteArray(in);
218 	if(this.columnFamily.length == 0) {
219 	  this.columnFamily = null;
220 	}
221     
222     this.columnQualifier = Bytes.readByteArray(in);
223     if(this.columnQualifier.length == 0) {
224       this.columnQualifier = null;
225     }	
226     
227     this.dropDependentColumn = in.readBoolean();
228   }
229 
230   @Override
231   public void write(DataOutput out) throws IOException {
232     super.write(out);
233     Bytes.writeByteArray(out, this.columnFamily);
234     Bytes.writeByteArray(out, this.columnQualifier);
235     out.writeBoolean(this.dropDependentColumn);    
236   }
237 
238 }