View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.NavigableSet;
28  import java.util.Set;
29  import java.util.TreeMap;
30  import java.util.TreeSet;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.hbase.classification.InterfaceAudience;
35  import org.apache.hadoop.hbase.classification.InterfaceStability;
36  import org.apache.hadoop.hbase.HConstants;
37  import org.apache.hadoop.hbase.filter.Filter;
38  import org.apache.hadoop.hbase.io.TimeRange;
39  import org.apache.hadoop.hbase.security.access.Permission;
40  import org.apache.hadoop.hbase.security.visibility.Authorizations;
41  import org.apache.hadoop.hbase.util.Bytes;
42  
43  /**
44   * Used to perform Get operations on a single row.
45   * <p>
46   * To get everything for a row, instantiate a Get object with the row to get.
47   * To further narrow the scope of what to Get, use the methods below.
48   * <p>
49   * To get all columns from specific families, execute {@link #addFamily(byte[]) addFamily}
50   * for each family to retrieve.
51   * <p>
52   * To get specific columns, execute {@link #addColumn(byte[], byte[]) addColumn}
53   * for each column to retrieve.
54   * <p>
55   * To only retrieve columns within a specific range of version timestamps,
56   * execute {@link #setTimeRange(long, long) setTimeRange}.
57   * <p>
58   * To only retrieve columns with a specific timestamp, execute
59   * {@link #setTimeStamp(long) setTimestamp}.
60   * <p>
61   * To limit the number of versions of each column to be returned, execute
62   * {@link #setMaxVersions(int) setMaxVersions}.
63   * <p>
64   * To add a filter, call {@link #setFilter(Filter) setFilter}.
65   */
66  @InterfaceAudience.Public
67  @InterfaceStability.Stable
68  public class Get extends Query
69    implements Row, Comparable<Row> {
70    private static final Log LOG = LogFactory.getLog(Get.class);
71  
72    private byte [] row = null;
73    private int maxVersions = 1;
74    private boolean cacheBlocks = true;
75    private int storeLimit = -1;
76    private int storeOffset = 0;
77    private TimeRange tr = new TimeRange();
78    private boolean checkExistenceOnly = false;
79    private boolean closestRowBefore = false;
80    private Map<byte [], NavigableSet<byte []>> familyMap =
81      new TreeMap<byte [], NavigableSet<byte []>>(Bytes.BYTES_COMPARATOR);
82  
83    /**
84     * Create a Get operation for the specified row.
85     * <p>
86     * If no further operations are done, this will get the latest version of
87     * all columns in all families of the specified row.
88     * @param row row key
89     */
90    public Get(byte [] row) {
91      Mutation.checkRow(row);
92      this.row = row;
93    }
94  
95    /**
96     * Copy-constructor
97     *
98     * @param get
99     */
100   public Get(Get get) {
101     this(get.getRow());
102     // from Query
103     this.setFilter(get.getFilter());
104     this.setReplicaId(get.getReplicaId());
105     this.setConsistency(get.getConsistency());
106     // from Get
107     this.cacheBlocks = get.getCacheBlocks();
108     this.maxVersions = get.getMaxVersions();
109     this.storeLimit = get.getMaxResultsPerColumnFamily();
110     this.storeOffset = get.getRowOffsetPerColumnFamily();
111     this.tr = get.getTimeRange();
112     this.checkExistenceOnly = get.isCheckExistenceOnly();
113     this.loadColumnFamiliesOnDemand = get.getLoadColumnFamiliesOnDemandValue();
114     this.closestRowBefore = get.isClosestRowBefore();
115     Map<byte[], NavigableSet<byte[]>> fams = get.getFamilyMap();
116     for (Map.Entry<byte[],NavigableSet<byte[]>> entry : fams.entrySet()) {
117       byte [] fam = entry.getKey();
118       NavigableSet<byte[]> cols = entry.getValue();
119       if (cols != null && cols.size() > 0) {
120         for (byte[] col : cols) {
121           addColumn(fam, col);
122         }
123       } else {
124         addFamily(fam);
125       }
126     }
127     for (Map.Entry<String, byte[]> attr : get.getAttributesMap().entrySet()) {
128       setAttribute(attr.getKey(), attr.getValue());
129     }
130     for (Map.Entry<byte[], TimeRange> entry : get.getColumnFamilyTimeRange().entrySet()) {
131       TimeRange tr = entry.getValue();
132       setColumnFamilyTimeRange(entry.getKey(), tr.getMin(), tr.getMax());
133     }
134     super.setPriority(get.getPriority());
135   }
136 
137   public boolean isCheckExistenceOnly() {
138     return checkExistenceOnly;
139   }
140 
141   public Get setCheckExistenceOnly(boolean checkExistenceOnly) {
142     this.checkExistenceOnly = checkExistenceOnly;
143     return this;
144   }
145 
146   public boolean isClosestRowBefore() {
147     return closestRowBefore;
148   }
149 
150   public Get setClosestRowBefore(boolean closestRowBefore) {
151     this.closestRowBefore = closestRowBefore;
152     return this;
153   }
154 
155   /**
156    * Get all columns from the specified family.
157    * <p>
158    * Overrides previous calls to addColumn for this family.
159    * @param family family name
160    * @return the Get object
161    */
162   public Get addFamily(byte [] family) {
163     familyMap.remove(family);
164     familyMap.put(family, null);
165     return this;
166   }
167 
168   /**
169    * Get the column from the specific family with the specified qualifier.
170    * <p>
171    * Overrides previous calls to addFamily for this family.
172    * @param family family name
173    * @param qualifier column qualifier
174    * @return the Get objec
175    */
176   public Get addColumn(byte [] family, byte [] qualifier) {
177     NavigableSet<byte []> set = familyMap.get(family);
178     if(set == null) {
179       set = new TreeSet<byte []>(Bytes.BYTES_COMPARATOR);
180       familyMap.put(family, set);
181     }
182     if (qualifier == null) {
183       qualifier = HConstants.EMPTY_BYTE_ARRAY;
184     }
185     set.add(qualifier);
186     return this;
187   }
188 
189   /**
190    * Get versions of columns only within the specified timestamp range,
191    * [minStamp, maxStamp).
192    * @param minStamp minimum timestamp value, inclusive
193    * @param maxStamp maximum timestamp value, exclusive
194    * @throws IOException
195    * @return this for invocation chaining
196    */
197   public Get setTimeRange(long minStamp, long maxStamp) throws IOException {
198     tr = new TimeRange(minStamp, maxStamp);
199     return this;
200   }
201 
202   /**
203    * Get versions of columns with the specified timestamp.
204    * @param timestamp version timestamp
205    * @return this for invocation chaining
206    */
207   public Get setTimeStamp(long timestamp)
208   throws IOException {
209     try {
210       tr = new TimeRange(timestamp, timestamp+1);
211     } catch(Exception e) {
212       // This should never happen, unless integer overflow or something extremely wrong...
213       LOG.error("TimeRange failed, likely caused by integer overflow. ", e);
214       throw e;
215     }
216     return this;
217   }
218 
219   @Override public Get setColumnFamilyTimeRange(byte[] cf, long minStamp, long maxStamp) {
220     return (Get) super.setColumnFamilyTimeRange(cf, minStamp, maxStamp);
221   }
222 
223   /**
224    * Get all available versions.
225    * @return this for invocation chaining
226    */
227   public Get setMaxVersions() {
228     this.maxVersions = Integer.MAX_VALUE;
229     return this;
230   }
231 
232   /**
233    * Get up to the specified number of versions of each column.
234    * @param maxVersions maximum versions for each column
235    * @throws IOException if invalid number of versions
236    * @return this for invocation chaining
237    */
238   public Get setMaxVersions(int maxVersions) throws IOException {
239     if(maxVersions <= 0) {
240       throw new IOException("maxVersions must be positive");
241     }
242     this.maxVersions = maxVersions;
243     return this;
244   }
245 
246   @Override
247   public Get setLoadColumnFamiliesOnDemand(boolean value) {
248     return (Get) super.setLoadColumnFamiliesOnDemand(value);
249   }
250 
251   /**
252    * Set the maximum number of values to return per row per Column Family
253    * @param limit the maximum number of values returned / row / CF
254    * @return this for invocation chaining
255    */
256   public Get setMaxResultsPerColumnFamily(int limit) {
257     this.storeLimit = limit;
258     return this;
259   }
260 
261   /**
262    * Set offset for the row per Column Family. This offset is only within a particular row/CF
263    * combination. It gets reset back to zero when we move to the next row or CF.
264    * @param offset is the number of kvs that will be skipped.
265    * @return this for invocation chaining
266    */
267   public Get setRowOffsetPerColumnFamily(int offset) {
268     this.storeOffset = offset;
269     return this;
270   }
271 
272   @Override
273   public Get setFilter(Filter filter) {
274     super.setFilter(filter);
275     return this;
276   }
277 
278   /* Accessors */
279 
280   /**
281    * Set whether blocks should be cached for this Get.
282    * <p>
283    * This is true by default.  When true, default settings of the table and
284    * family are used (this will never override caching blocks if the block
285    * cache is disabled for that family or entirely).
286    *
287    * @param cacheBlocks if false, default settings are overridden and blocks
288    * will not be cached
289    */
290   public Get setCacheBlocks(boolean cacheBlocks) {
291     this.cacheBlocks = cacheBlocks;
292     return this;
293   }
294 
295   /**
296    * Get whether blocks should be cached for this Get.
297    * @return true if default caching should be used, false if blocks should not
298    * be cached
299    */
300   public boolean getCacheBlocks() {
301     return cacheBlocks;
302   }
303 
304   /**
305    * Method for retrieving the get's row
306    * @return row
307    */
308   @Override
309   public byte [] getRow() {
310     return this.row;
311   }
312 
313   /**
314    * Method for retrieving the get's maximum number of version
315    * @return the maximum number of version to fetch for this get
316    */
317   public int getMaxVersions() {
318     return this.maxVersions;
319   }
320 
321   /**
322    * Method for retrieving the get's maximum number of values
323    * to return per Column Family
324    * @return the maximum number of values to fetch per CF
325    */
326   public int getMaxResultsPerColumnFamily() {
327     return this.storeLimit;
328   }
329 
330   /**
331    * Method for retrieving the get's offset per row per column
332    * family (#kvs to be skipped)
333    * @return the row offset
334    */
335   public int getRowOffsetPerColumnFamily() {
336     return this.storeOffset;
337   }
338 
339   /**
340    * Method for retrieving the get's TimeRange
341    * @return timeRange
342    */
343   public TimeRange getTimeRange() {
344     return this.tr;
345   }
346 
347   /**
348    * Method for retrieving the keys in the familyMap
349    * @return keys in the current familyMap
350    */
351   public Set<byte[]> familySet() {
352     return this.familyMap.keySet();
353   }
354 
355   /**
356    * Method for retrieving the number of families to get from
357    * @return number of families
358    */
359   public int numFamilies() {
360     return this.familyMap.size();
361   }
362 
363   /**
364    * Method for checking if any families have been inserted into this Get
365    * @return true if familyMap is non empty false otherwise
366    */
367   public boolean hasFamilies() {
368     return !this.familyMap.isEmpty();
369   }
370 
371   /**
372    * Method for retrieving the get's familyMap
373    * @return familyMap
374    */
375   public Map<byte[],NavigableSet<byte[]>> getFamilyMap() {
376     return this.familyMap;
377   }
378 
379   /**
380    * Compile the table and column family (i.e. schema) information
381    * into a String. Useful for parsing and aggregation by debugging,
382    * logging, and administration tools.
383    * @return Map
384    */
385   @Override
386   public Map<String, Object> getFingerprint() {
387     Map<String, Object> map = new HashMap<String, Object>();
388     List<String> families = new ArrayList<String>();
389     map.put("families", families);
390     for (Map.Entry<byte [], NavigableSet<byte[]>> entry :
391       this.familyMap.entrySet()) {
392       families.add(Bytes.toStringBinary(entry.getKey()));
393     }
394     return map;
395   }
396 
397   /**
398    * Compile the details beyond the scope of getFingerprint (row, columns,
399    * timestamps, etc.) into a Map along with the fingerprinted information.
400    * Useful for debugging, logging, and administration tools.
401    * @param maxCols a limit on the number of columns output prior to truncation
402    * @return Map
403    */
404   @Override
405   public Map<String, Object> toMap(int maxCols) {
406     // we start with the fingerprint map and build on top of it.
407     Map<String, Object> map = getFingerprint();
408     // replace the fingerprint's simple list of families with a
409     // map from column families to lists of qualifiers and kv details
410     Map<String, List<String>> columns = new HashMap<String, List<String>>();
411     map.put("families", columns);
412     // add scalar information first
413     map.put("row", Bytes.toStringBinary(this.row));
414     map.put("maxVersions", this.maxVersions);
415     map.put("cacheBlocks", this.cacheBlocks);
416     List<Long> timeRange = new ArrayList<Long>();
417     timeRange.add(this.tr.getMin());
418     timeRange.add(this.tr.getMax());
419     map.put("timeRange", timeRange);
420     int colCount = 0;
421     // iterate through affected families and add details
422     for (Map.Entry<byte [], NavigableSet<byte[]>> entry :
423       this.familyMap.entrySet()) {
424       List<String> familyList = new ArrayList<String>();
425       columns.put(Bytes.toStringBinary(entry.getKey()), familyList);
426       if(entry.getValue() == null) {
427         colCount++;
428         --maxCols;
429         familyList.add("ALL");
430       } else {
431         colCount += entry.getValue().size();
432         if (maxCols <= 0) {
433           continue;
434         }
435         for (byte [] column : entry.getValue()) {
436           if (--maxCols <= 0) {
437             continue;
438           }
439           familyList.add(Bytes.toStringBinary(column));
440         }
441       }
442     }
443     map.put("totalColumns", colCount);
444     if (this.filter != null) {
445       map.put("filter", this.filter.toString());
446     }
447     // add the id if set
448     if (getId() != null) {
449       map.put("id", getId());
450     }
451     return map;
452   }
453 
454   //Row
455   @Override
456   public int compareTo(Row other) {
457     // TODO: This is wrong.  Can't have two gets the same just because on same row.
458     return Bytes.compareTo(this.getRow(), other.getRow());
459   }
460 
461   @Override
462   public int hashCode() {
463     // TODO: This is wrong.  Can't have two gets the same just because on same row.  But it
464     // matches how equals works currently and gets rid of the findbugs warning.
465     return Bytes.hashCode(this.getRow());
466   }
467 
468   @Override
469   public boolean equals(Object obj) {
470     if (this == obj) {
471       return true;
472     }
473     if (obj == null || getClass() != obj.getClass()) {
474       return false;
475     }
476     Row other = (Row) obj;
477     // TODO: This is wrong.  Can't have two gets the same just because on same row.
478     return compareTo(other) == 0;
479   }
480 
481   @Override
482   public Get setAttribute(String name, byte[] value) {
483     return (Get) super.setAttribute(name, value);
484   }
485 
486   @Override
487   public Get setId(String id) {
488     return (Get) super.setId(id);
489   }
490 
491   @Override
492   public Get setAuthorizations(Authorizations authorizations) {
493     return (Get) super.setAuthorizations(authorizations);
494   }
495 
496   @Override
497   public Get setACL(Map<String, Permission> perms) {
498     return (Get) super.setACL(perms);
499   }
500 
501   @Override
502   public Get setACL(String user, Permission perms) {
503     return (Get) super.setACL(user, perms);
504   }
505 
506   @Override
507   public Get setConsistency(Consistency consistency) {
508     return (Get) super.setConsistency(consistency);
509   }
510 
511   @Override
512   public Get setReplicaId(int Id) {
513     return (Get) super.setReplicaId(Id);
514   }
515 
516   @Override
517   public Get setIsolationLevel(IsolationLevel level) {
518       return (Get) super.setIsolationLevel(level);
519   }
520 
521   @Override
522   public Get setPriority(int priority) {
523     return (Get) super.setPriority(priority);
524   }
525 
526 }