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.client;
21  
22  import org.apache.hadoop.conf.Configuration;
23  import org.apache.hadoop.hbase.KeyValue;
24  import org.apache.hadoop.hbase.filter.Filter;
25  import org.apache.hadoop.hbase.io.TimeRange;
26  import org.apache.hadoop.hbase.util.Bytes;
27  import org.apache.hadoop.io.Writable;
28  import org.apache.hadoop.io.WritableFactories;
29  
30  import java.io.DataInput;
31  import java.io.DataOutput;
32  import java.io.IOException;
33  import java.util.Map;
34  import java.util.NavigableSet;
35  import java.util.Set;
36  import java.util.TreeMap;
37  import java.util.TreeSet;
38  
39  /**
40   * Used to perform Get operations on a single row.
41   * <p>
42   * To get everything for a row, instantiate a Get object with the row to get.
43   * To further define the scope of what to get, perform additional methods as
44   * outlined below.
45   * <p>
46   * To get all columns from specific families, execute {@link #addFamily(byte[]) addFamily}
47   * for each family to retrieve.
48   * <p>
49   * To get specific columns, execute {@link #addColumn(byte[], byte[]) addColumn}
50   * for each column to retrieve.
51   * <p>
52   * To only retrieve columns within a specific range of version timestamps,
53   * execute {@link #setTimeRange(long, long) setTimeRange}.
54   * <p>
55   * To only retrieve columns with a specific timestamp, execute
56   * {@link #setTimeStamp(long) setTimestamp}.
57   * <p>
58   * To limit the number of versions of each column to be returned, execute
59   * {@link #setMaxVersions(int) setMaxVersions}.
60   * <p>
61   * To add a filter, execute {@link #setFilter(Filter) setFilter}.
62   */
63  public class Get implements Writable, Row, Comparable<Row> {
64    private static final byte GET_VERSION = (byte)1;
65  
66    private byte [] row = null;
67    private long lockId = -1L;
68    private int maxVersions = 1;
69    private boolean cacheBlocks = true;
70    private Filter filter = null;
71    private TimeRange tr = new TimeRange();
72    private Map<byte [], NavigableSet<byte []>> familyMap =
73      new TreeMap<byte [], NavigableSet<byte []>>(Bytes.BYTES_COMPARATOR);
74  
75    /** Constructor for Writable.  DO NOT USE */
76    public Get() {}
77  
78    /**
79     * Create a Get operation for the specified row.
80     * <p>
81     * If no further operations are done, this will get the latest version of
82     * all columns in all families of the specified row.
83     * @param row row key
84     */
85    public Get(byte [] row) {
86      this(row, null);
87    }
88  
89    /**
90     * Create a Get operation for the specified row, using an existing row lock.
91     * <p>
92     * If no further operations are done, this will get the latest version of
93     * all columns in all families of the specified row.
94     * @param row row key
95     * @param rowLock previously acquired row lock, or null
96     */
97    public Get(byte [] row, RowLock rowLock) {
98      this.row = row;
99      if(rowLock != null) {
100       this.lockId = rowLock.getLockId();
101     }
102   }
103 
104   /**
105    * Get all columns from the specified family.
106    * <p>
107    * Overrides previous calls to addColumn for this family.
108    * @param family family name
109    * @return the Get object
110    */
111   public Get addFamily(byte [] family) {
112     familyMap.remove(family);
113     familyMap.put(family, null);
114     return this;
115   }
116 
117   /**
118    * Get the column from the specific family with the specified qualifier.
119    * <p>
120    * Overrides previous calls to addFamily for this family.
121    * @param family family name
122    * @param qualifier column qualifier
123    * @return the Get objec
124    */
125   public Get addColumn(byte [] family, byte [] qualifier) {
126     NavigableSet<byte []> set = familyMap.get(family);
127     if(set == null) {
128       set = new TreeSet<byte []>(Bytes.BYTES_COMPARATOR);
129     }
130     set.add(qualifier);
131     familyMap.put(family, set);
132     return this;
133   }
134 
135   /**
136    * Get versions of columns only within the specified timestamp range,
137    * [minStamp, maxStamp).
138    * @param minStamp minimum timestamp value, inclusive
139    * @param maxStamp maximum timestamp value, exclusive
140    * @throws IOException if invalid time range
141    * @return this for invocation chaining
142    */
143   public Get setTimeRange(long minStamp, long maxStamp)
144   throws IOException {
145     tr = new TimeRange(minStamp, maxStamp);
146     return this;
147   }
148 
149   /**
150    * Get versions of columns with the specified timestamp.
151    * @param timestamp version timestamp
152    * @return this for invocation chaining
153    */
154   public Get setTimeStamp(long timestamp) {
155     try {
156       tr = new TimeRange(timestamp, timestamp+1);
157     } catch(IOException e) {
158       // Will never happen
159     }
160     return this;
161   }
162 
163   /**
164    * Get all available versions.
165    * @return this for invocation chaining
166    */
167   public Get setMaxVersions() {
168     this.maxVersions = Integer.MAX_VALUE;
169     return this;
170   }
171 
172   /**
173    * Get up to the specified number of versions of each column.
174    * @param maxVersions maximum versions for each column
175    * @throws IOException if invalid number of versions
176    * @return this for invocation chaining
177    */
178   public Get setMaxVersions(int maxVersions) throws IOException {
179     if(maxVersions <= 0) {
180       throw new IOException("maxVersions must be positive");
181     }
182     this.maxVersions = maxVersions;
183     return this;
184   }
185 
186   /**
187    * Apply the specified server-side filter when performing the Get.
188    * Only {@link Filter#filterKeyValue(KeyValue)} is called AFTER all tests
189    * for ttl, column match, deletes and max versions have been run.
190    * @param filter filter to run on the server
191    * @return this for invocation chaining
192    */
193   public Get setFilter(Filter filter) {
194     this.filter = filter;
195     return this;
196   }
197 
198   /* Accessors */
199 
200   /**
201    * @return Filter
202    */
203   public Filter getFilter() {
204     return this.filter;
205   }
206 
207   /**
208    * Set whether blocks should be cached for this Get.
209    * <p>
210    * This is true by default.  When true, default settings of the table and
211    * family are used (this will never override caching blocks if the block
212    * cache is disabled for that family or entirely).
213    *
214    * @param cacheBlocks if false, default settings are overridden and blocks
215    * will not be cached
216    */
217   public void setCacheBlocks(boolean cacheBlocks) {
218     this.cacheBlocks = cacheBlocks;
219   }
220 
221   /**
222    * Get whether blocks should be cached for this Get.
223    * @return true if default caching should be used, false if blocks should not
224    * be cached
225    */
226   public boolean getCacheBlocks() {
227     return cacheBlocks;
228   }
229 
230   /**
231    * Method for retrieving the get's row
232    * @return row
233    */
234   public byte [] getRow() {
235     return this.row;
236   }
237 
238   /**
239    * Method for retrieving the get's RowLock
240    * @return RowLock
241    */
242   public RowLock getRowLock() {
243     return new RowLock(this.row, this.lockId);
244   }
245 
246   /**
247    * Method for retrieving the get's lockId
248    * @return lockId
249    */
250   public long getLockId() {
251     return this.lockId;
252   }
253 
254   /**
255    * Method for retrieving the get's maximum number of version
256    * @return the maximum number of version to fetch for this get
257    */
258   public int getMaxVersions() {
259     return this.maxVersions;
260   }
261 
262   /**
263    * Method for retrieving the get's TimeRange
264    * @return timeRange
265    */
266   public TimeRange getTimeRange() {
267     return this.tr;
268   }
269 
270   /**
271    * Method for retrieving the keys in the familyMap
272    * @return keys in the current familyMap
273    */
274   public Set<byte[]> familySet() {
275     return this.familyMap.keySet();
276   }
277 
278   /**
279    * Method for retrieving the number of families to get from
280    * @return number of families
281    */
282   public int numFamilies() {
283     return this.familyMap.size();
284   }
285 
286   /**
287    * Method for checking if any families have been inserted into this Get
288    * @return true if familyMap is non empty false otherwise
289    */
290   public boolean hasFamilies() {
291     return !this.familyMap.isEmpty();
292   }
293 
294   /**
295    * Method for retrieving the get's familyMap
296    * @return familyMap
297    */
298   public Map<byte[],NavigableSet<byte[]>> getFamilyMap() {
299     return this.familyMap;
300   }
301 
302   /**
303    * @return String
304    */
305   @Override
306   public String toString() {
307     StringBuilder sb = new StringBuilder();
308     sb.append("row=");
309     sb.append(Bytes.toStringBinary(this.row));
310     sb.append(", maxVersions=");
311     sb.append("").append(this.maxVersions);
312     sb.append(", cacheBlocks=");
313     sb.append(this.cacheBlocks);
314     sb.append(", timeRange=");
315     sb.append("[").append(this.tr.getMin()).append(",");
316     sb.append(this.tr.getMax()).append(")");
317     sb.append(", families=");
318     if(this.familyMap.size() == 0) {
319       sb.append("ALL");
320       return sb.toString();
321     }
322     boolean moreThanOne = false;
323     for(Map.Entry<byte [], NavigableSet<byte[]>> entry :
324       this.familyMap.entrySet()) {
325       if(moreThanOne) {
326         sb.append("), ");
327       } else {
328         moreThanOne = true;
329         sb.append("{");
330       }
331       sb.append("(family=");
332       sb.append(Bytes.toString(entry.getKey()));
333       sb.append(", columns=");
334       if(entry.getValue() == null) {
335         sb.append("ALL");
336       } else {
337         sb.append("{");
338         boolean moreThanOneB = false;
339         for(byte [] column : entry.getValue()) {
340           if(moreThanOneB) {
341             sb.append(", ");
342           } else {
343             moreThanOneB = true;
344           }
345           sb.append(Bytes.toStringBinary(column));
346         }
347         sb.append("}");
348       }
349     }
350     sb.append("}");
351     return sb.toString();
352   }
353 
354   //Row
355   public int compareTo(Row other) {
356     return Bytes.compareTo(this.getRow(), other.getRow());
357   }
358   
359   //Writable
360   public void readFields(final DataInput in)
361   throws IOException {
362     int version = in.readByte();
363     if (version > GET_VERSION) {
364       throw new IOException("unsupported version");
365     }
366     this.row = Bytes.readByteArray(in);
367     this.lockId = in.readLong();
368     this.maxVersions = in.readInt();
369     boolean hasFilter = in.readBoolean();
370     if (hasFilter) {
371       this.filter = (Filter)createForName(Bytes.toString(Bytes.readByteArray(in)));
372       this.filter.readFields(in);
373     }
374     this.cacheBlocks = in.readBoolean();
375     this.tr = new TimeRange();
376     tr.readFields(in);
377     int numFamilies = in.readInt();
378     this.familyMap =
379       new TreeMap<byte [],NavigableSet<byte []>>(Bytes.BYTES_COMPARATOR);
380     for(int i=0; i<numFamilies; i++) {
381       byte [] family = Bytes.readByteArray(in);
382       boolean hasColumns = in.readBoolean();
383       NavigableSet<byte []> set = null;
384       if(hasColumns) {
385         int numColumns = in.readInt();
386         set = new TreeSet<byte []>(Bytes.BYTES_COMPARATOR);
387         for(int j=0; j<numColumns; j++) {
388           byte [] qualifier = Bytes.readByteArray(in);
389           set.add(qualifier);
390         }
391       }
392       this.familyMap.put(family, set);
393     }
394   }
395 
396   public void write(final DataOutput out)
397   throws IOException {
398     out.writeByte(GET_VERSION);
399     Bytes.writeByteArray(out, this.row);
400     out.writeLong(this.lockId);
401     out.writeInt(this.maxVersions);
402     if(this.filter == null) {
403       out.writeBoolean(false);
404     } else {
405       out.writeBoolean(true);
406       Bytes.writeByteArray(out, Bytes.toBytes(filter.getClass().getName()));
407       filter.write(out);
408     }
409     out.writeBoolean(this.cacheBlocks);
410     tr.write(out);
411     out.writeInt(familyMap.size());
412     for(Map.Entry<byte [], NavigableSet<byte []>> entry :
413       familyMap.entrySet()) {
414       Bytes.writeByteArray(out, entry.getKey());
415       NavigableSet<byte []> columnSet = entry.getValue();
416       if(columnSet == null) {
417         out.writeBoolean(false);
418       } else {
419         out.writeBoolean(true);
420         out.writeInt(columnSet.size());
421         for(byte [] qualifier : columnSet) {
422           Bytes.writeByteArray(out, qualifier);
423         }
424       }
425     }
426   }
427 
428   @SuppressWarnings("unchecked")
429   private Writable createForName(String className) {
430     try {
431       Class<? extends Writable> clazz =
432         (Class<? extends Writable>) Class.forName(className);
433       return WritableFactories.newInstance(clazz, new Configuration());
434     } catch (ClassNotFoundException e) {
435       throw new RuntimeException("Can't find class " + className);
436     }
437   }
438 
439   /**
440    * Adds an array of columns specified the old format, family:qualifier.
441    * <p>
442    * Overrides previous calls to addFamily for any families in the input.
443    * @param columns array of columns, formatted as <pre>family:qualifier</pre>
444    * @deprecated issue multiple {@link #addColumn(byte[], byte[])} instead
445    * @return this for invocation chaining
446    */
447   @SuppressWarnings({"deprecation"})
448   public Get addColumns(byte [][] columns) {
449     if (columns == null) return this;
450     for (byte[] column : columns) {
451       try {
452         addColumn(column);
453       } catch (Exception ignored) {
454       }
455     }
456     return this;
457   }
458 
459   /**
460    *
461    * @param column Old format column.
462    * @return This.
463    * @deprecated use {@link #addColumn(byte[], byte[])} instead
464    */
465   public Get addColumn(final byte [] column) {
466     if (column == null) return this;
467     byte [][] split = KeyValue.parseColumn(column);
468     if (split.length > 1 && split[1] != null && split[1].length > 0) {
469       addColumn(split[0], split[1]);
470     } else {
471       addFamily(split[0]);
472     }
473     return this;
474   }
475 }