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