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.BufferOverflowException; 022import java.nio.ByteBuffer; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Collections; 026import java.util.Comparator; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030import java.util.NavigableMap; 031import java.util.NoSuchElementException; 032import java.util.TreeMap; 033import org.apache.hadoop.hbase.Cell; 034import org.apache.hadoop.hbase.CellComparator; 035import org.apache.hadoop.hbase.CellScanner; 036import org.apache.hadoop.hbase.CellUtil; 037import org.apache.hadoop.hbase.ExtendedCell; 038import org.apache.hadoop.hbase.ExtendedCellScannable; 039import org.apache.hadoop.hbase.ExtendedCellScanner; 040import org.apache.hadoop.hbase.HConstants; 041import org.apache.hadoop.hbase.KeyValue; 042import org.apache.hadoop.hbase.KeyValueUtil; 043import org.apache.hadoop.hbase.PrivateCellUtil; 044import org.apache.hadoop.hbase.util.Bytes; 045import org.apache.yetus.audience.InterfaceAudience; 046 047/** 048 * Single row result of a {@link Get} or {@link Scan} query. 049 * <p> 050 * This class is <b>NOT THREAD SAFE</b>. 051 * <p> 052 * Convenience methods are available that return various {@link Map} structures and values directly. 053 * <p> 054 * To get a complete mapping of all cells in the Result, which can include multiple families and 055 * multiple versions, use {@link #getMap()}. 056 * <p> 057 * To get a mapping of each family to its columns (qualifiers and values), including only the latest 058 * version of each, use {@link #getNoVersionMap()}. To get a mapping of qualifiers to latest values 059 * for an individual family use {@link #getFamilyMap(byte[])}. 060 * <p> 061 * To get the latest value for a specific family and qualifier use 062 * {@link #getValue(byte[], byte[])}. A Result is backed by an array of {@link Cell} objects, each 063 * representing an HBase cell defined by the row, family, qualifier, timestamp, and value. 064 * <p> 065 * The underlying {@link Cell} objects can be accessed through the method {@link #listCells()}. This 066 * will create a List from the internal Cell []. Better is to exploit the fact that a new Result 067 * instance is a primed {@link CellScanner}; just call {@link #advance()} and {@link #current()} to 068 * iterate over Cells as you would any {@link CellScanner}. Call {@link #cellScanner()} to reset 069 * should you need to iterate the same Result over again ({@link CellScanner}s are one-shot). If you 070 * need to overwrite a Result with another Result instance -- as in the old 'mapred' RecordReader 071 * next invocations -- then create an empty Result with the null constructor and in then use 072 * {@link #copyFrom(Result)} 073 */ 074@InterfaceAudience.Public 075public class Result implements ExtendedCellScannable, ExtendedCellScanner { 076 private ExtendedCell[] cells; 077 private Boolean exists; // if the query was just to check existence. 078 private boolean stale = false; 079 080 /** 081 * See {@link #mayHaveMoreCellsInRow()}. 082 */ 083 private boolean mayHaveMoreCellsInRow = false; 084 // We're not using java serialization. Transient here is just a marker to say 085 // that this is where we cache row if we're ever asked for it. 086 private transient byte[] row = null; 087 // Ditto for familyMap. It can be composed on fly from passed in kvs. 088 private transient NavigableMap<byte[], 089 NavigableMap<byte[], NavigableMap<Long, byte[]>>> familyMap = null; 090 091 private static ThreadLocal<byte[]> localBuffer = new ThreadLocal<>(); 092 private static final int PAD_WIDTH = 128; 093 public static final Result EMPTY_RESULT = new Result(true); 094 095 private final static int INITIAL_CELLSCANNER_INDEX = -1; 096 097 /** 098 * Index for where we are when Result is acting as a {@link CellScanner}. 099 */ 100 private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX; 101 private RegionLoadStats stats; 102 private QueryMetrics metrics = null; 103 104 private final boolean readonly; 105 106 private Cursor cursor = null; 107 108 /** 109 * Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}. 110 * Use this to represent no results if {@code null} won't do or in old 'mapred' as opposed to 111 * 'mapreduce' package MapReduce where you need to overwrite a Result instance with a 112 * {@link #copyFrom(Result)} call. 113 */ 114 public Result() { 115 this(false); 116 } 117 118 /** 119 * Allows to construct special purpose immutable Result objects, such as EMPTY_RESULT. 120 * @param readonly whether this Result instance is readonly 121 */ 122 private Result(boolean readonly) { 123 this.readonly = readonly; 124 } 125 126 /** 127 * Instantiate a Result with the specified List of KeyValues. <br> 128 * <strong>Note:</strong> You must ensure that the keyvalues are already sorted. 129 * @param cells List of cells 130 */ 131 public static Result create(List<? extends Cell> cells) { 132 return create(cells, null); 133 } 134 135 public static Result create(List<? extends Cell> cells, Boolean exists) { 136 return create(cells, exists, false); 137 } 138 139 public static Result create(List<? extends Cell> cells, Boolean exists, boolean stale) { 140 return create(cells, exists, stale, false); 141 } 142 143 public static Result create(List<? extends Cell> cells, Boolean exists, boolean stale, 144 boolean mayHaveMoreCellsInRow) { 145 if (exists != null) { 146 return new Result(null, exists, stale, mayHaveMoreCellsInRow); 147 } 148 return new Result(cells.toArray(new ExtendedCell[cells.size()]), null, stale, 149 mayHaveMoreCellsInRow); 150 } 151 152 /** 153 * Instantiate a Result with the specified array of KeyValues. <br> 154 * <strong>Note:</strong> You must ensure that the keyvalues are already sorted. 155 * @param cells array of cells 156 */ 157 public static Result create(Cell[] cells) { 158 return create(cells, null, false); 159 } 160 161 public static Result create(Cell[] cells, Boolean exists, boolean stale) { 162 return create(cells, exists, stale, false); 163 } 164 165 public static Result create(Cell[] cells, Boolean exists, boolean stale, 166 boolean mayHaveMoreCellsInRow) { 167 if (exists != null) { 168 return new Result(null, exists, stale, mayHaveMoreCellsInRow); 169 } 170 ExtendedCell[] extendCells = cells instanceof ExtendedCell[] 171 ? (ExtendedCell[]) cells 172 : Arrays.copyOf(cells, cells.length, ExtendedCell[].class); 173 return new Result(extendCells, null, stale, mayHaveMoreCellsInRow); 174 } 175 176 // prefer these below methods inside hbase to avoid casting or copying 177 static Result create(ExtendedCell[] cells) { 178 return create(cells, null, false); 179 } 180 181 static Result create(ExtendedCell[] cells, Boolean exists, boolean stale) { 182 return create(cells, exists, stale, false); 183 } 184 185 static Result create(ExtendedCell[] cells, Boolean exists, boolean stale, 186 boolean mayHaveMoreCellsInRow) { 187 if (exists != null) { 188 return new Result(null, exists, stale, mayHaveMoreCellsInRow); 189 } 190 return new Result(cells, null, stale, mayHaveMoreCellsInRow); 191 } 192 193 public static Result createCursorResult(Cursor cursor) { 194 return new Result(cursor); 195 } 196 197 private Result(Cursor cursor) { 198 this.cursor = cursor; 199 this.readonly = false; 200 } 201 202 /** Private ctor. Use {@link #create(Cell[])}. */ 203 private Result(ExtendedCell[] cells, Boolean exists, boolean stale, 204 boolean mayHaveMoreCellsInRow) { 205 this.cells = cells; 206 this.exists = exists; 207 this.stale = stale; 208 this.mayHaveMoreCellsInRow = mayHaveMoreCellsInRow; 209 this.readonly = false; 210 } 211 212 /** 213 * Method for retrieving the row key that corresponds to the row from which this Result was 214 * created. 215 */ 216 public byte[] getRow() { 217 if (this.row == null) { 218 this.row = 219 (this.cells == null || this.cells.length == 0) ? null : CellUtil.cloneRow(this.cells[0]); 220 } 221 return this.row; 222 } 223 224 /** 225 * Return the array of Cells backing this Result instance. The array is sorted from smallest -> 226 * largest using the {@link CellComparator}. The array only contains what your Get or Scan 227 * specifies and no more. For example if you request column "A" 1 version you will have at most 1 228 * Cell in the array. If you request column "A" with 2 version you will have at most 2 Cells, with 229 * the first one being the newer timestamp and the second being the older timestamp (this is the 230 * sort order defined by {@link CellComparator}). If columns don't exist, they won't be present in 231 * the result. Therefore if you ask for 1 version all columns, it is safe to iterate over this 232 * array and expect to see 1 Cell for each column and no more. This API is faster than using 233 * getFamilyMap() and getMap() 234 * @return array of Cells; can be null if nothing in the result 235 */ 236 public Cell[] rawCells() { 237 return cells; 238 } 239 240 ExtendedCell[] rawExtendedCells() { 241 return cells; 242 } 243 244 /** 245 * Create a sorted list of the Cell's in this result. Since HBase 0.20.5 this is equivalent to 246 * raw(). 247 * @return sorted List of Cells; can be null if no cells in the result 248 */ 249 public List<Cell> listCells() { 250 return isEmpty() ? null : Arrays.asList(rawCells()); 251 } 252 253 /** 254 * Return the Cells for the specific column. The Cells are sorted in the {@link CellComparator} 255 * order. That implies the first entry in the list is the most recent column. If the query (Scan 256 * or Get) only requested 1 version the list will contain at most 1 entry. If the column did not 257 * exist in the result set (either the column does not exist or the column was not selected in the 258 * query) the list will be empty. Also see getColumnLatest which returns just a Cell 259 * @param family the family 260 * @return a list of Cells for this column or empty list if the column did not exist in the result 261 * set 262 */ 263 public List<Cell> getColumnCells(byte[] family, byte[] qualifier) { 264 List<Cell> result = new ArrayList<>(); 265 266 Cell[] kvs = rawCells(); 267 268 if (kvs == null || kvs.length == 0) { 269 return result; 270 } 271 int pos = binarySearch(kvs, family, qualifier); 272 if (pos == -1) { 273 return result; // cant find it 274 } 275 276 for (int i = pos; i < kvs.length; i++) { 277 if (CellUtil.matchingColumn(kvs[i], family, qualifier)) { 278 result.add(kvs[i]); 279 } else { 280 break; 281 } 282 } 283 284 return result; 285 } 286 287 private byte[] notNullBytes(final byte[] bytes) { 288 if (bytes == null) { 289 return HConstants.EMPTY_BYTE_ARRAY; 290 } else { 291 return bytes; 292 } 293 } 294 295 private int binarySearch(final Cell[] kvs, final byte[] family, final byte[] qualifier) { 296 byte[] familyNotNull = notNullBytes(family); 297 byte[] qualifierNotNull = notNullBytes(qualifier); 298 Cell searchTerm = PrivateCellUtil.createFirstOnRow(kvs[0].getRowArray(), kvs[0].getRowOffset(), 299 kvs[0].getRowLength(), familyNotNull, 0, (byte) familyNotNull.length, qualifierNotNull, 0, 300 qualifierNotNull.length); 301 302 // pos === ( -(insertion point) - 1) 303 int pos = Arrays.binarySearch(kvs, searchTerm, CellComparator.getInstance()); 304 // never will exact match 305 if (pos < 0) { 306 pos = (pos + 1) * -1; 307 // pos is now insertion point 308 } 309 if (pos == kvs.length) { 310 return -1; // doesn't exist 311 } 312 return pos; 313 } 314 315 /** 316 * Searches for the latest value for the specified column. 317 * @param kvs the array to search 318 * @param family family name 319 * @param foffset family offset 320 * @param flength family length 321 * @param qualifier column qualifier 322 * @param qoffset qualifier offset 323 * @param qlength qualifier length 324 * @return the index where the value was found, or -1 otherwise 325 */ 326 private int binarySearch(final Cell[] kvs, final byte[] family, final int foffset, 327 final int flength, final byte[] qualifier, final int qoffset, final int qlength) { 328 329 double keyValueSize = 330 (double) KeyValue.getKeyValueDataStructureSize(kvs[0].getRowLength(), flength, qlength, 0); 331 332 byte[] buffer = localBuffer.get(); 333 if (buffer == null || keyValueSize > buffer.length) { 334 // pad to the smallest multiple of the pad width 335 buffer = new byte[(int) Math.ceil(keyValueSize / PAD_WIDTH) * PAD_WIDTH]; 336 localBuffer.set(buffer); 337 } 338 339 Cell searchTerm = 340 KeyValueUtil.createFirstOnRow(buffer, 0, kvs[0].getRowArray(), kvs[0].getRowOffset(), 341 kvs[0].getRowLength(), family, foffset, flength, qualifier, qoffset, qlength); 342 343 // pos === ( -(insertion point) - 1) 344 int pos = Arrays.binarySearch(kvs, searchTerm, CellComparator.getInstance()); 345 // never will exact match 346 if (pos < 0) { 347 pos = (pos + 1) * -1; 348 // pos is now insertion point 349 } 350 if (pos == kvs.length) { 351 return -1; // doesn't exist 352 } 353 return pos; 354 } 355 356 /** 357 * The Cell for the most recent timestamp for a given column. 358 * @return the Cell for the column, or null if no value exists in the row or none have been 359 * selected in the query (Get/Scan) 360 */ 361 public Cell getColumnLatestCell(byte[] family, byte[] qualifier) { 362 Cell[] kvs = rawCells(); // side effect possibly. 363 if (kvs == null || kvs.length == 0) { 364 return null; 365 } 366 int pos = binarySearch(kvs, family, qualifier); 367 if (pos == -1) { 368 return null; 369 } 370 if (CellUtil.matchingColumn(kvs[pos], family, qualifier)) { 371 return kvs[pos]; 372 } 373 return null; 374 } 375 376 /** 377 * The Cell for the most recent timestamp for a given column. 378 * @param family family name 379 * @param foffset family offset 380 * @param flength family length 381 * @param qualifier column qualifier 382 * @param qoffset qualifier offset 383 * @param qlength qualifier length 384 * @return the Cell for the column, or null if no value exists in the row or none have been 385 * selected in the query (Get/Scan) 386 */ 387 public Cell getColumnLatestCell(byte[] family, int foffset, int flength, byte[] qualifier, 388 int qoffset, int qlength) { 389 390 Cell[] kvs = rawCells(); // side effect possibly. 391 if (kvs == null || kvs.length == 0) { 392 return null; 393 } 394 int pos = binarySearch(kvs, family, foffset, flength, qualifier, qoffset, qlength); 395 if (pos == -1) { 396 return null; 397 } 398 if ( 399 PrivateCellUtil.matchingColumn(kvs[pos], family, foffset, flength, qualifier, qoffset, 400 qlength) 401 ) { 402 return kvs[pos]; 403 } 404 return null; 405 } 406 407 /** 408 * Get the latest version of the specified column. Note: this call clones the value content of the 409 * hosting Cell. See {@link #getValueAsByteBuffer(byte[], byte[])}, etc., or {@link #listCells()} 410 * if you would avoid the cloning. 411 * @param family family name 412 * @param qualifier column qualifier 413 * @return value of latest version of column, null if none found 414 */ 415 public byte[] getValue(byte[] family, byte[] qualifier) { 416 Cell kv = getColumnLatestCell(family, qualifier); 417 if (kv == null) { 418 return null; 419 } 420 return CellUtil.cloneValue(kv); 421 } 422 423 /** 424 * Returns the value wrapped in a new <code>ByteBuffer</code>. 425 * @param family family name 426 * @param qualifier column qualifier 427 * @return the latest version of the column, or <code>null</code> if none found 428 */ 429 public ByteBuffer getValueAsByteBuffer(byte[] family, byte[] qualifier) { 430 431 Cell kv = getColumnLatestCell(family, 0, family.length, qualifier, 0, qualifier.length); 432 433 if (kv == null) { 434 return null; 435 } 436 return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()) 437 .asReadOnlyBuffer(); 438 } 439 440 /** 441 * Returns the value wrapped in a new <code>ByteBuffer</code>. 442 * @param family family name 443 * @param foffset family offset 444 * @param flength family length 445 * @param qualifier column qualifier 446 * @param qoffset qualifier offset 447 * @param qlength qualifier length 448 * @return the latest version of the column, or <code>null</code> if none found 449 */ 450 public ByteBuffer getValueAsByteBuffer(byte[] family, int foffset, int flength, byte[] qualifier, 451 int qoffset, int qlength) { 452 453 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength); 454 455 if (kv == null) { 456 return null; 457 } 458 return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()) 459 .asReadOnlyBuffer(); 460 } 461 462 /** 463 * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>. 464 * <p> 465 * Does not clear or flip the buffer. 466 * @param family family name 467 * @param qualifier column qualifier 468 * @param dst the buffer where to write the value 469 * @return <code>true</code> if a value was found, <code>false</code> otherwise 470 * @throws BufferOverflowException there is insufficient space remaining in the buffer 471 */ 472 public boolean loadValue(byte[] family, byte[] qualifier, ByteBuffer dst) 473 throws BufferOverflowException { 474 return loadValue(family, 0, family.length, qualifier, 0, qualifier.length, dst); 475 } 476 477 /** 478 * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>. 479 * <p> 480 * Does not clear or flip the buffer. 481 * @param family family name 482 * @param foffset family offset 483 * @param flength family length 484 * @param qualifier column qualifier 485 * @param qoffset qualifier offset 486 * @param qlength qualifier length 487 * @param dst the buffer where to write the value 488 * @return <code>true</code> if a value was found, <code>false</code> otherwise 489 * @throws BufferOverflowException there is insufficient space remaining in the buffer 490 */ 491 public boolean loadValue(byte[] family, int foffset, int flength, byte[] qualifier, int qoffset, 492 int qlength, ByteBuffer dst) throws BufferOverflowException { 493 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength); 494 495 if (kv == null) { 496 return false; 497 } 498 dst.put(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()); 499 return true; 500 } 501 502 /** 503 * Checks if the specified column contains a non-empty value (not a zero-length byte array). 504 * @param family family name 505 * @param qualifier column qualifier 506 * @return whether or not a latest value exists and is not empty 507 */ 508 public boolean containsNonEmptyColumn(byte[] family, byte[] qualifier) { 509 510 return containsNonEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length); 511 } 512 513 /** 514 * Checks if the specified column contains a non-empty value (not a zero-length byte array). 515 * @param family family name 516 * @param foffset family offset 517 * @param flength family length 518 * @param qualifier column qualifier 519 * @param qoffset qualifier offset 520 * @param qlength qualifier length 521 * @return whether or not a latest value exists and is not empty 522 */ 523 public boolean containsNonEmptyColumn(byte[] family, int foffset, int flength, byte[] qualifier, 524 int qoffset, int qlength) { 525 526 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength); 527 528 return (kv != null) && (kv.getValueLength() > 0); 529 } 530 531 /** 532 * Checks if the specified column contains an empty value (a zero-length byte array). 533 * @param family family name 534 * @param qualifier column qualifier 535 * @return whether or not a latest value exists and is empty 536 */ 537 public boolean containsEmptyColumn(byte[] family, byte[] qualifier) { 538 539 return containsEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length); 540 } 541 542 /** 543 * Checks if the specified column contains an empty value (a zero-length byte array). 544 * @param family family name 545 * @param foffset family offset 546 * @param flength family length 547 * @param qualifier column qualifier 548 * @param qoffset qualifier offset 549 * @param qlength qualifier length 550 * @return whether or not a latest value exists and is empty 551 */ 552 public boolean containsEmptyColumn(byte[] family, int foffset, int flength, byte[] qualifier, 553 int qoffset, int qlength) { 554 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength); 555 556 return (kv != null) && (kv.getValueLength() == 0); 557 } 558 559 /** 560 * Checks for existence of a value for the specified column (empty or not). 561 * @param family family name 562 * @param qualifier column qualifier 563 * @return true if at least one value exists in the result, false if not 564 */ 565 public boolean containsColumn(byte[] family, byte[] qualifier) { 566 Cell kv = getColumnLatestCell(family, qualifier); 567 return kv != null; 568 } 569 570 /** 571 * Checks for existence of a value for the specified column (empty or not). 572 * @param family family name 573 * @param foffset family offset 574 * @param flength family length 575 * @param qualifier column qualifier 576 * @param qoffset qualifier offset 577 * @param qlength qualifier length 578 * @return true if at least one value exists in the result, false if not 579 */ 580 public boolean containsColumn(byte[] family, int foffset, int flength, byte[] qualifier, 581 int qoffset, int qlength) { 582 583 return getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength) != null; 584 } 585 586 /** 587 * Map of families to all versions of its qualifiers and values. 588 * <p> 589 * Returns a three level Map of the form: 590 * <code>Map&family,Map<qualifier,Map<timestamp,value>>></code> 591 * <p> 592 * Note: All other map returning methods make use of this map internally. 593 * @return map from families to qualifiers to versions 594 */ 595 public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() { 596 if (this.familyMap != null) { 597 return this.familyMap; 598 } 599 if (isEmpty()) { 600 return null; 601 } 602 this.familyMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); 603 for (Cell kv : this.cells) { 604 byte[] family = CellUtil.cloneFamily(kv); 605 NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap = familyMap.get(family); 606 if (columnMap == null) { 607 columnMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); 608 familyMap.put(family, columnMap); 609 } 610 byte[] qualifier = CellUtil.cloneQualifier(kv); 611 NavigableMap<Long, byte[]> versionMap = columnMap.get(qualifier); 612 if (versionMap == null) { 613 versionMap = new TreeMap<>(new Comparator<Long>() { 614 @Override 615 public int compare(Long l1, Long l2) { 616 return l2.compareTo(l1); 617 } 618 }); 619 columnMap.put(qualifier, versionMap); 620 } 621 Long timestamp = kv.getTimestamp(); 622 byte[] value = CellUtil.cloneValue(kv); 623 624 versionMap.put(timestamp, value); 625 } 626 return this.familyMap; 627 } 628 629 /** 630 * Map of families to their most recent qualifiers and values. 631 * <p> 632 * Returns a two level Map of the form: <code>Map&family,Map<qualifier,value>></code> 633 * <p> 634 * The most recent version of each qualifier will be used. 635 * @return map from families to qualifiers and value 636 */ 637 public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() { 638 if (this.familyMap == null) { 639 getMap(); 640 } 641 if (isEmpty()) { 642 return null; 643 } 644 NavigableMap<byte[], NavigableMap<byte[], byte[]>> returnMap = 645 new TreeMap<>(Bytes.BYTES_COMPARATOR); 646 for (Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> familyEntry : familyMap 647 .entrySet()) { 648 NavigableMap<byte[], byte[]> qualifierMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); 649 for (Map.Entry<byte[], NavigableMap<Long, byte[]>> qualifierEntry : familyEntry.getValue() 650 .entrySet()) { 651 byte[] value = qualifierEntry.getValue().get(qualifierEntry.getValue().firstKey()); 652 qualifierMap.put(qualifierEntry.getKey(), value); 653 } 654 returnMap.put(familyEntry.getKey(), qualifierMap); 655 } 656 return returnMap; 657 } 658 659 /** 660 * Map of qualifiers to values. 661 * <p> 662 * Returns a Map of the form: <code>Map<qualifier,value></code> 663 * @param family column family to get 664 * @return map of qualifiers to values 665 */ 666 public NavigableMap<byte[], byte[]> getFamilyMap(byte[] family) { 667 if (this.familyMap == null) { 668 getMap(); 669 } 670 if (isEmpty()) { 671 return null; 672 } 673 NavigableMap<byte[], byte[]> returnMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); 674 NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifierMap = familyMap.get(family); 675 if (qualifierMap == null) { 676 return returnMap; 677 } 678 for (Map.Entry<byte[], NavigableMap<Long, byte[]>> entry : qualifierMap.entrySet()) { 679 byte[] value = entry.getValue().get(entry.getValue().firstKey()); 680 returnMap.put(entry.getKey(), value); 681 } 682 return returnMap; 683 } 684 685 /** 686 * Returns the value of the first column in the Result. 687 * @return value of the first column 688 */ 689 public byte[] value() { 690 if (isEmpty()) { 691 return null; 692 } 693 return CellUtil.cloneValue(cells[0]); 694 } 695 696 /** 697 * Check if the underlying Cell [] is empty or not 698 * @return true if empty 699 */ 700 public boolean isEmpty() { 701 return this.cells == null || this.cells.length == 0; 702 } 703 704 /** Returns the size of the underlying Cell [] */ 705 public int size() { 706 return this.cells == null ? 0 : this.cells.length; 707 } 708 709 /** 710 * */ 711 @Override 712 public String toString() { 713 StringBuilder sb = new StringBuilder(); 714 sb.append("keyvalues="); 715 if (isEmpty()) { 716 sb.append("NONE"); 717 return sb.toString(); 718 } 719 sb.append("{"); 720 boolean moreThanOne = false; 721 for (Cell kv : this.cells) { 722 if (moreThanOne) { 723 sb.append(", "); 724 } else { 725 moreThanOne = true; 726 } 727 sb.append(kv.toString()); 728 } 729 sb.append("}"); 730 return sb.toString(); 731 } 732 733 /** 734 * Does a deep comparison of two Results, down to the byte arrays. 735 * @param res1 first result to compare 736 * @param res2 second result to compare 737 * @throws Exception Every difference is throwing an exception 738 */ 739 public static void compareResults(Result res1, Result res2) throws Exception { 740 compareResults(res1, res2, true); 741 } 742 743 /** 744 * Does a deep comparison of two Results, down to the byte arrays. 745 * @param res1 first result to compare 746 * @param res2 second result to compare 747 * @param verbose includes string representation for all cells in the exception if true; otherwise 748 * include rowkey only 749 * @throws Exception Every difference is throwing an exception 750 */ 751 public static void compareResults(Result res1, Result res2, boolean verbose) throws Exception { 752 if (res2 == null) { 753 throw new Exception( 754 "There wasn't enough rows, we stopped at " + Bytes.toStringBinary(res1.getRow())); 755 } 756 if (res1.size() != res2.size()) { 757 if (verbose) { 758 throw new Exception( 759 "This row doesn't have the same number of KVs: " + res1 + " compared to " + res2); 760 } else { 761 throw new Exception( 762 "This row doesn't have the same number of KVs: row=" + Bytes.toStringBinary(res1.getRow()) 763 + ", " + res1.size() + " cells are compared to " + res2.size() + " cells"); 764 } 765 } 766 ExtendedCell[] ourKVs = res1.cells; 767 ExtendedCell[] replicatedKVs = res2.cells; 768 for (int i = 0; i < res1.size(); i++) { 769 if ( 770 !ourKVs[i].equals(replicatedKVs[i]) || !CellUtil.matchingValue(ourKVs[i], replicatedKVs[i]) 771 || !PrivateCellUtil.matchingTags(ourKVs[i], replicatedKVs[i]) 772 ) { 773 if (verbose) { 774 throw new Exception("This result was different: " + res1 + " compared to " + res2); 775 } else { 776 throw new Exception( 777 "This result was different: row=" + Bytes.toStringBinary(res1.getRow())); 778 } 779 } 780 } 781 } 782 783 /** 784 * Forms a single result from the partial results in the partialResults list. This method is 785 * useful for reconstructing partial results on the client side. 786 * @param partialResults list of partial results 787 * @return The complete result that is formed by combining all of the partial results together 788 * @throws IOException A complete result cannot be formed because the results in the partial list 789 * come from different rows 790 */ 791 public static Result createCompleteResult(Iterable<Result> partialResults) throws IOException { 792 if (partialResults == null) { 793 return Result.create(Collections.emptyList(), null, false); 794 } 795 List<Cell> cells = new ArrayList<>(); 796 boolean stale = false; 797 byte[] prevRow = null; 798 byte[] currentRow = null; 799 for (Iterator<Result> iter = partialResults.iterator(); iter.hasNext();) { 800 Result r = iter.next(); 801 currentRow = r.getRow(); 802 if (prevRow != null && !Bytes.equals(prevRow, currentRow)) { 803 throw new IOException("Cannot form complete result. Rows of partial results do not match." 804 + " Partial Results: " + partialResults); 805 } 806 // Ensure that all Results except the last one are marked as partials. The last result 807 // may not be marked as a partial because Results are only marked as partials when 808 // the scan on the server side must be stopped due to reaching the maxResultSize. 809 // Visualizing it makes it easier to understand: 810 // maxResultSize: 2 cells 811 // (-x-) represents cell number x in a row 812 // Example: row1: -1- -2- -3- -4- -5- (5 cells total) 813 // How row1 will be returned by the server as partial Results: 814 // Result1: -1- -2- (2 cells, size limit reached, mark as partial) 815 // Result2: -3- -4- (2 cells, size limit reached, mark as partial) 816 // Result3: -5- (1 cell, size limit NOT reached, NOT marked as partial) 817 if (iter.hasNext() && !r.mayHaveMoreCellsInRow()) { 818 throw new IOException("Cannot form complete result. Result is missing partial flag. " 819 + "Partial Results: " + partialResults); 820 } 821 prevRow = currentRow; 822 stale = stale || r.isStale(); 823 Collections.addAll(cells, r.rawCells()); 824 } 825 826 return Result.create(cells, null, stale); 827 } 828 829 /** 830 * Get total size of raw cells 831 * @return Total size. 832 */ 833 public static long getTotalSizeOfCells(Result result) { 834 long size = 0; 835 if (result.isEmpty()) { 836 return size; 837 } 838 for (Cell c : result.rawCells()) { 839 size += c.heapSize(); 840 } 841 return size; 842 } 843 844 /** 845 * Copy another Result into this one. Needed for the old Mapred framework 846 * @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT (which is supposed 847 * to be immutable). 848 */ 849 public void copyFrom(Result other) { 850 checkReadonly(); 851 this.row = null; 852 this.familyMap = null; 853 this.cells = other.cells; 854 } 855 856 /** 857 * For client users: You should only use the return value as a 858 * {@link org.apache.hadoop.hbase.CellScanner}, {@link ExtendedCellScanner} is marked as 859 * IA.Private which means there is no guarantee about its API stability. 860 */ 861 @Override 862 public ExtendedCellScanner cellScanner() { 863 // Reset 864 this.cellScannerIndex = INITIAL_CELLSCANNER_INDEX; 865 return this; 866 } 867 868 /** 869 * For client users: You should only use the return value as a {@link Cell}, {@link ExtendedCell} 870 * is marked as IA.Private which means there is no guarantee about its API stability. 871 */ 872 @Override 873 public ExtendedCell current() { 874 if ( 875 isEmpty() || cellScannerIndex == INITIAL_CELLSCANNER_INDEX || cellScannerIndex >= cells.length 876 ) { 877 return null; 878 } 879 return this.cells[cellScannerIndex]; 880 } 881 882 @Override 883 public boolean advance() { 884 if (isEmpty()) { 885 return false; 886 } 887 cellScannerIndex++; 888 if (cellScannerIndex < this.cells.length) { 889 return true; 890 } else if (cellScannerIndex == this.cells.length) { 891 return false; 892 } 893 throw new NoSuchElementException("Cannot advance beyond the last cell"); 894 } 895 896 public Boolean getExists() { 897 return exists; 898 } 899 900 public void setExists(Boolean exists) { 901 checkReadonly(); 902 this.exists = exists; 903 } 904 905 /** 906 * Whether or not the results are coming from possibly stale data. Stale results might be returned 907 * if {@link Consistency} is not STRONG for the query. 908 * @return Whether or not the results are coming from possibly stale data. 909 */ 910 public boolean isStale() { 911 return stale; 912 } 913 914 /** 915 * For scanning large rows, the RS may choose to return the cells chunk by chunk to prevent OOM or 916 * timeout. This flag is used to tell you if the current Result is the last one of the current 917 * row. False means this Result is the last one. True means there MAY be more cells belonging to 918 * the current row. If you don't use {@link Scan#setAllowPartialResults(boolean)} or 919 * {@link Scan#setBatch(int)}, this method will always return false because the Result must 920 * contains all cells in one Row. 921 */ 922 public boolean mayHaveMoreCellsInRow() { 923 return mayHaveMoreCellsInRow; 924 } 925 926 /** 927 * Set load information about the region to the information about the result 928 * @param loadStats statistics about the current region from which this was returned 929 */ 930 @InterfaceAudience.Private 931 public void setStatistics(RegionLoadStats loadStats) { 932 this.stats = loadStats; 933 } 934 935 @InterfaceAudience.Private 936 public void setMetrics(QueryMetrics metrics) { 937 this.metrics = metrics; 938 } 939 940 /** 941 * Returns the associated statistics about the region from which this was returned. Can be 942 * <tt>null</tt> if stats are disabled. 943 */ 944 public RegionLoadStats getStats() { 945 return stats; 946 } 947 948 /** Returns the query metrics, or {@code null} if we do not enable metrics. */ 949 public QueryMetrics getMetrics() { 950 return metrics; 951 } 952 953 /** 954 * All methods modifying state of Result object must call this method to ensure that special 955 * purpose immutable Results can't be accidentally modified. 956 */ 957 private void checkReadonly() { 958 if (readonly == true) { 959 throw new UnsupportedOperationException("Attempting to modify readonly EMPTY_RESULT!"); 960 } 961 } 962 963 /** 964 * Return true if this Result is a cursor to tell users where the server has scanned. In this 965 * Result the only meaningful method is {@link #getCursor()}. {@code 966 * while (r = scanner.next() && r != null) { 967 * if(r.isCursor()){ 968 * // scanning is not end, it is a cursor, save its row key and close scanner if you want, or 969 * // just continue the loop to call next(). } else { // just like before } } // scanning is end } 970 * {@link Scan#setNeedCursorResult(boolean)} {@link Cursor} {@link #getCursor()} 971 */ 972 public boolean isCursor() { 973 return cursor != null; 974 } 975 976 /** 977 * Return the cursor if this Result is a cursor result. {@link Scan#setNeedCursorResult(boolean)} 978 * {@link Cursor} {@link #isCursor()} 979 */ 980 public Cursor getCursor() { 981 return cursor; 982 } 983}