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.hadoop.classification.InterfaceAudience;
33 import org.apache.hadoop.classification.InterfaceStability;
34 import org.apache.hadoop.hbase.HConstants;
35 import org.apache.hadoop.hbase.KeyValue;
36 import org.apache.hadoop.hbase.filter.Filter;
37 import org.apache.hadoop.hbase.io.TimeRange;
38 import org.apache.hadoop.hbase.util.Bytes;
39
40 /**
41 * Used to perform Get operations on a single row.
42 * <p>
43 * To get everything for a row, instantiate a Get object with the row to get.
44 * To further narrow the scope of what to Get, use the methods 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, call {@link #setFilter(Filter) setFilter}.
62 */
63 @InterfaceAudience.Public
64 @InterfaceStability.Stable
65 public class Get extends OperationWithAttributes
66 implements Row, Comparable<Row> {
67
68 private byte [] row = null;
69 private int maxVersions = 1;
70 private boolean cacheBlocks = true;
71 private int storeLimit = -1;
72 private int storeOffset = 0;
73 private Filter filter = null;
74 private TimeRange tr = new TimeRange();
75 private Map<byte [], NavigableSet<byte []>> familyMap =
76 new TreeMap<byte [], NavigableSet<byte []>>(Bytes.BYTES_COMPARATOR);
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 Mutation.checkRow(row);
87 this.row = row;
88 }
89
90 /**
91 * Get all columns from the specified family.
92 * <p>
93 * Overrides previous calls to addColumn for this family.
94 * @param family family name
95 * @return the Get object
96 */
97 public Get addFamily(byte [] family) {
98 familyMap.remove(family);
99 familyMap.put(family, null);
100 return this;
101 }
102
103 /**
104 * Get the column from the specific family with the specified qualifier.
105 * <p>
106 * Overrides previous calls to addFamily for this family.
107 * @param family family name
108 * @param qualifier column qualifier
109 * @return the Get objec
110 */
111 public Get addColumn(byte [] family, byte [] qualifier) {
112 NavigableSet<byte []> set = familyMap.get(family);
113 if(set == null) {
114 set = new TreeSet<byte []>(Bytes.BYTES_COMPARATOR);
115 }
116 if (qualifier == null) {
117 qualifier = HConstants.EMPTY_BYTE_ARRAY;
118 }
119 set.add(qualifier);
120 familyMap.put(family, set);
121 return this;
122 }
123
124 /**
125 * Get versions of columns only within the specified timestamp range,
126 * [minStamp, maxStamp).
127 * @param minStamp minimum timestamp value, inclusive
128 * @param maxStamp maximum timestamp value, exclusive
129 * @throws IOException if invalid time range
130 * @return this for invocation chaining
131 */
132 public Get setTimeRange(long minStamp, long maxStamp)
133 throws IOException {
134 tr = new TimeRange(minStamp, maxStamp);
135 return this;
136 }
137
138 /**
139 * Get versions of columns with the specified timestamp.
140 * @param timestamp version timestamp
141 * @return this for invocation chaining
142 */
143 public Get setTimeStamp(long timestamp) {
144 try {
145 tr = new TimeRange(timestamp, timestamp+1);
146 } catch(IOException e) {
147 // Will never happen
148 }
149 return this;
150 }
151
152 /**
153 * Get all available versions.
154 * @return this for invocation chaining
155 */
156 public Get setMaxVersions() {
157 this.maxVersions = Integer.MAX_VALUE;
158 return this;
159 }
160
161 /**
162 * Get up to the specified number of versions of each column.
163 * @param maxVersions maximum versions for each column
164 * @throws IOException if invalid number of versions
165 * @return this for invocation chaining
166 */
167 public Get setMaxVersions(int maxVersions) throws IOException {
168 if(maxVersions <= 0) {
169 throw new IOException("maxVersions must be positive");
170 }
171 this.maxVersions = maxVersions;
172 return this;
173 }
174
175 /**
176 * Set the maximum number of values to return per row per Column Family
177 * @param limit the maximum number of values returned / row / CF
178 * @return this for invocation chaining
179 */
180 public Get setMaxResultsPerColumnFamily(int limit) {
181 this.storeLimit = limit;
182 return this;
183 }
184
185 /**
186 * Set offset for the row per Column Family. This offset is only within a particular row/CF
187 * combination. It gets reset back to zero when we move to the next row or CF.
188 * @param offset is the number of kvs that will be skipped.
189 * @return this for invocation chaining
190 */
191 public Get setRowOffsetPerColumnFamily(int offset) {
192 this.storeOffset = offset;
193 return this;
194 }
195
196 /**
197 * Apply the specified server-side filter when performing the Get.
198 * Only {@link Filter#filterKeyValue(KeyValue)} is called AFTER all tests
199 * for ttl, column match, deletes and max versions have been run.
200 * @param filter filter to run on the server
201 * @return this for invocation chaining
202 */
203 public Get setFilter(Filter filter) {
204 this.filter = filter;
205 return this;
206 }
207
208 /* Accessors */
209
210 /**
211 * @return Filter
212 */
213 public Filter getFilter() {
214 return this.filter;
215 }
216
217 /**
218 * Set whether blocks should be cached for this Get.
219 * <p>
220 * This is true by default. When true, default settings of the table and
221 * family are used (this will never override caching blocks if the block
222 * cache is disabled for that family or entirely).
223 *
224 * @param cacheBlocks if false, default settings are overridden and blocks
225 * will not be cached
226 */
227 public void setCacheBlocks(boolean cacheBlocks) {
228 this.cacheBlocks = cacheBlocks;
229 }
230
231 /**
232 * Get whether blocks should be cached for this Get.
233 * @return true if default caching should be used, false if blocks should not
234 * be cached
235 */
236 public boolean getCacheBlocks() {
237 return cacheBlocks;
238 }
239
240 /**
241 * Method for retrieving the get's row
242 * @return row
243 */
244 public byte [] getRow() {
245 return this.row;
246 }
247
248 /**
249 * Method for retrieving the get's maximum number of version
250 * @return the maximum number of version to fetch for this get
251 */
252 public int getMaxVersions() {
253 return this.maxVersions;
254 }
255
256 /**
257 * Method for retrieving the get's maximum number of values
258 * to return per Column Family
259 * @return the maximum number of values to fetch per CF
260 */
261 public int getMaxResultsPerColumnFamily() {
262 return this.storeLimit;
263 }
264
265 /**
266 * Method for retrieving the get's offset per row per column
267 * family (#kvs to be skipped)
268 * @return the row offset
269 */
270 public int getRowOffsetPerColumnFamily() {
271 return this.storeOffset;
272 }
273
274 /**
275 * Method for retrieving the get's TimeRange
276 * @return timeRange
277 */
278 public TimeRange getTimeRange() {
279 return this.tr;
280 }
281
282 /**
283 * Method for retrieving the keys in the familyMap
284 * @return keys in the current familyMap
285 */
286 public Set<byte[]> familySet() {
287 return this.familyMap.keySet();
288 }
289
290 /**
291 * Method for retrieving the number of families to get from
292 * @return number of families
293 */
294 public int numFamilies() {
295 return this.familyMap.size();
296 }
297
298 /**
299 * Method for checking if any families have been inserted into this Get
300 * @return true if familyMap is non empty false otherwise
301 */
302 public boolean hasFamilies() {
303 return !this.familyMap.isEmpty();
304 }
305
306 /**
307 * Method for retrieving the get's familyMap
308 * @return familyMap
309 */
310 public Map<byte[],NavigableSet<byte[]>> getFamilyMap() {
311 return this.familyMap;
312 }
313
314 /**
315 * Compile the table and column family (i.e. schema) information
316 * into a String. Useful for parsing and aggregation by debugging,
317 * logging, and administration tools.
318 * @return Map
319 */
320 @Override
321 public Map<String, Object> getFingerprint() {
322 Map<String, Object> map = new HashMap<String, Object>();
323 List<String> families = new ArrayList<String>();
324 map.put("families", families);
325 for (Map.Entry<byte [], NavigableSet<byte[]>> entry :
326 this.familyMap.entrySet()) {
327 families.add(Bytes.toStringBinary(entry.getKey()));
328 }
329 return map;
330 }
331
332 /**
333 * Compile the details beyond the scope of getFingerprint (row, columns,
334 * timestamps, etc.) into a Map along with the fingerprinted information.
335 * Useful for debugging, logging, and administration tools.
336 * @param maxCols a limit on the number of columns output prior to truncation
337 * @return Map
338 */
339 @Override
340 public Map<String, Object> toMap(int maxCols) {
341 // we start with the fingerprint map and build on top of it.
342 Map<String, Object> map = getFingerprint();
343 // replace the fingerprint's simple list of families with a
344 // map from column families to lists of qualifiers and kv details
345 Map<String, List<String>> columns = new HashMap<String, List<String>>();
346 map.put("families", columns);
347 // add scalar information first
348 map.put("row", Bytes.toStringBinary(this.row));
349 map.put("maxVersions", this.maxVersions);
350 map.put("cacheBlocks", this.cacheBlocks);
351 List<Long> timeRange = new ArrayList<Long>();
352 timeRange.add(this.tr.getMin());
353 timeRange.add(this.tr.getMax());
354 map.put("timeRange", timeRange);
355 int colCount = 0;
356 // iterate through affected families and add details
357 for (Map.Entry<byte [], NavigableSet<byte[]>> entry :
358 this.familyMap.entrySet()) {
359 List<String> familyList = new ArrayList<String>();
360 columns.put(Bytes.toStringBinary(entry.getKey()), familyList);
361 if(entry.getValue() == null) {
362 colCount++;
363 --maxCols;
364 familyList.add("ALL");
365 } else {
366 colCount += entry.getValue().size();
367 if (maxCols <= 0) {
368 continue;
369 }
370 for (byte [] column : entry.getValue()) {
371 if (--maxCols <= 0) {
372 continue;
373 }
374 familyList.add(Bytes.toStringBinary(column));
375 }
376 }
377 }
378 map.put("totalColumns", colCount);
379 if (this.filter != null) {
380 map.put("filter", this.filter.toString());
381 }
382 // add the id if set
383 if (getId() != null) {
384 map.put("id", getId());
385 }
386 return map;
387 }
388
389 //Row
390 @Override
391 public int compareTo(Row other) {
392 // TODO: This is wrong. Can't have two gets the same just because on same row.
393 return Bytes.compareTo(this.getRow(), other.getRow());
394 }
395
396 @Override
397 public int hashCode() {
398 // TODO: This is wrong. Can't have two gets the same just because on same row. But it
399 // matches how equals works currently and gets rid of the findbugs warning.
400 return Bytes.hashCode(this.getRow());
401 }
402
403 @Override
404 public boolean equals(Object obj) {
405 if (this == obj) {
406 return true;
407 }
408 if (obj == null || getClass() != obj.getClass()) {
409 return false;
410 }
411 Row other = (Row) obj;
412 // TODO: This is wrong. Can't have two gets the same just because on same row.
413 return compareTo(other) == 0;
414 }
415 }