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