View Javadoc

1   /*
2    *
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
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
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.
18   */
19  
20  package org.apache.hadoop.hbase.client;
21  
22  import java.nio.BufferOverflowException;
23  import java.nio.ByteBuffer;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Comparator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.NavigableMap;
30  import java.util.TreeMap;
31  
32  import org.apache.hadoop.hbase.Cell;
33  import org.apache.hadoop.hbase.CellScannable;
34  import org.apache.hadoop.hbase.CellScanner;
35  import org.apache.hadoop.hbase.CellUtil;
36  import org.apache.hadoop.hbase.KeyValue;
37  import org.apache.hadoop.hbase.KeyValueUtil;
38  import org.apache.hadoop.hbase.classification.InterfaceAudience;
39  import org.apache.hadoop.hbase.classification.InterfaceStability;
40  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
41  import org.apache.hadoop.hbase.util.Bytes;
42  
43  /**
44   * Single row result of a {@link Get} or {@link Scan} query.<p>
45   *
46   * This class is <b>NOT THREAD SAFE</b>.<p>
47   *
48   * Convenience methods are available that return various {@link Map}
49   * structures and values directly.<p>
50   *
51   * To get a complete mapping of all cells in the Result, which can include
52   * multiple families and multiple versions, use {@link #getMap()}.<p>
53   *
54   * To get a mapping of each family to its columns (qualifiers and values),
55   * including only the latest version of each, use {@link #getNoVersionMap()}.
56   *
57   * To get a mapping of qualifiers to latest values for an individual family use
58   * {@link #getFamilyMap(byte[])}.<p>
59   *
60   * To get the latest value for a specific family and qualifier use
61   * {@link #getValue(byte[], byte[])}.
62   *
63   * A Result is backed by an array of {@link Cell} objects, each representing
64   * an HBase cell defined by the row, family, qualifier, timestamp, and value.<p>
65   *
66   * The underlying {@link Cell} objects can be accessed through the method {@link #listCells()}.
67   * This will create a List from the internal Cell []. Better is to exploit the fact that
68   * a new Result instance is a primed {@link CellScanner}; just call {@link #advance()} and
69   * {@link #current()} to iterate over Cells as you would any {@link CellScanner}.
70   * Call {@link #cellScanner()} to reset should you need to iterate the same Result over again
71   * ({@link CellScanner}s are one-shot).
72   *
73   * If you need to overwrite a Result with another Result instance -- as in the old 'mapred'
74   * RecordReader next invocations -- then create an empty Result with the null constructor and
75   * in then use {@link #copyFrom(Result)}
76   */
77  @InterfaceAudience.Public
78  @InterfaceStability.Stable
79  public class Result implements CellScannable, CellScanner {
80    private Cell[] cells;
81    private Boolean exists; // if the query was just to check existence.
82    private boolean stale = false;
83    // We're not using java serialization.  Transient here is just a marker to say
84    // that this is where we cache row if we're ever asked for it.
85    private transient byte [] row = null;
86    // Ditto for familyMap.  It can be composed on fly from passed in kvs.
87    private transient NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
88        familyMap = null;
89  
90    private static ThreadLocal<byte[]> localBuffer = new ThreadLocal<byte[]>();
91    private static final int PAD_WIDTH = 128;
92    public static final Result EMPTY_RESULT = new Result();
93  
94    private final static int INITIAL_CELLSCANNER_INDEX = -1;
95  
96    /**
97     * Index for where we are when Result is acting as a {@link CellScanner}.
98     */
99    private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
100   private ClientProtos.RegionLoadStats stats;
101 
102   /**
103    * Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
104    * Use this to represent no results if {@code null} won't do or in old 'mapred' as opposed
105    * to 'mapreduce' package MapReduce where you need to overwrite a Result instance with a
106    * {@link #copyFrom(Result)} call.
107    */
108   public Result() {
109     super();
110   }
111 
112   /**
113    * Instantiate a Result with the specified List of KeyValues.
114    * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
115    * @param cells List of cells
116    */
117   public static Result create(List<Cell> cells) {
118     return new Result(cells.toArray(new Cell[cells.size()]), null, false);
119   }
120 
121   public static Result create(List<Cell> cells, Boolean exists) {
122     return create(cells, exists, false);
123   }
124 
125   public static Result create(List<Cell> cells, Boolean exists, boolean stale) {
126     if (exists != null){
127       return new Result(null, exists, stale);
128     }
129     return new Result(cells.toArray(new Cell[cells.size()]), null, stale);
130   }
131 
132   /**
133    * Instantiate a Result with the specified array of KeyValues.
134    * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
135    * @param cells array of cells
136    */
137   public static Result create(Cell[] cells) {
138     return new Result(cells, null, false);
139   }
140 
141   public static Result create(Cell[] cells, Boolean exists, boolean stale) {
142     if (exists != null){
143       return new Result(null, exists, stale);
144     }
145     return new Result(cells, null, stale);
146   }
147 
148   /** Private ctor. Use {@link #create(Cell[])}. */
149   private Result(Cell[] cells, Boolean exists, boolean stale) {
150     this.cells = cells;
151     this.exists = exists;
152     this.stale = stale;
153   }
154 
155   /**
156    * Method for retrieving the row key that corresponds to
157    * the row from which this Result was created.
158    * @return row
159    */
160   public byte [] getRow() {
161     if (this.row == null) {
162       this.row = (this.cells == null || this.cells.length == 0) ?
163           null :
164           CellUtil.cloneRow(this.cells[0]);
165     }
166     return this.row;
167   }
168 
169   /**
170    * Return the array of Cells backing this Result instance.
171    *
172    * The array is sorted from smallest -> largest using the
173    * {@link KeyValue#COMPARATOR}.
174    *
175    * The array only contains what your Get or Scan specifies and no more.
176    * For example if you request column "A" 1 version you will have at most 1
177    * Cell in the array. If you request column "A" with 2 version you will
178    * have at most 2 Cells, with the first one being the newer timestamp and
179    * the second being the older timestamp (this is the sort order defined by
180    * {@link KeyValue#COMPARATOR}).  If columns don't exist, they won't be
181    * present in the result. Therefore if you ask for 1 version all columns,
182    * it is safe to iterate over this array and expect to see 1 Cell for
183    * each column and no more.
184    *
185    * This API is faster than using getFamilyMap() and getMap()
186    *
187    * @return array of Cells; can be null if nothing in the result
188    */
189   public Cell[] rawCells() {
190     return cells;
191   }
192 
193   /**
194    * Create a sorted list of the Cell's in this result.
195    *
196    * Since HBase 0.20.5 this is equivalent to raw().
197    *
198    * @return sorted List of Cells; can be null if no cells in the result
199    */
200   public List<Cell> listCells() {
201     return isEmpty()? null: Arrays.asList(rawCells());
202   }
203 
204   /**
205    * Return the Cells for the specific column.  The Cells are sorted in
206    * the {@link KeyValue#COMPARATOR} order.  That implies the first entry in
207    * the list is the most recent column.  If the query (Scan or Get) only
208    * requested 1 version the list will contain at most 1 entry.  If the column
209    * did not exist in the result set (either the column does not exist
210    * or the column was not selected in the query) the list will be empty.
211    *
212    * Also see getColumnLatest which returns just a Cell
213    *
214    * @param family the family
215    * @param qualifier
216    * @return a list of Cells for this column or empty list if the column
217    * did not exist in the result set
218    */
219   public List<Cell> getColumnCells(byte [] family, byte [] qualifier) {
220     List<Cell> result = new ArrayList<Cell>();
221 
222     Cell [] kvs = rawCells();
223 
224     if (kvs == null || kvs.length == 0) {
225       return result;
226     }
227     int pos = binarySearch(kvs, family, qualifier);
228     if (pos == -1) {
229       return result; // cant find it
230     }
231 
232     for (int i = pos; i < kvs.length; i++) {
233       if (CellUtil.matchingColumn(kvs[i], family,qualifier)) {
234         result.add(kvs[i]);
235       } else {
236         break;
237       }
238     }
239 
240     return result;
241   }
242 
243   protected int binarySearch(final Cell [] kvs,
244                              final byte [] family,
245                              final byte [] qualifier) {
246     Cell searchTerm =
247         KeyValueUtil.createFirstOnRow(CellUtil.cloneRow(kvs[0]),
248             family, qualifier);
249 
250     // pos === ( -(insertion point) - 1)
251     int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
252     // never will exact match
253     if (pos < 0) {
254       pos = (pos+1) * -1;
255       // pos is now insertion point
256     }
257     if (pos == kvs.length) {
258       return -1; // doesn't exist
259     }
260     return pos;
261   }
262 
263   /**
264    * Searches for the latest value for the specified column.
265    *
266    * @param kvs the array to search
267    * @param family family name
268    * @param foffset family offset
269    * @param flength family length
270    * @param qualifier column qualifier
271    * @param qoffset qualifier offset
272    * @param qlength qualifier length
273    *
274    * @return the index where the value was found, or -1 otherwise
275    */
276   protected int binarySearch(final Cell [] kvs,
277       final byte [] family, final int foffset, final int flength,
278       final byte [] qualifier, final int qoffset, final int qlength) {
279 
280     double keyValueSize = (double)
281         KeyValue.getKeyValueDataStructureSize(kvs[0].getRowLength(), flength, qlength, 0);
282 
283     byte[] buffer = localBuffer.get();
284     if (buffer == null || keyValueSize > buffer.length) {
285       // pad to the smallest multiple of the pad width
286       buffer = new byte[(int) Math.ceil(keyValueSize / PAD_WIDTH) * PAD_WIDTH];
287       localBuffer.set(buffer);
288     }
289 
290     Cell searchTerm = KeyValueUtil.createFirstOnRow(buffer, 0,
291         kvs[0].getRowArray(), kvs[0].getRowOffset(), kvs[0].getRowLength(),
292         family, foffset, flength,
293         qualifier, qoffset, qlength);
294 
295     // pos === ( -(insertion point) - 1)
296     int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
297     // never will exact match
298     if (pos < 0) {
299       pos = (pos+1) * -1;
300       // pos is now insertion point
301     }
302     if (pos == kvs.length) {
303       return -1; // doesn't exist
304     }
305     return pos;
306   }
307 
308   /**
309    * The Cell for the most recent timestamp for a given column.
310    *
311    * @param family
312    * @param qualifier
313    *
314    * @return the Cell for the column, or null if no value exists in the row or none have been
315    * selected in the query (Get/Scan)
316    */
317   public Cell getColumnLatestCell(byte [] family, byte [] qualifier) {
318     Cell [] kvs = rawCells(); // side effect possibly.
319     if (kvs == null || kvs.length == 0) {
320       return null;
321     }
322     int pos = binarySearch(kvs, family, qualifier);
323     if (pos == -1) {
324       return null;
325     }
326     if (CellUtil.matchingColumn(kvs[pos], family, qualifier)) {
327       return kvs[pos];
328     }
329     return null;
330   }
331 
332   /**
333    * The Cell for the most recent timestamp for a given column.
334    *
335    * @param family family name
336    * @param foffset family offset
337    * @param flength family length
338    * @param qualifier column qualifier
339    * @param qoffset qualifier offset
340    * @param qlength qualifier length
341    *
342    * @return the Cell for the column, or null if no value exists in the row or none have been
343    * selected in the query (Get/Scan)
344    */
345   public Cell getColumnLatestCell(byte [] family, int foffset, int flength,
346       byte [] qualifier, int qoffset, int qlength) {
347 
348     Cell [] kvs = rawCells(); // side effect possibly.
349     if (kvs == null || kvs.length == 0) {
350       return null;
351     }
352     int pos = binarySearch(kvs, family, foffset, flength, qualifier, qoffset, qlength);
353     if (pos == -1) {
354       return null;
355     }
356     if (CellUtil.matchingColumn(kvs[pos], family, foffset, flength, qualifier, qoffset, qlength)) {
357       return kvs[pos];
358     }
359     return null;
360   }
361 
362   /**
363    * Get the latest version of the specified column.
364    * @param family family name
365    * @param qualifier column qualifier
366    * @return value of latest version of column, null if none found
367    */
368   public byte[] getValue(byte [] family, byte [] qualifier) {
369     Cell kv = getColumnLatestCell(family, qualifier);
370     if (kv == null) {
371       return null;
372     }
373     return CellUtil.cloneValue(kv);
374   }
375 
376   /**
377    * Returns the value wrapped in a new <code>ByteBuffer</code>.
378    *
379    * @param family family name
380    * @param qualifier column qualifier
381    *
382    * @return the latest version of the column, or <code>null</code> if none found
383    */
384   public ByteBuffer getValueAsByteBuffer(byte [] family, byte [] qualifier) {
385 
386     Cell kv = getColumnLatestCell(family, 0, family.length, qualifier, 0, qualifier.length);
387 
388     if (kv == null) {
389       return null;
390     }
391     return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
392   }
393 
394   /**
395    * Returns the value wrapped in a new <code>ByteBuffer</code>.
396    *
397    * @param family family name
398    * @param foffset family offset
399    * @param flength family length
400    * @param qualifier column qualifier
401    * @param qoffset qualifier offset
402    * @param qlength qualifier length
403    *
404    * @return the latest version of the column, or <code>null</code> if none found
405    */
406   public ByteBuffer getValueAsByteBuffer(byte [] family, int foffset, int flength,
407       byte [] qualifier, int qoffset, int qlength) {
408 
409     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
410 
411     if (kv == null) {
412       return null;
413     }
414     return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
415   }
416 
417   /**
418    * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
419    * <p>
420    * Does not clear or flip the buffer.
421    *
422    * @param family family name
423    * @param qualifier column qualifier
424    * @param dst the buffer where to write the value
425    *
426    * @return <code>true</code> if a value was found, <code>false</code> otherwise
427    *
428    * @throws BufferOverflowException there is insufficient space remaining in the buffer
429    */
430   public boolean loadValue(byte [] family, byte [] qualifier, ByteBuffer dst)
431           throws BufferOverflowException {
432     return loadValue(family, 0, family.length, qualifier, 0, qualifier.length, dst);
433   }
434 
435   /**
436    * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
437    * <p>
438    * Does not clear or flip the buffer.
439    *
440    * @param family family name
441    * @param foffset family offset
442    * @param flength family length
443    * @param qualifier column qualifier
444    * @param qoffset qualifier offset
445    * @param qlength qualifier length
446    * @param dst the buffer where to write the value
447    *
448    * @return <code>true</code> if a value was found, <code>false</code> otherwise
449    *
450    * @throws BufferOverflowException there is insufficient space remaining in the buffer
451    */
452   public boolean loadValue(byte [] family, int foffset, int flength,
453       byte [] qualifier, int qoffset, int qlength, ByteBuffer dst)
454           throws BufferOverflowException {
455     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
456 
457     if (kv == null) {
458       return false;
459     }
460     dst.put(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
461     return true;
462   }
463 
464   /**
465    * Checks if the specified column contains a non-empty value (not a zero-length byte array).
466    *
467    * @param family family name
468    * @param qualifier column qualifier
469    *
470    * @return whether or not a latest value exists and is not empty
471    */
472   public boolean containsNonEmptyColumn(byte [] family, byte [] qualifier) {
473 
474     return containsNonEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
475   }
476 
477   /**
478    * Checks if the specified column contains a non-empty value (not a zero-length byte array).
479    *
480    * @param family family name
481    * @param foffset family offset
482    * @param flength family length
483    * @param qualifier column qualifier
484    * @param qoffset qualifier offset
485    * @param qlength qualifier length
486    *
487    * @return whether or not a latest value exists and is not empty
488    */
489   public boolean containsNonEmptyColumn(byte [] family, int foffset, int flength,
490       byte [] qualifier, int qoffset, int qlength) {
491 
492     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
493 
494     return (kv != null) && (kv.getValueLength() > 0);
495   }
496 
497   /**
498    * Checks if the specified column contains an empty value (a zero-length byte array).
499    *
500    * @param family family name
501    * @param qualifier column qualifier
502    *
503    * @return whether or not a latest value exists and is empty
504    */
505   public boolean containsEmptyColumn(byte [] family, byte [] qualifier) {
506 
507     return containsEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
508   }
509 
510   /**
511    * Checks if the specified column contains an empty value (a zero-length byte array).
512    *
513    * @param family family name
514    * @param foffset family offset
515    * @param flength family length
516    * @param qualifier column qualifier
517    * @param qoffset qualifier offset
518    * @param qlength qualifier length
519    *
520    * @return whether or not a latest value exists and is empty
521    */
522   public boolean containsEmptyColumn(byte [] family, int foffset, int flength,
523       byte [] qualifier, int qoffset, int qlength) {
524     Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
525 
526     return (kv != null) && (kv.getValueLength() == 0);
527   }
528 
529   /**
530    * Checks for existence of a value for the specified column (empty or not).
531    *
532    * @param family family name
533    * @param qualifier column qualifier
534    *
535    * @return true if at least one value exists in the result, false if not
536    */
537   public boolean containsColumn(byte [] family, byte [] qualifier) {
538     Cell kv = getColumnLatestCell(family, qualifier);
539     return kv != null;
540   }
541 
542   /**
543    * Checks for existence of a value for the specified column (empty or not).
544    *
545    * @param family family name
546    * @param foffset family offset
547    * @param flength family length
548    * @param qualifier column qualifier
549    * @param qoffset qualifier offset
550    * @param qlength qualifier length
551    *
552    * @return true if at least one value exists in the result, false if not
553    */
554   public boolean containsColumn(byte [] family, int foffset, int flength,
555       byte [] qualifier, int qoffset, int qlength) {
556 
557     return getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength) != null;
558   }
559 
560   /**
561    * Map of families to all versions of its qualifiers and values.
562    * <p>
563    * Returns a three level Map of the form:
564    * <code>Map&amp;family,Map&lt;qualifier,Map&lt;timestamp,value>>></code>
565    * <p>
566    * Note: All other map returning methods make use of this map internally.
567    * @return map from families to qualifiers to versions
568    */
569   public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() {
570     if (this.familyMap != null) {
571       return this.familyMap;
572     }
573     if(isEmpty()) {
574       return null;
575     }
576     this.familyMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
577     for(Cell kv : this.cells) {
578       byte [] family = CellUtil.cloneFamily(kv);
579       NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap = familyMap.get(family);
580       if(columnMap == null) {
581         columnMap = new TreeMap<>(Bytes.BYTES_COMPARATOR);
582         familyMap.put(family, columnMap);
583       }
584       byte [] qualifier = CellUtil.cloneQualifier(kv);
585       NavigableMap<Long, byte[]> versionMap = columnMap.get(qualifier);
586       if(versionMap == null) {
587         versionMap = new TreeMap<>(new Comparator<Long>() {
588           @Override
589           public int compare(Long l1, Long l2) {
590             return l2.compareTo(l1);
591           }
592         });
593         columnMap.put(qualifier, versionMap);
594       }
595       Long timestamp = kv.getTimestamp();
596       byte [] value = CellUtil.cloneValue(kv);
597 
598       versionMap.put(timestamp, value);
599     }
600     return this.familyMap;
601   }
602 
603   /**
604    * Map of families to their most recent qualifiers and values.
605    * <p>
606    * Returns a two level Map of the form: <code>Map&amp;family,Map&lt;qualifier,value>></code>
607    * <p>
608    * The most recent version of each qualifier will be used.
609    * @return map from families to qualifiers and value
610    */
611   public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() {
612     if(this.familyMap == null) {
613       getMap();
614     }
615     if(isEmpty()) {
616       return null;
617     }
618     NavigableMap<byte[], NavigableMap<byte[], byte[]>> returnMap =
619       new TreeMap<byte[], NavigableMap<byte[], byte[]>>(Bytes.BYTES_COMPARATOR);
620     for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
621       familyEntry : familyMap.entrySet()) {
622       NavigableMap<byte[], byte[]> qualifierMap =
623         new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
624       for(Map.Entry<byte[], NavigableMap<Long, byte[]>> qualifierEntry :
625         familyEntry.getValue().entrySet()) {
626         byte [] value =
627           qualifierEntry.getValue().get(qualifierEntry.getValue().firstKey());
628         qualifierMap.put(qualifierEntry.getKey(), value);
629       }
630       returnMap.put(familyEntry.getKey(), qualifierMap);
631     }
632     return returnMap;
633   }
634 
635   /**
636    * Map of qualifiers to values.
637    * <p>
638    * Returns a Map of the form: <code>Map&lt;qualifier,value></code>
639    * @param family column family to get
640    * @return map of qualifiers to values
641    */
642   public NavigableMap<byte[], byte[]> getFamilyMap(byte [] family) {
643     if(this.familyMap == null) {
644       getMap();
645     }
646     if(isEmpty()) {
647       return null;
648     }
649     NavigableMap<byte[], byte[]> returnMap =
650       new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
651     NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifierMap =
652       familyMap.get(family);
653     if(qualifierMap == null) {
654       return returnMap;
655     }
656     for(Map.Entry<byte[], NavigableMap<Long, byte[]>> entry :
657       qualifierMap.entrySet()) {
658       byte [] value =
659         entry.getValue().get(entry.getValue().firstKey());
660       returnMap.put(entry.getKey(), value);
661     }
662     return returnMap;
663   }
664 
665   /**
666    * Returns the value of the first column in the Result.
667    * @return value of the first column
668    */
669   public byte [] value() {
670     if (isEmpty()) {
671       return null;
672     }
673     return CellUtil.cloneValue(cells[0]);
674   }
675 
676   /**
677    * Check if the underlying Cell [] is empty or not
678    * @return true if empty
679    */
680   public boolean isEmpty() {
681     return this.cells == null || this.cells.length == 0;
682   }
683 
684   /**
685    * @return the size of the underlying Cell []
686    */
687   public int size() {
688     return this.cells == null? 0: this.cells.length;
689   }
690 
691   /**
692    * @return String
693    */
694   @Override
695   public String toString() {
696     StringBuilder sb = new StringBuilder();
697     sb.append("keyvalues=");
698     if(isEmpty()) {
699       sb.append("NONE");
700       return sb.toString();
701     }
702     sb.append("{");
703     boolean moreThanOne = false;
704     for(Cell kv : this.cells) {
705       if(moreThanOne) {
706         sb.append(", ");
707       } else {
708         moreThanOne = true;
709       }
710       sb.append(kv.toString());
711     }
712     sb.append("}");
713     return sb.toString();
714   }
715 
716   /**
717    * Does a deep comparison of two Results, down to the byte arrays.
718    * @param res1 first result to compare
719    * @param res2 second result to compare
720    * @throws Exception Every difference is throwing an exception
721    */
722   public static void compareResults(Result res1, Result res2)
723       throws Exception {
724     if (res2 == null) {
725       throw new Exception("There wasn't enough rows, we stopped at "
726           + Bytes.toStringBinary(res1.getRow()));
727     }
728     if (res1.size() != res2.size()) {
729       throw new Exception("This row doesn't have the same number of KVs: "
730           + res1.toString() + " compared to " + res2.toString());
731     }
732     Cell[] ourKVs = res1.rawCells();
733     Cell[] replicatedKVs = res2.rawCells();
734     for (int i = 0; i < res1.size(); i++) {
735       if (!ourKVs[i].equals(replicatedKVs[i]) ||
736           !Bytes.equals(CellUtil.cloneValue(ourKVs[i]), CellUtil.cloneValue(replicatedKVs[i]))) {
737         throw new Exception("This result was different: "
738             + res1.toString() + " compared to " + res2.toString());
739       }
740     }
741   }
742 
743   /**
744    * Get total size of raw cells 
745    * @param result
746    * @return Total size.
747    */
748   public static long getTotalSizeOfCells(Result result) {
749     long size = 0;
750     for (Cell c : result.rawCells()) {
751       size += CellUtil.estimatedHeapSizeOf(c);
752     }
753     return size;
754   }
755 
756   /**
757    * Copy another Result into this one. Needed for the old Mapred framework
758    * @param other
759    */
760   public void copyFrom(Result other) {
761     this.row = null;
762     this.familyMap = null;
763     this.cells = other.cells;
764   }
765 
766   @Override
767   public CellScanner cellScanner() {
768     // Reset
769     this.cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
770     return this;
771   }
772 
773   @Override
774   public Cell current() {
775     if (cells == null) return null;
776     return (cellScannerIndex < 0)? null: this.cells[cellScannerIndex];
777   }
778 
779   @Override
780   public boolean advance() {
781     if (cells == null) return false;
782     return ++cellScannerIndex < this.cells.length;
783   }
784 
785   public Boolean getExists() {
786     return exists;
787   }
788 
789   public void setExists(Boolean exists) {
790     this.exists = exists;
791   }
792 
793   /**
794    * Whether or not the results are coming from possibly stale data. Stale results
795    * might be returned if {@link Consistency} is not STRONG for the query.
796    * @return Whether or not the results are coming from possibly stale data.
797    */
798   public boolean isStale() {
799     return stale;
800   }
801 
802   /**
803    * Add load information about the region to the information about the result
804    * @param loadStats statistics about the current region from which this was returned
805    */
806   public void addResults(ClientProtos.RegionLoadStats loadStats) {
807     this.stats = loadStats;
808   }
809 
810   /**
811    * @return the associated statistics about the region from which this was returned. Can be
812    * <tt>null</tt> if stats are disabled.
813    */
814   public ClientProtos.RegionLoadStats getStats() {
815     return stats;
816   }
817 }