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    // from Get
100    this.cacheBlocks = get.getCacheBlocks();
101    this.maxVersions = get.getMaxVersions();
102    this.storeLimit = get.getMaxResultsPerColumnFamily();
103    this.storeOffset = get.getRowOffsetPerColumnFamily();
104    this.tr = get.getTimeRange();
105    this.checkExistenceOnly = get.isCheckExistenceOnly();
106    this.loadColumnFamiliesOnDemand = get.getLoadColumnFamiliesOnDemandValue();
107    Map<byte[], NavigableSet<byte[]>> fams = get.getFamilyMap();
108    for (Map.Entry<byte[], NavigableSet<byte[]>> entry : fams.entrySet()) {
109      byte[] fam = entry.getKey();
110      NavigableSet<byte[]> cols = entry.getValue();
111      if (cols != null && cols.size() > 0) {
112        for (byte[] col : cols) {
113          addColumn(fam, col);
114        }
115      } else {
116        addFamily(fam);
117      }
118    }
119    for (Map.Entry<String, byte[]> attr : get.getAttributesMap().entrySet()) {
120      setAttribute(attr.getKey(), attr.getValue());
121    }
122    for (Map.Entry<byte[], TimeRange> entry : get.getColumnFamilyTimeRange().entrySet()) {
123      TimeRange tr = entry.getValue();
124      setColumnFamilyTimeRange(entry.getKey(), tr.getMin(), tr.getMax());
125    }
126    super.setPriority(get.getPriority());
127  }
128
129  /**
130   * Create a Get operation for the specified row.
131   */
132  public Get(byte[] row, int rowOffset, int rowLength) {
133    Mutation.checkRow(row, rowOffset, rowLength);
134    this.row = Bytes.copy(row, rowOffset, rowLength);
135  }
136
137  /**
138   * Create a Get operation for the specified row.
139   */
140  public Get(ByteBuffer row) {
141    Mutation.checkRow(row);
142    this.row = new byte[row.remaining()];
143    row.get(this.row);
144  }
145
146  public boolean isCheckExistenceOnly() {
147    return checkExistenceOnly;
148  }
149
150  public Get setCheckExistenceOnly(boolean checkExistenceOnly) {
151    this.checkExistenceOnly = checkExistenceOnly;
152    return this;
153  }
154
155  /**
156   * This will always return the default value which is false as client cannot set the value to this
157   * property any more.
158   * @deprecated since 2.0.0 and will be removed in 3.0.0
159   */
160  @Deprecated
161  public boolean isClosestRowBefore() {
162    return closestRowBefore;
163  }
164
165  /**
166   * This is not used any more and does nothing. Use reverse scan instead.
167   * @deprecated since 2.0.0 and will be removed in 3.0.0
168   */
169  @Deprecated
170  public Get setClosestRowBefore(boolean closestRowBefore) {
171    // do Nothing
172    return this;
173  }
174
175  /**
176   * Get all columns from the specified family.
177   * <p>
178   * Overrides previous calls to addColumn for this family.
179   * @param family family name
180   * @return the Get object
181   */
182  public Get addFamily(byte[] family) {
183    familyMap.remove(family);
184    familyMap.put(family, null);
185    return this;
186  }
187
188  /**
189   * Get the column from the specific family with the specified qualifier.
190   * <p>
191   * Overrides previous calls to addFamily for this family.
192   * @param family    family name
193   * @param qualifier column qualifier
194   * @return the Get objec
195   */
196  public Get addColumn(byte[] family, byte[] qualifier) {
197    NavigableSet<byte[]> set = familyMap.get(family);
198    if (set == null) {
199      set = new TreeSet<>(Bytes.BYTES_COMPARATOR);
200      familyMap.put(family, set);
201    }
202    if (qualifier == null) {
203      qualifier = HConstants.EMPTY_BYTE_ARRAY;
204    }
205    set.add(qualifier);
206    return this;
207  }
208
209  /**
210   * Get versions of columns only within the specified timestamp range, [minStamp, maxStamp).
211   * @param minStamp minimum timestamp value, inclusive
212   * @param maxStamp maximum timestamp value, exclusive
213   * @return this for invocation chaining
214   */
215  public Get setTimeRange(long minStamp, long maxStamp) throws IOException {
216    tr = new TimeRange(minStamp, maxStamp);
217    return this;
218  }
219
220  /**
221   * Get versions of columns with the specified timestamp.
222   * @param timestamp version timestamp
223   * @return this for invocation chaining
224   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0. Use
225   *             {@link #setTimestamp(long)} instead
226   */
227  @Deprecated
228  public Get setTimeStamp(long timestamp) throws IOException {
229    return this.setTimestamp(timestamp);
230  }
231
232  /**
233   * Get versions of columns with the specified timestamp.
234   * @param timestamp version timestamp
235   * @return this for invocation chaining
236   */
237  public Get setTimestamp(long timestamp) {
238    try {
239      tr = new TimeRange(timestamp, timestamp + 1);
240    } catch (Exception e) {
241      // This should never happen, unless integer overflow or something extremely wrong...
242      LOG.error("TimeRange failed, likely caused by integer overflow. ", e);
243      throw e;
244    }
245
246    return this;
247  }
248
249  @Override
250  public Get setColumnFamilyTimeRange(byte[] cf, long minStamp, long maxStamp) {
251    super.setColumnFamilyTimeRange(cf, minStamp, maxStamp);
252    return this;
253  }
254
255  /**
256   * Get all available versions.
257   * @return this for invocation chaining
258   * @deprecated It is easy to misunderstand with column family's max versions, so use
259   *             {@link #readAllVersions()} instead.
260   */
261  @Deprecated
262  public Get setMaxVersions() {
263    return readAllVersions();
264  }
265
266  /**
267   * Get up to the specified number of versions of each column.
268   * @param maxVersions maximum versions for each column
269   * @throws IOException if invalid number of versions
270   * @return this for invocation chaining
271   * @deprecated It is easy to misunderstand with column family's max versions, so use
272   *             {@link #readVersions(int)} instead.
273   */
274  @Deprecated
275  public Get setMaxVersions(int maxVersions) throws IOException {
276    return readVersions(maxVersions);
277  }
278
279  /**
280   * Get all available versions.
281   * @return this for invocation chaining
282   */
283  public Get readAllVersions() {
284    this.maxVersions = Integer.MAX_VALUE;
285    return this;
286  }
287
288  /**
289   * Get up to the specified number of versions of each column.
290   * @param versions specified number of versions for each column
291   * @throws IOException if invalid number of versions
292   * @return this for invocation chaining
293   */
294  public Get readVersions(int versions) throws IOException {
295    if (versions <= 0) {
296      throw new IOException("versions must be positive");
297    }
298    this.maxVersions = versions;
299    return this;
300  }
301
302  @Override
303  public Get setLoadColumnFamiliesOnDemand(boolean value) {
304    super.setLoadColumnFamiliesOnDemand(value);
305    return this;
306  }
307
308  /**
309   * Set the maximum number of values to return per row per Column Family
310   * @param limit the maximum number of values returned / row / CF
311   * @return this for invocation chaining
312   */
313  public Get setMaxResultsPerColumnFamily(int limit) {
314    this.storeLimit = limit;
315    return this;
316  }
317
318  /**
319   * Set offset for the row per Column Family. This offset is only within a particular row/CF
320   * combination. It gets reset back to zero when we move to the next row or CF.
321   * @param offset is the number of kvs that will be skipped.
322   * @return this for invocation chaining
323   */
324  public Get setRowOffsetPerColumnFamily(int offset) {
325    this.storeOffset = offset;
326    return this;
327  }
328
329  @Override
330  public Get setFilter(Filter filter) {
331    super.setFilter(filter);
332    return this;
333  }
334
335  /* Accessors */
336
337  /**
338   * Set whether blocks should be cached for this Get.
339   * <p>
340   * This is true by default. When true, default settings of the table and family are used (this
341   * will never override caching blocks if the block cache is disabled for that family or entirely).
342   * @param cacheBlocks if false, default settings are overridden and blocks will not be cached
343   */
344  public Get setCacheBlocks(boolean cacheBlocks) {
345    this.cacheBlocks = cacheBlocks;
346    return this;
347  }
348
349  /**
350   * Get whether blocks should be cached for this Get.
351   * @return true if default caching should be used, false if blocks should not be cached
352   */
353  public boolean getCacheBlocks() {
354    return cacheBlocks;
355  }
356
357  /**
358   * Method for retrieving the get's row
359   */
360  @Override
361  public byte[] getRow() {
362    return this.row;
363  }
364
365  /**
366   * Method for retrieving the get's maximum number of version
367   * @return the maximum number of version to fetch for this get
368   */
369  public int getMaxVersions() {
370    return this.maxVersions;
371  }
372
373  /**
374   * Method for retrieving the get's maximum number of values to return per Column Family
375   * @return the maximum number of values to fetch per CF
376   */
377  public int getMaxResultsPerColumnFamily() {
378    return this.storeLimit;
379  }
380
381  /**
382   * Method for retrieving the get's offset per row per column family (#kvs to be skipped)
383   * @return the row offset
384   */
385  public int getRowOffsetPerColumnFamily() {
386    return this.storeOffset;
387  }
388
389  /**
390   * Method for retrieving the get's TimeRange
391   */
392  public TimeRange getTimeRange() {
393    return this.tr;
394  }
395
396  /**
397   * Method for retrieving the keys in the familyMap
398   * @return keys in the current familyMap
399   */
400  public Set<byte[]> familySet() {
401    return this.familyMap.keySet();
402  }
403
404  /**
405   * Method for retrieving the number of families to get from
406   * @return number of families
407   */
408  public int numFamilies() {
409    return this.familyMap.size();
410  }
411
412  /**
413   * Method for checking if any families have been inserted into this Get
414   * @return true if familyMap is non empty false otherwise
415   */
416  public boolean hasFamilies() {
417    return !this.familyMap.isEmpty();
418  }
419
420  /**
421   * Method for retrieving the get's familyMap
422   */
423  public Map<byte[], NavigableSet<byte[]>> getFamilyMap() {
424    return this.familyMap;
425  }
426
427  /**
428   * Compile the table and column family (i.e. schema) information into a String. Useful for parsing
429   * and aggregation by debugging, logging, and administration tools.
430   */
431  @Override
432  public Map<String, Object> getFingerprint() {
433    Map<String, Object> map = new HashMap<>();
434    List<String> families = new ArrayList<>(this.familyMap.entrySet().size());
435    map.put("families", families);
436    for (Map.Entry<byte[], NavigableSet<byte[]>> entry : this.familyMap.entrySet()) {
437      families.add(Bytes.toStringBinary(entry.getKey()));
438    }
439    return map;
440  }
441
442  /**
443   * Compile the details beyond the scope of getFingerprint (row, columns, timestamps, etc.) into a
444   * Map along with the fingerprinted information. Useful for debugging, logging, and administration
445   * tools.
446   * @param maxCols a limit on the number of columns output prior to truncation
447   */
448  @Override
449  public Map<String, Object> toMap(int maxCols) {
450    // we start with the fingerprint map and build on top of it.
451    Map<String, Object> map = getFingerprint();
452    // replace the fingerprint's simple list of families with a
453    // map from column families to lists of qualifiers and kv details
454    Map<String, List<String>> columns = new HashMap<>();
455    map.put("families", columns);
456    // add scalar information first
457    map.put("row", Bytes.toStringBinary(this.row));
458    map.put("maxVersions", this.maxVersions);
459    map.put("cacheBlocks", this.cacheBlocks);
460    List<Long> timeRange = new ArrayList<>(2);
461    timeRange.add(this.tr.getMin());
462    timeRange.add(this.tr.getMax());
463    map.put("timeRange", timeRange);
464    int colCount = 0;
465    // iterate through affected families and add details
466    for (Map.Entry<byte[], NavigableSet<byte[]>> entry : this.familyMap.entrySet()) {
467      List<String> familyList = new ArrayList<>();
468      columns.put(Bytes.toStringBinary(entry.getKey()), familyList);
469      if (entry.getValue() == null) {
470        colCount++;
471        --maxCols;
472        familyList.add("ALL");
473      } else {
474        colCount += entry.getValue().size();
475        if (maxCols <= 0) {
476          continue;
477        }
478        for (byte[] column : entry.getValue()) {
479          if (--maxCols <= 0) {
480            continue;
481          }
482          familyList.add(Bytes.toStringBinary(column));
483        }
484      }
485    }
486    map.put("totalColumns", colCount);
487    if (this.filter != null) {
488      map.put("filter", this.filter.toString());
489    }
490    // add the id if set
491    if (getId() != null) {
492      map.put("id", getId());
493    }
494    map.put("storeLimit", this.storeLimit);
495    map.put("storeOffset", this.storeOffset);
496    map.put("checkExistenceOnly", this.checkExistenceOnly);
497
498    map.put("targetReplicaId", this.targetReplicaId);
499    map.put("consistency", this.consistency);
500    map.put("loadColumnFamiliesOnDemand", this.loadColumnFamiliesOnDemand);
501    if (!colFamTimeRangeMap.isEmpty()) {
502      Map<String, List<Long>> colFamTimeRangeMapStr = colFamTimeRangeMap.entrySet().stream()
503        .collect(Collectors.toMap((e) -> Bytes.toStringBinary(e.getKey()), e -> {
504          TimeRange value = e.getValue();
505          List<Long> rangeList = new ArrayList<>();
506          rangeList.add(value.getMin());
507          rangeList.add(value.getMax());
508          return rangeList;
509        }));
510
511      map.put("colFamTimeRangeMap", colFamTimeRangeMapStr);
512    }
513    map.put("priority", getPriority());
514    return map;
515  }
516
517  // Row
518  @Override
519  public int compareTo(Row other) {
520    // TODO: This is wrong. Can't have two gets the same just because on same row.
521    return Bytes.compareTo(this.getRow(), other.getRow());
522  }
523
524  @Override
525  public int hashCode() {
526    // TODO: This is wrong. Can't have two gets the same just because on same row. But it
527    // matches how equals works currently and gets rid of the findbugs warning.
528    return Bytes.hashCode(this.getRow());
529  }
530
531  @Override
532  public boolean equals(Object obj) {
533    if (this == obj) {
534      return true;
535    }
536    if (!(obj instanceof Row)) {
537      return false;
538    }
539    Row other = (Row) obj;
540    // TODO: This is wrong. Can't have two gets the same just because on same row.
541    return compareTo(other) == 0;
542  }
543
544  @Override
545  public Get setAttribute(String name, byte[] value) {
546    super.setAttribute(name, value);
547    return this;
548  }
549
550  @Override
551  public Get setId(String id) {
552    super.setId(id);
553    return this;
554  }
555
556  @Override
557  public Get setAuthorizations(Authorizations authorizations) {
558    super.setAuthorizations(authorizations);
559    return this;
560  }
561
562  @Override
563  public Get setACL(Map<String, Permission> perms) {
564    super.setACL(perms);
565    return this;
566  }
567
568  @Override
569  public Get setACL(String user, Permission perms) {
570    super.setACL(user, perms);
571    return this;
572  }
573
574  @Override
575  public Get setConsistency(Consistency consistency) {
576    super.setConsistency(consistency);
577    return this;
578  }
579
580  @Override
581  public Get setReplicaId(int Id) {
582    super.setReplicaId(Id);
583    return this;
584  }
585
586  @Override
587  public Get setIsolationLevel(IsolationLevel level) {
588    super.setIsolationLevel(level);
589    return this;
590  }
591
592  @Override
593  public Get setPriority(int priority) {
594    super.setPriority(priority);
595    return this;
596  }
597}