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