001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import java.io.IOException;
021import java.nio.ByteBuffer;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.NavigableSet;
027import java.util.Set;
028import java.util.TreeMap;
029import java.util.TreeSet;
030import java.util.stream.Collectors;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.filter.Filter;
033import org.apache.hadoop.hbase.io.TimeRange;
034import org.apache.hadoop.hbase.security.access.Permission;
035import org.apache.hadoop.hbase.security.visibility.Authorizations;
036import org.apache.hadoop.hbase.util.Bytes;
037import org.apache.yetus.audience.InterfaceAudience;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041/**
042 * Used to perform Get operations on a single row.
043 * <p>
044 * To get everything for a row, instantiate a Get object with the row to get. To further narrow the
045 * scope of what to Get, use the methods below.
046 * <p>
047 * To get all columns from specific families, execute {@link #addFamily(byte[]) addFamily} for each
048 * family to retrieve.
049 * <p>
050 * To get specific columns, execute {@link #addColumn(byte[], byte[]) addColumn} for each column to
051 * retrieve.
052 * <p>
053 * To only retrieve columns within a specific range of version timestamps, execute
054 * {@link #setTimeRange(long, long) setTimeRange}.
055 * <p>
056 * To only retrieve columns with a specific timestamp, execute {@link #setTimestamp(long)
057 * setTimestamp}.
058 * <p>
059 * To limit the number of versions of each column to be returned, execute
060 * {@link #setMaxVersions(int) setMaxVersions}.
061 * <p>
062 * To add a filter, call {@link #setFilter(Filter) setFilter}.
063 */
064@InterfaceAudience.Public
065public class Get extends Query implements Row {
066  private static final Logger LOG = LoggerFactory.getLogger(Get.class);
067
068  private byte[] row = null;
069  private int maxVersions = 1;
070  private boolean cacheBlocks = true;
071  private int storeLimit = -1;
072  private int storeOffset = 0;
073  private TimeRange tr = TimeRange.allTime();
074  private boolean checkExistenceOnly = false;
075  private boolean closestRowBefore = false;
076  private Map<byte[], NavigableSet<byte[]>> familyMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
077
078  /**
079   * Create a Get operation for the specified row.
080   * <p>
081   * If no further operations are done, this will get the latest version of all columns in all
082   * families of the specified row.
083   * @param row row key
084   */
085  public Get(byte[] row) {
086    Mutation.checkRow(row);
087    this.row = row;
088  }
089
090  /**
091   * Copy-constructor
092   */
093  public Get(Get get) {
094    this(get.getRow());
095    // from Query
096    this.setFilter(get.getFilter());
097    this.setReplicaId(get.getReplicaId());
098    this.setConsistency(get.getConsistency());
099    this.setQueryMetricsEnabled(get.isQueryMetricsEnabled());
100    // from Get
101    this.cacheBlocks = get.getCacheBlocks();
102    this.maxVersions = get.getMaxVersions();
103    this.storeLimit = get.getMaxResultsPerColumnFamily();
104    this.storeOffset = get.getRowOffsetPerColumnFamily();
105    this.tr = get.getTimeRange();
106    this.checkExistenceOnly = get.isCheckExistenceOnly();
107    this.loadColumnFamiliesOnDemand = get.getLoadColumnFamiliesOnDemandValue();
108    Map<byte[], NavigableSet<byte[]>> fams = get.getFamilyMap();
109    for (Map.Entry<byte[], NavigableSet<byte[]>> entry : fams.entrySet()) {
110      byte[] fam = entry.getKey();
111      NavigableSet<byte[]> cols = entry.getValue();
112      if (cols != null && cols.size() > 0) {
113        for (byte[] col : cols) {
114          addColumn(fam, col);
115        }
116      } else {
117        addFamily(fam);
118      }
119    }
120    for (Map.Entry<String, byte[]> attr : get.getAttributesMap().entrySet()) {
121      setAttribute(attr.getKey(), attr.getValue());
122    }
123    for (Map.Entry<byte[], TimeRange> entry : get.getColumnFamilyTimeRange().entrySet()) {
124      TimeRange tr = entry.getValue();
125      setColumnFamilyTimeRange(entry.getKey(), tr.getMin(), tr.getMax());
126    }
127    super.setPriority(get.getPriority());
128  }
129
130  /**
131   * Create a Get operation for the specified row.
132   */
133  public Get(byte[] row, int rowOffset, int rowLength) {
134    Mutation.checkRow(row, rowOffset, rowLength);
135    this.row = Bytes.copy(row, rowOffset, rowLength);
136  }
137
138  /**
139   * Create a Get operation for the specified row.
140   */
141  public Get(ByteBuffer row) {
142    Mutation.checkRow(row);
143    this.row = new byte[row.remaining()];
144    row.get(this.row);
145  }
146
147  public boolean isCheckExistenceOnly() {
148    return checkExistenceOnly;
149  }
150
151  public Get setCheckExistenceOnly(boolean checkExistenceOnly) {
152    this.checkExistenceOnly = checkExistenceOnly;
153    return this;
154  }
155
156  /**
157   * This will always return the default value which is false as client cannot set the value to this
158   * property any more.
159   * @deprecated since 2.0.0 and will be removed in 3.0.0
160   */
161  @Deprecated
162  public boolean isClosestRowBefore() {
163    return closestRowBefore;
164  }
165
166  /**
167   * This is not used any more and does nothing. Use reverse scan instead.
168   * @deprecated since 2.0.0 and will be removed in 3.0.0
169   */
170  @Deprecated
171  public Get setClosestRowBefore(boolean closestRowBefore) {
172    // do Nothing
173    return this;
174  }
175
176  /**
177   * Get all columns from the specified family.
178   * <p>
179   * Overrides previous calls to addColumn for this family.
180   * @param family family name
181   * @return the Get object
182   */
183  public Get addFamily(byte[] family) {
184    familyMap.remove(family);
185    familyMap.put(family, null);
186    return this;
187  }
188
189  /**
190   * Get the column from the specific family with the specified qualifier.
191   * <p>
192   * Overrides previous calls to addFamily for this family.
193   * @param family    family name
194   * @param qualifier column qualifier
195   * @return the Get objec
196   */
197  public Get addColumn(byte[] family, byte[] qualifier) {
198    NavigableSet<byte[]> set = familyMap.get(family);
199    if (set == null) {
200      set = new TreeSet<>(Bytes.BYTES_COMPARATOR);
201      familyMap.put(family, set);
202    }
203    if (qualifier == null) {
204      qualifier = HConstants.EMPTY_BYTE_ARRAY;
205    }
206    set.add(qualifier);
207    return this;
208  }
209
210  /**
211   * Get versions of columns only within the specified timestamp range, [minStamp, maxStamp).
212   * @param minStamp minimum timestamp value, inclusive
213   * @param maxStamp maximum timestamp value, exclusive
214   * @return this for invocation chaining
215   */
216  public Get setTimeRange(long minStamp, long maxStamp) throws IOException {
217    tr = new TimeRange(minStamp, maxStamp);
218    return this;
219  }
220
221  /**
222   * Get versions of columns with the specified timestamp.
223   * @param timestamp version timestamp
224   * @return this for invocation chaining
225   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0. Use
226   *             {@link #setTimestamp(long)} instead
227   */
228  @Deprecated
229  public Get setTimeStamp(long timestamp) throws IOException {
230    return this.setTimestamp(timestamp);
231  }
232
233  /**
234   * Get versions of columns with the specified timestamp.
235   * @param timestamp version timestamp
236   * @return this for invocation chaining
237   */
238  public Get setTimestamp(long timestamp) {
239    try {
240      tr = new TimeRange(timestamp, timestamp + 1);
241    } catch (Exception e) {
242      // This should never happen, unless integer overflow or something extremely wrong...
243      LOG.error("TimeRange failed, likely caused by integer overflow. ", e);
244      throw e;
245    }
246
247    return this;
248  }
249
250  @Override
251  public Get setColumnFamilyTimeRange(byte[] cf, long minStamp, long maxStamp) {
252    super.setColumnFamilyTimeRange(cf, minStamp, maxStamp);
253    return this;
254  }
255
256  /**
257   * Get all available versions.
258   * @return this for invocation chaining
259   * @deprecated It is easy to misunderstand with column family's max versions, so use
260   *             {@link #readAllVersions()} instead.
261   */
262  @Deprecated
263  public Get setMaxVersions() {
264    return readAllVersions();
265  }
266
267  /**
268   * Get up to the specified number of versions of each column.
269   * @param maxVersions maximum versions for each column
270   * @throws IOException if invalid number of versions
271   * @return this for invocation chaining
272   * @deprecated It is easy to misunderstand with column family's max versions, so use
273   *             {@link #readVersions(int)} instead.
274   */
275  @Deprecated
276  public Get setMaxVersions(int maxVersions) throws IOException {
277    return readVersions(maxVersions);
278  }
279
280  /**
281   * Get all available versions.
282   * @return this for invocation chaining
283   */
284  public Get readAllVersions() {
285    this.maxVersions = Integer.MAX_VALUE;
286    return this;
287  }
288
289  /**
290   * Get up to the specified number of versions of each column.
291   * @param versions specified number of versions for each column
292   * @throws IOException if invalid number of versions
293   * @return this for invocation chaining
294   */
295  public Get readVersions(int versions) throws IOException {
296    if (versions <= 0) {
297      throw new IOException("versions must be positive");
298    }
299    this.maxVersions = versions;
300    return this;
301  }
302
303  @Override
304  public Get setLoadColumnFamiliesOnDemand(boolean value) {
305    super.setLoadColumnFamiliesOnDemand(value);
306    return this;
307  }
308
309  /**
310   * Set the maximum number of values to return per row per Column Family
311   * @param limit the maximum number of values returned / row / CF
312   * @return this for invocation chaining
313   */
314  public Get setMaxResultsPerColumnFamily(int limit) {
315    this.storeLimit = limit;
316    return this;
317  }
318
319  /**
320   * Set offset for the row per Column Family. This offset is only within a particular row/CF
321   * combination. It gets reset back to zero when we move to the next row or CF.
322   * @param offset is the number of kvs that will be skipped.
323   * @return this for invocation chaining
324   */
325  public Get setRowOffsetPerColumnFamily(int offset) {
326    this.storeOffset = offset;
327    return this;
328  }
329
330  @Override
331  public Get setFilter(Filter filter) {
332    super.setFilter(filter);
333    return this;
334  }
335
336  /* Accessors */
337
338  /**
339   * Set whether blocks should be cached for this Get.
340   * <p>
341   * This is true by default. When true, default settings of the table and family are used (this
342   * will never override caching blocks if the block cache is disabled for that family or entirely).
343   * @param cacheBlocks if false, default settings are overridden and blocks will not be cached
344   */
345  public Get setCacheBlocks(boolean cacheBlocks) {
346    this.cacheBlocks = cacheBlocks;
347    return this;
348  }
349
350  /**
351   * Get whether blocks should be cached for this Get.
352   * @return true if default caching should be used, false if blocks should not be cached
353   */
354  public boolean getCacheBlocks() {
355    return cacheBlocks;
356  }
357
358  /**
359   * Method for retrieving the get's row
360   */
361  @Override
362  public byte[] getRow() {
363    return this.row;
364  }
365
366  /**
367   * Method for retrieving the get's maximum number of version
368   * @return the maximum number of version to fetch for this get
369   */
370  public int getMaxVersions() {
371    return this.maxVersions;
372  }
373
374  /**
375   * Method for retrieving the get's maximum number of values to return per Column Family
376   * @return the maximum number of values to fetch per CF
377   */
378  public int getMaxResultsPerColumnFamily() {
379    return this.storeLimit;
380  }
381
382  /**
383   * Method for retrieving the get's offset per row per column family (#kvs to be skipped)
384   * @return the row offset
385   */
386  public int getRowOffsetPerColumnFamily() {
387    return this.storeOffset;
388  }
389
390  /**
391   * Method for retrieving the get's TimeRange
392   */
393  public TimeRange getTimeRange() {
394    return this.tr;
395  }
396
397  /**
398   * Method for retrieving the keys in the familyMap
399   * @return keys in the current familyMap
400   */
401  public Set<byte[]> familySet() {
402    return this.familyMap.keySet();
403  }
404
405  /**
406   * Method for retrieving the number of families to get from
407   * @return number of families
408   */
409  public int numFamilies() {
410    return this.familyMap.size();
411  }
412
413  /**
414   * Method for checking if any families have been inserted into this Get
415   * @return true if familyMap is non empty false otherwise
416   */
417  public boolean hasFamilies() {
418    return !this.familyMap.isEmpty();
419  }
420
421  /**
422   * Method for retrieving the get's familyMap
423   */
424  public Map<byte[], NavigableSet<byte[]>> getFamilyMap() {
425    return this.familyMap;
426  }
427
428  /**
429   * Compile the table and column family (i.e. schema) information into a String. Useful for parsing
430   * and aggregation by debugging, logging, and administration tools.
431   */
432  @Override
433  public Map<String, Object> getFingerprint() {
434    Map<String, Object> map = new HashMap<>();
435    List<String> families = new ArrayList<>(this.familyMap.entrySet().size());
436    map.put("families", families);
437    for (Map.Entry<byte[], NavigableSet<byte[]>> entry : this.familyMap.entrySet()) {
438      families.add(Bytes.toStringBinary(entry.getKey()));
439    }
440    return map;
441  }
442
443  /**
444   * Compile the details beyond the scope of getFingerprint (row, columns, timestamps, etc.) into a
445   * Map along with the fingerprinted information. Useful for debugging, logging, and administration
446   * tools.
447   * @param maxCols a limit on the number of columns output prior to truncation
448   */
449  @Override
450  public Map<String, Object> toMap(int maxCols) {
451    // we start with the fingerprint map and build on top of it.
452    Map<String, Object> map = getFingerprint();
453    // replace the fingerprint's simple list of families with a
454    // map from column families to lists of qualifiers and kv details
455    Map<String, List<String>> columns = new HashMap<>();
456    map.put("families", columns);
457    // add scalar information first
458    map.put("row", Bytes.toStringBinary(this.row));
459    map.put("maxVersions", this.maxVersions);
460    map.put("cacheBlocks", this.cacheBlocks);
461    List<Long> timeRange = new ArrayList<>(2);
462    timeRange.add(this.tr.getMin());
463    timeRange.add(this.tr.getMax());
464    map.put("timeRange", timeRange);
465    int colCount = 0;
466    // iterate through affected families and add details
467    for (Map.Entry<byte[], NavigableSet<byte[]>> entry : this.familyMap.entrySet()) {
468      List<String> familyList = new ArrayList<>();
469      columns.put(Bytes.toStringBinary(entry.getKey()), familyList);
470      if (entry.getValue() == null) {
471        colCount++;
472        --maxCols;
473        familyList.add("ALL");
474      } else {
475        colCount += entry.getValue().size();
476        if (maxCols <= 0) {
477          continue;
478        }
479        for (byte[] column : entry.getValue()) {
480          if (--maxCols <= 0) {
481            continue;
482          }
483          familyList.add(Bytes.toStringBinary(column));
484        }
485      }
486    }
487    map.put("totalColumns", colCount);
488    if (this.filter != null) {
489      map.put("filter", this.filter.toString());
490    }
491    // add the id if set
492    if (getId() != null) {
493      map.put("id", getId());
494    }
495    map.put("storeLimit", this.storeLimit);
496    map.put("storeOffset", this.storeOffset);
497    map.put("checkExistenceOnly", this.checkExistenceOnly);
498
499    map.put("targetReplicaId", this.targetReplicaId);
500    map.put("consistency", this.consistency);
501    map.put("loadColumnFamiliesOnDemand", this.loadColumnFamiliesOnDemand);
502    if (!colFamTimeRangeMap.isEmpty()) {
503      Map<String, List<Long>> colFamTimeRangeMapStr = colFamTimeRangeMap.entrySet().stream()
504        .collect(Collectors.toMap((e) -> Bytes.toStringBinary(e.getKey()), e -> {
505          TimeRange value = e.getValue();
506          List<Long> rangeList = new ArrayList<>();
507          rangeList.add(value.getMin());
508          rangeList.add(value.getMax());
509          return rangeList;
510        }));
511
512      map.put("colFamTimeRangeMap", colFamTimeRangeMapStr);
513    }
514    map.put("priority", getPriority());
515    map.put("queryMetricsEnabled", queryMetricsEnabled);
516    return map;
517  }
518
519  // Row
520  @Override
521  public int compareTo(Row other) {
522    // TODO: This is wrong. Can't have two gets the same just because on same row.
523    return Bytes.compareTo(this.getRow(), other.getRow());
524  }
525
526  @Override
527  public int hashCode() {
528    // TODO: This is wrong. Can't have two gets the same just because on same row. But it
529    // matches how equals works currently and gets rid of the findbugs warning.
530    return Bytes.hashCode(this.getRow());
531  }
532
533  @Override
534  public boolean equals(Object obj) {
535    if (this == obj) {
536      return true;
537    }
538    if (!(obj instanceof Row)) {
539      return false;
540    }
541    Row other = (Row) obj;
542    // TODO: This is wrong. Can't have two gets the same just because on same row.
543    return compareTo(other) == 0;
544  }
545
546  @Override
547  public Get setAttribute(String name, byte[] value) {
548    super.setAttribute(name, value);
549    return this;
550  }
551
552  @Override
553  public Get setId(String id) {
554    super.setId(id);
555    return this;
556  }
557
558  @Override
559  public Get setAuthorizations(Authorizations authorizations) {
560    super.setAuthorizations(authorizations);
561    return this;
562  }
563
564  @Override
565  public Get setACL(Map<String, Permission> perms) {
566    super.setACL(perms);
567    return this;
568  }
569
570  @Override
571  public Get setACL(String user, Permission perms) {
572    super.setACL(user, perms);
573    return this;
574  }
575
576  @Override
577  public Get setConsistency(Consistency consistency) {
578    super.setConsistency(consistency);
579    return this;
580  }
581
582  @Override
583  public Get setReplicaId(int Id) {
584    super.setReplicaId(Id);
585    return this;
586  }
587
588  @Override
589  public Get setIsolationLevel(IsolationLevel level) {
590    super.setIsolationLevel(level);
591    return this;
592  }
593
594  @Override
595  public Get setPriority(int priority) {
596    super.setPriority(priority);
597    return this;
598  }
599}