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