HBASE-26921 Rewrite the counting cells part in TestMultiVersions (#4316)
[hbase.git] / hbase-client / src / main / java / org / apache / hadoop / hbase / client / Result.java
blobeb72ef82995f7f0317c76d1313dacaaa12b3217c
1 /*
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 package org.apache.hadoop.hbase.client;
22 import java.io.IOException;
23 import java.nio.BufferOverflowException;
24 import java.nio.ByteBuffer;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.NavigableMap;
33 import java.util.NoSuchElementException;
34 import java.util.TreeMap;
36 import org.apache.hadoop.hbase.Cell;
37 import org.apache.hadoop.hbase.CellComparator;
38 import org.apache.hadoop.hbase.CellScannable;
39 import org.apache.hadoop.hbase.CellScanner;
40 import org.apache.hadoop.hbase.CellUtil;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.PrivateCellUtil;
43 import org.apache.hadoop.hbase.KeyValue;
44 import org.apache.hadoop.hbase.KeyValueUtil;
45 import org.apache.yetus.audience.InterfaceAudience;
46 import org.apache.hadoop.hbase.util.Bytes;
48 /**
49 * Single row result of a {@link Get} or {@link Scan} query.<p>
51 * This class is <b>NOT THREAD SAFE</b>.<p>
53 * Convenience methods are available that return various {@link Map}
54 * structures and values directly.<p>
56 * To get a complete mapping of all cells in the Result, which can include
57 * multiple families and multiple versions, use {@link #getMap()}.<p>
59 * To get a mapping of each family to its columns (qualifiers and values),
60 * including only the latest version of each, use {@link #getNoVersionMap()}.
62 * To get a mapping of qualifiers to latest values for an individual family use
63 * {@link #getFamilyMap(byte[])}.<p>
65 * To get the latest value for a specific family and qualifier use
66 * {@link #getValue(byte[], byte[])}.
68 * A Result is backed by an array of {@link Cell} objects, each representing
69 * an HBase cell defined by the row, family, qualifier, timestamp, and value.<p>
71 * The underlying {@link Cell} objects can be accessed through the method {@link #listCells()}.
72 * This will create a List from the internal Cell []. Better is to exploit the fact that
73 * a new Result instance is a primed {@link CellScanner}; just call {@link #advance()} and
74 * {@link #current()} to iterate over Cells as you would any {@link CellScanner}.
75 * Call {@link #cellScanner()} to reset should you need to iterate the same Result over again
76 * ({@link CellScanner}s are one-shot).
78 * If you need to overwrite a Result with another Result instance -- as in the old 'mapred'
79 * RecordReader next invocations -- then create an empty Result with the null constructor and
80 * in then use {@link #copyFrom(Result)}
82 @InterfaceAudience.Public
83 public class Result implements CellScannable, CellScanner {
84 private Cell[] cells;
85 private Boolean exists; // if the query was just to check existence.
86 private boolean stale = false;
88 /**
89 * See {@link #mayHaveMoreCellsInRow()}.
91 private boolean mayHaveMoreCellsInRow = false;
92 // We're not using java serialization. Transient here is just a marker to say
93 // that this is where we cache row if we're ever asked for it.
94 private transient byte [] row = null;
95 // Ditto for familyMap. It can be composed on fly from passed in kvs.
96 private transient NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
97 familyMap = null;
99 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);
103 private final static int INITIAL_CELLSCANNER_INDEX = -1;
106 * Index for where we are when Result is acting as a {@link CellScanner}.
108 private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
109 private RegionLoadStats stats;
111 private final boolean readonly;
113 private Cursor cursor = null;
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.
121 public Result() {
122 this(false);
126 * Allows to construct special purpose immutable Result objects,
127 * such as EMPTY_RESULT.
128 * @param readonly whether this Result instance is readonly
130 private Result(boolean readonly) {
131 this.readonly = readonly;
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
139 public static Result create(List<Cell> cells) {
140 return create(cells, null);
143 public static Result create(List<Cell> cells, Boolean exists) {
144 return create(cells, exists, false);
147 public static Result create(List<Cell> cells, Boolean exists, boolean stale) {
148 return create(cells, exists, stale, false);
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);
156 return new Result(cells.toArray(new Cell[cells.size()]), null, stale, mayHaveMoreCellsInRow);
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
164 public static Result create(Cell[] cells) {
165 return create(cells, null, false);
168 public static Result create(Cell[] cells, Boolean exists, boolean stale) {
169 return create(cells, exists, stale, false);
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);
177 return new Result(cells, null, stale, mayHaveMoreCellsInRow);
180 public static Result createCursorResult(Cursor cursor) {
181 return new Result(cursor);
184 private Result(Cursor cursor) {
185 this.cursor = cursor;
186 this.readonly = false;
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;
199 * Method for retrieving the row key that corresponds to
200 * the row from which this Result was created.
201 * @return row
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]);
209 return this.row;
213 * Return the array of Cells backing this Result instance.
215 * The array is sorted from smallest -&gt; largest using the
216 * {@link CellComparator}.
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.
228 * This API is faster than using getFamilyMap() and getMap()
230 * @return array of Cells; can be null if nothing in the result
232 public Cell[] rawCells() {
233 return cells;
237 * Create a sorted list of the Cell's in this result.
239 * Since HBase 0.20.5 this is equivalent to raw().
241 * @return sorted List of Cells; can be null if no cells in the result
243 public List<Cell> listCells() {
244 return isEmpty()? null: Arrays.asList(rawCells());
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.
255 * Also see getColumnLatest which returns just a Cell
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
262 public List<Cell> getColumnCells(byte [] family, byte [] qualifier) {
263 List<Cell> result = new ArrayList<>();
265 Cell [] kvs = rawCells();
267 if (kvs == null || kvs.length == 0) {
268 return result;
270 int pos = binarySearch(kvs, family, qualifier);
271 if (pos == -1) {
272 return result; // cant find it
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;
283 return result;
286 private byte[] notNullBytes(final byte[] bytes) {
287 if (bytes == null) {
288 return HConstants.EMPTY_BYTE_ARRAY;
289 } else {
290 return bytes;
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);
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
312 if (pos == kvs.length) {
313 return -1; // doesn't exist
315 return pos;
319 * Searches for the latest value for the specified column.
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
329 * @return the index where the value was found, or -1 otherwise
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) {
335 double keyValueSize = (double)
336 KeyValue.getKeyValueDataStructureSize(kvs[0].getRowLength(), flength, qlength, 0);
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);
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);
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
357 if (pos == kvs.length) {
358 return -1; // doesn't exist
360 return pos;
364 * The Cell for the most recent timestamp for a given column.
366 * @param family
367 * @param qualifier
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)
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;
377 int pos = binarySearch(kvs, family, qualifier);
378 if (pos == -1) {
379 return null;
381 if (CellUtil.matchingColumn(kvs[pos], family, qualifier)) {
382 return kvs[pos];
384 return null;
388 * The Cell for the most recent timestamp for a given column.
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
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)
400 public Cell getColumnLatestCell(byte [] family, int foffset, int flength,
401 byte [] qualifier, int qoffset, int qlength) {
403 Cell [] kvs = rawCells(); // side effect possibly.
404 if (kvs == null || kvs.length == 0) {
405 return null;
407 int pos = binarySearch(kvs, family, foffset, flength, qualifier, qoffset, qlength);
408 if (pos == -1) {
409 return null;
411 if (PrivateCellUtil.matchingColumn(kvs[pos], family, foffset, flength, qualifier, qoffset,
412 qlength)) {
413 return kvs[pos];
415 return null;
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
427 public byte[] getValue(byte [] family, byte [] qualifier) {
428 Cell kv = getColumnLatestCell(family, qualifier);
429 if (kv == null) {
430 return null;
432 return CellUtil.cloneValue(kv);
436 * Returns the value wrapped in a new <code>ByteBuffer</code>.
438 * @param family family name
439 * @param qualifier column qualifier
441 * @return the latest version of the column, or <code>null</code> if none found
443 public ByteBuffer getValueAsByteBuffer(byte [] family, byte [] qualifier) {
445 Cell kv = getColumnLatestCell(family, 0, family.length, qualifier, 0, qualifier.length);
447 if (kv == null) {
448 return null;
450 return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()).
451 asReadOnlyBuffer();
455 * Returns the value wrapped in a new <code>ByteBuffer</code>.
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
464 * @return the latest version of the column, or <code>null</code> if none found
466 public ByteBuffer getValueAsByteBuffer(byte [] family, int foffset, int flength,
467 byte [] qualifier, int qoffset, int qlength) {
469 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
471 if (kv == null) {
472 return null;
474 return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()).
475 asReadOnlyBuffer();
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.
483 * @param family family name
484 * @param qualifier column qualifier
485 * @param dst the buffer where to write the value
487 * @return <code>true</code> if a value was found, <code>false</code> otherwise
489 * @throws BufferOverflowException there is insufficient space remaining in the buffer
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);
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.
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
509 * @return <code>true</code> if a value was found, <code>false</code> otherwise
511 * @throws BufferOverflowException there is insufficient space remaining in the buffer
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);
518 if (kv == null) {
519 return false;
521 dst.put(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
522 return true;
526 * Checks if the specified column contains a non-empty value (not a zero-length byte array).
528 * @param family family name
529 * @param qualifier column qualifier
531 * @return whether or not a latest value exists and is not empty
533 public boolean containsNonEmptyColumn(byte [] family, byte [] qualifier) {
535 return containsNonEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
539 * Checks if the specified column contains a non-empty value (not a zero-length byte array).
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
548 * @return whether or not a latest value exists and is not empty
550 public boolean containsNonEmptyColumn(byte [] family, int foffset, int flength,
551 byte [] qualifier, int qoffset, int qlength) {
553 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
555 return (kv != null) && (kv.getValueLength() > 0);
559 * Checks if the specified column contains an empty value (a zero-length byte array).
561 * @param family family name
562 * @param qualifier column qualifier
564 * @return whether or not a latest value exists and is empty
566 public boolean containsEmptyColumn(byte [] family, byte [] qualifier) {
568 return containsEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
572 * Checks if the specified column contains an empty value (a zero-length byte array).
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
581 * @return whether or not a latest value exists and is empty
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);
587 return (kv != null) && (kv.getValueLength() == 0);
591 * Checks for existence of a value for the specified column (empty or not).
593 * @param family family name
594 * @param qualifier column qualifier
596 * @return true if at least one value exists in the result, false if not
598 public boolean containsColumn(byte [] family, byte [] qualifier) {
599 Cell kv = getColumnLatestCell(family, qualifier);
600 return kv != null;
604 * Checks for existence of a value for the specified column (empty or not).
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
613 * @return true if at least one value exists in the result, false if not
615 public boolean containsColumn(byte [] family, int foffset, int flength,
616 byte [] qualifier, int qoffset, int qlength) {
618 return getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength) != null;
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&amp;family,Map&lt;qualifier,Map&lt;timestamp,value&gt;&gt;&gt;</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
630 public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() {
631 if (this.familyMap != null) {
632 return this.familyMap;
634 if(isEmpty()) {
635 return null;
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);
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);
654 columnMap.put(qualifier, versionMap);
656 Long timestamp = kv.getTimestamp();
657 byte [] value = CellUtil.cloneValue(kv);
659 versionMap.put(timestamp, value);
661 return this.familyMap;
665 * Map of families to their most recent qualifiers and values.
666 * <p>
667 * Returns a two level Map of the form: <code>Map&amp;family,Map&lt;qualifier,value&gt;&gt;</code>
668 * <p>
669 * The most recent version of each qualifier will be used.
670 * @return map from families to qualifiers and value
672 public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() {
673 if(this.familyMap == null) {
674 getMap();
676 if(isEmpty()) {
677 return null;
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);
689 returnMap.put(familyEntry.getKey(), qualifierMap);
691 return returnMap;
695 * Map of qualifiers to values.
696 * <p>
697 * Returns a Map of the form: <code>Map&lt;qualifier,value&gt;</code>
698 * @param family column family to get
699 * @return map of qualifiers to values
701 public NavigableMap<byte[], byte[]> getFamilyMap(byte [] family) {
702 if(this.familyMap == null) {
703 getMap();
705 if(isEmpty()) {
706 return null;
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;
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);
720 return returnMap;
724 * Returns the value of the first column in the Result.
725 * @return value of the first column
727 public byte [] value() {
728 if (isEmpty()) {
729 return null;
731 return CellUtil.cloneValue(cells[0]);
735 * Check if the underlying Cell [] is empty or not
736 * @return true if empty
738 public boolean isEmpty() {
739 return this.cells == null || this.cells.length == 0;
743 * @return the size of the underlying Cell []
745 public int size() {
746 return this.cells == null? 0: this.cells.length;
750 * @return String
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();
760 sb.append("{");
761 boolean moreThanOne = false;
762 for(Cell kv : this.cells) {
763 if(moreThanOne) {
764 sb.append(", ");
765 } else {
766 moreThanOne = true;
768 sb.append(kv.toString());
770 sb.append("}");
771 return sb.toString();
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
780 public static void compareResults(Result res1, Result res2)
781 throws Exception{
782 compareResults(res1, res2, true);
786 * Does a deep comparison of two Results, down to the byte arrays.
787 * @param res1 first result to compare
788 * @param res2 second result to compare
789 * @param verbose includes string representation for all cells in the exception if true;
790 * otherwise include rowkey only
791 * @throws Exception Every difference is throwing an exception
793 public static void compareResults(Result res1, Result res2, boolean verbose)
794 throws Exception {
795 if (res2 == null) {
796 throw new Exception("There wasn't enough rows, we stopped at "
797 + Bytes.toStringBinary(res1.getRow()));
799 if (res1.size() != res2.size()) {
800 if (verbose) {
801 throw new Exception(
802 "This row doesn't have the same number of KVs: "
803 + res1 + " compared to " + res2);
804 } else {
805 throw new Exception(
806 "This row doesn't have the same number of KVs: row="
807 + Bytes.toStringBinary(res1.getRow())
808 + ", " + res1.size() + " cells are compared to " + res2.size() + " cells");
811 Cell[] ourKVs = res1.rawCells();
812 Cell[] replicatedKVs = res2.rawCells();
813 for (int i = 0; i < res1.size(); i++) {
814 if (!ourKVs[i].equals(replicatedKVs[i]) ||
815 !CellUtil.matchingValue(ourKVs[i], replicatedKVs[i]) ||
816 !CellUtil.matchingTags(ourKVs[i], replicatedKVs[i])) {
817 if (verbose) {
818 throw new Exception("This result was different: "
819 + res1 + " compared to " + res2);
820 } else {
821 throw new Exception("This result was different: row="
822 + Bytes.toStringBinary(res1.getRow()));
829 * Forms a single result from the partial results in the partialResults list. This method is
830 * useful for reconstructing partial results on the client side.
831 * @param partialResults list of partial results
832 * @return The complete result that is formed by combining all of the partial results together
833 * @throws IOException A complete result cannot be formed because the results in the partial list
834 * come from different rows
836 public static Result createCompleteResult(Iterable<Result> partialResults)
837 throws IOException {
838 if (partialResults == null) {
839 return Result.create(Collections.emptyList(), null, false);
841 List<Cell> cells = new ArrayList<>();
842 boolean stale = false;
843 byte[] prevRow = null;
844 byte[] currentRow = null;
845 for (Iterator<Result> iter = partialResults.iterator(); iter.hasNext();) {
846 Result r = iter.next();
847 currentRow = r.getRow();
848 if (prevRow != null && !Bytes.equals(prevRow, currentRow)) {
849 throw new IOException(
850 "Cannot form complete result. Rows of partial results do not match." +
851 " Partial Results: " + partialResults);
853 // Ensure that all Results except the last one are marked as partials. The last result
854 // may not be marked as a partial because Results are only marked as partials when
855 // the scan on the server side must be stopped due to reaching the maxResultSize.
856 // Visualizing it makes it easier to understand:
857 // maxResultSize: 2 cells
858 // (-x-) represents cell number x in a row
859 // Example: row1: -1- -2- -3- -4- -5- (5 cells total)
860 // How row1 will be returned by the server as partial Results:
861 // Result1: -1- -2- (2 cells, size limit reached, mark as partial)
862 // Result2: -3- -4- (2 cells, size limit reached, mark as partial)
863 // Result3: -5- (1 cell, size limit NOT reached, NOT marked as partial)
864 if (iter.hasNext() && !r.mayHaveMoreCellsInRow()) {
865 throw new IOException("Cannot form complete result. Result is missing partial flag. " +
866 "Partial Results: " + partialResults);
868 prevRow = currentRow;
869 stale = stale || r.isStale();
870 for (Cell c : r.rawCells()) {
871 cells.add(c);
875 return Result.create(cells, null, stale);
879 * Get total size of raw cells
880 * @param result
881 * @return Total size.
883 public static long getTotalSizeOfCells(Result result) {
884 long size = 0;
885 if (result.isEmpty()) {
886 return size;
888 for (Cell c : result.rawCells()) {
889 size += c.heapSize();
891 return size;
895 * Copy another Result into this one. Needed for the old Mapred framework
896 * @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT
897 * (which is supposed to be immutable).
898 * @param other
900 public void copyFrom(Result other) {
901 checkReadonly();
902 this.row = null;
903 this.familyMap = null;
904 this.cells = other.cells;
907 @Override
908 public CellScanner cellScanner() {
909 // Reset
910 this.cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
911 return this;
914 @Override
915 public Cell current() {
916 if (isEmpty()
917 || cellScannerIndex == INITIAL_CELLSCANNER_INDEX
918 || cellScannerIndex >= cells.length)
919 return null;
920 return this.cells[cellScannerIndex];
923 @Override
924 public boolean advance() {
925 if (isEmpty()) {
926 return false;
928 cellScannerIndex++;
929 if (cellScannerIndex < this.cells.length) {
930 return true;
931 } else if (cellScannerIndex == this.cells.length) {
932 return false;
934 throw new NoSuchElementException("Cannot advance beyond the last cell");
937 public Boolean getExists() {
938 return exists;
941 public void setExists(Boolean exists) {
942 checkReadonly();
943 this.exists = exists;
947 * Whether or not the results are coming from possibly stale data. Stale results
948 * might be returned if {@link Consistency} is not STRONG for the query.
949 * @return Whether or not the results are coming from possibly stale data.
951 public boolean isStale() {
952 return stale;
956 * For scanning large rows, the RS may choose to return the cells chunk by chunk to prevent OOM
957 * or timeout. This flag is used to tell you if the current Result is the last one of the current
958 * row. False means this Result is the last one. True means there MAY be more cells belonging to
959 * the current row.
960 * If you don't use {@link Scan#setAllowPartialResults(boolean)} or {@link Scan#setBatch(int)},
961 * this method will always return false because the Result must contains all cells in one Row.
963 public boolean mayHaveMoreCellsInRow() {
964 return mayHaveMoreCellsInRow;
968 * Set load information about the region to the information about the result
969 * @param loadStats statistics about the current region from which this was returned
971 @InterfaceAudience.Private
972 public void setStatistics(RegionLoadStats loadStats) {
973 this.stats = loadStats;
977 * @return the associated statistics about the region from which this was returned. Can be
978 * <tt>null</tt> if stats are disabled.
980 public RegionLoadStats getStats() {
981 return stats;
985 * All methods modifying state of Result object must call this method
986 * to ensure that special purpose immutable Results can't be accidentally modified.
988 private void checkReadonly() {
989 if (readonly == true) {
990 throw new UnsupportedOperationException("Attempting to modify readonly EMPTY_RESULT!");
995 * Return true if this Result is a cursor to tell users where the server has scanned.
996 * In this Result the only meaningful method is {@link #getCursor()}.
998 * {@code
999 * while (r = scanner.next() && r != null) {
1000 * if(r.isCursor()){
1001 * // scanning is not end, it is a cursor, save its row key and close scanner if you want, or
1002 * // just continue the loop to call next().
1003 * } else {
1004 * // just like before
1007 * // scanning is end
1010 * {@link Scan#setNeedCursorResult(boolean)}
1011 * {@link Cursor}
1012 * {@link #getCursor()}
1014 public boolean isCursor() {
1015 return cursor != null ;
1019 * Return the cursor if this Result is a cursor result.
1020 * {@link Scan#setNeedCursorResult(boolean)}
1021 * {@link Cursor}
1022 * {@link #isCursor()}
1024 public Cursor getCursor(){
1025 return cursor;