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  
21  package org.apache.hadoop.hbase.filter;
22  
23  import org.apache.hadoop.hbase.KeyValue;
24  import org.apache.hadoop.hbase.util.Bytes;
25  
26  import java.io.DataOutput;
27  import java.io.IOException;
28  import java.io.DataInput;
29  import java.util.ArrayList;
30  
31  import com.google.common.base.Preconditions;
32  
33  /**
34   * This filter is used for selecting only those keys with columns that are
35   * between minColumn to maxColumn. For example, if minColumn is 'an', and
36   * maxColumn is 'be', it will pass keys with columns like 'ana', 'bad', but not
37   * keys with columns like 'bed', 'eye'
38   *
39   * If minColumn is null, there is no lower bound. If maxColumn is null, there is
40   * no upper bound.
41   *
42   * minColumnInclusive and maxColumnInclusive specify if the ranges are inclusive
43   * or not.
44   */
45  public class ColumnRangeFilter extends FilterBase {
46    protected byte[] minColumn = null;
47    protected boolean minColumnInclusive = true;
48    protected byte[] maxColumn = null;
49    protected boolean maxColumnInclusive = false;
50  
51    public ColumnRangeFilter() {
52      super();
53    }
54    /**
55     * Create a filter to select those keys with columns that are between minColumn
56     * and maxColumn.
57     * @param minColumn minimum value for the column range. If if it's null,
58     * there is no lower bound.
59     * @param minColumnInclusive if true, include minColumn in the range.
60     * @param maxColumn maximum value for the column range. If it's null,
61     * @param maxColumnInclusive if true, include maxColumn in the range.
62     * there is no upper bound.
63     */
64    public ColumnRangeFilter(final byte[] minColumn, boolean minColumnInclusive,
65        final byte[] maxColumn, boolean maxColumnInclusive) {
66      this.minColumn = minColumn;
67      this.minColumnInclusive = minColumnInclusive;
68      this.maxColumn = maxColumn;
69      this.maxColumnInclusive = maxColumnInclusive;
70    }
71  
72    /**
73     * @return if min column range is inclusive.
74     */
75    public boolean isMinColumnInclusive() {
76      return minColumnInclusive;
77    }
78  
79    /**
80     * @return if max column range is inclusive.
81     */
82    public boolean isMaxColumnInclusive() {
83      return maxColumnInclusive;
84    }
85  
86    /**
87     * @return the min column range for the filter
88     */
89    public byte[] getMinColumn() {
90      return this.minColumn;
91    }
92  
93    /**
94     * @return true if min column is inclusive, false otherwise
95     */
96    public boolean getMinColumnInclusive() {
97      return this.minColumnInclusive;
98    }
99  
100   /**
101    * @return the max column range for the filter
102    */
103   public byte[] getMaxColumn() {
104     return this.maxColumn;
105   }
106 
107   /**
108    * @return true if max column is inclusive, false otherwise
109    */
110   public boolean getMaxColumnInclusive() {
111     return this.maxColumnInclusive;
112   }
113 
114   @Override
115   public ReturnCode filterKeyValue(KeyValue kv) {
116     byte[] buffer = kv.getBuffer();
117     int qualifierOffset = kv.getQualifierOffset();
118     int qualifierLength = kv.getQualifierLength();
119     int cmpMin = 1;
120 
121     if (this.minColumn != null) {
122       cmpMin = Bytes.compareTo(buffer, qualifierOffset, qualifierLength,
123           this.minColumn, 0, this.minColumn.length);
124     }
125 
126     if (cmpMin < 0) {
127       return ReturnCode.SEEK_NEXT_USING_HINT;
128     }
129 
130     if (!this.minColumnInclusive && cmpMin == 0) {
131       return ReturnCode.SKIP;
132     }
133 
134     if (this.maxColumn == null) {
135       return ReturnCode.INCLUDE;
136     }
137 
138     int cmpMax = Bytes.compareTo(buffer, qualifierOffset, qualifierLength,
139         this.maxColumn, 0, this.maxColumn.length);
140 
141     if (this.maxColumnInclusive && cmpMax <= 0 ||
142         !this.maxColumnInclusive && cmpMax < 0) {
143       return ReturnCode.INCLUDE;
144     }
145 
146     return ReturnCode.NEXT_ROW;
147   }
148 
149   public static Filter createFilterFromArguments(ArrayList<byte []> filterArguments) {
150     Preconditions.checkArgument(filterArguments.size() == 4,
151                                 "Expected 4 but got: %s", filterArguments.size());
152     byte [] minColumn = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
153     boolean minColumnInclusive = ParseFilter.convertByteArrayToBoolean(filterArguments.get(1));
154     byte [] maxColumn = ParseFilter.removeQuotesFromByteArray(filterArguments.get(2));
155     boolean maxColumnInclusive = ParseFilter.convertByteArrayToBoolean(filterArguments.get(3));
156 
157     if (minColumn.length == 0)
158       minColumn = null;
159     if (maxColumn.length == 0)
160       maxColumn = null;
161     return new ColumnRangeFilter(minColumn, minColumnInclusive,
162                                  maxColumn, maxColumnInclusive);
163   }
164 
165   @Override
166   public void write(DataOutput out) throws IOException {
167     // need to write out a flag for null value separately. Otherwise,
168     // we will not be able to differentiate empty string and null
169     out.writeBoolean(this.minColumn == null);
170     Bytes.writeByteArray(out, this.minColumn);
171     out.writeBoolean(this.minColumnInclusive);
172 
173     out.writeBoolean(this.maxColumn == null);
174     Bytes.writeByteArray(out, this.maxColumn);
175     out.writeBoolean(this.maxColumnInclusive);
176   }
177 
178   @Override
179   public void readFields(DataInput in) throws IOException {
180     boolean isMinColumnNull = in.readBoolean();
181     this.minColumn = Bytes.readByteArray(in);
182 
183     if (isMinColumnNull) {
184       this.minColumn = null;
185     }
186 
187     this.minColumnInclusive = in.readBoolean();
188 
189     boolean isMaxColumnNull = in.readBoolean();
190     this.maxColumn = Bytes.readByteArray(in);
191     if (isMaxColumnNull) {
192       this.maxColumn = null;
193     }
194     this.maxColumnInclusive = in.readBoolean();
195   }
196 
197 
198   @Override
199   public KeyValue getNextKeyHint(KeyValue kv) {
200     return KeyValue.createFirstOnRow(kv.getBuffer(), kv.getRowOffset(), kv
201         .getRowLength(), kv.getBuffer(), kv.getFamilyOffset(), kv
202         .getFamilyLength(), this.minColumn, 0, this.minColumn == null ? 0
203         : this.minColumn.length);
204   }
205 
206   @Override
207   public String toString() {
208     return this.getClass().getSimpleName() + " "
209         + (this.minColumnInclusive ? "[" : "(") + Bytes.toStringBinary(this.minColumn)
210         + ", " + Bytes.toStringBinary(this.maxColumn)
211         + (this.maxColumnInclusive ? "]" : ")");
212   }
213 }